qwt_round_scale_draw.cpp 8.1 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
5
 *
pixhawk's avatar
pixhawk committed
6 7 8 9
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

Bryant's avatar
Bryant committed
10
#include "qwt_round_scale_draw.h"
pixhawk's avatar
pixhawk committed
11 12 13
#include "qwt_painter.h"
#include "qwt_scale_div.h"
#include "qwt_scale_map.h"
Bryant's avatar
Bryant committed
14 15 16 17 18
#include "qwt_math.h"
#include <qpen.h>
#include <qpainter.h>
#include <qfontmetrics.h>
#include <qmath.h>
pixhawk's avatar
pixhawk committed
19 20 21 22 23

class QwtRoundScaleDraw::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
24 25 26 27 28
        center( 50.0, 50.0 ),
        radius( 50.0 ),
        startAngle( -135.0 ),
        endAngle( 135.0 )
    {
pixhawk's avatar
pixhawk committed
29 30
    }

Bryant's avatar
Bryant committed
31 32
    QPointF center;
    double radius;
pixhawk's avatar
pixhawk committed
33

Bryant's avatar
Bryant committed
34 35
    double startAngle;
    double endAngle;
pixhawk's avatar
pixhawk committed
36 37 38 39 40 41 42 43 44 45 46 47 48
};

/*!
  \brief Constructor

  The range of the scale is initialized to [0, 100],
  The center is set to (50, 50) with a radius of 50.
  The angle range is set to [-135, 135].
*/
QwtRoundScaleDraw::QwtRoundScaleDraw()
{
    d_data = new QwtRoundScaleDraw::PrivateData;

Bryant's avatar
Bryant committed
49 50
    setRadius( 50 );
    scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
pixhawk's avatar
pixhawk committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64
}

//! Destructor
QwtRoundScaleDraw::~QwtRoundScaleDraw()
{
    delete d_data;
}

/*!
  Change of radius the scale

  Radius is the radius of the backbone without ticks and labels.

  \param radius New Radius
Bryant's avatar
Bryant committed
65
  \sa moveCenter()
pixhawk's avatar
pixhawk committed
66
*/
Bryant's avatar
Bryant committed
67
void QwtRoundScaleDraw::setRadius( double radius )
pixhawk's avatar
pixhawk committed
68 69 70 71 72
{
    d_data->radius = radius;
}

/*!
73
  Get the radius
pixhawk's avatar
pixhawk committed
74 75 76

  Radius is the radius of the backbone without ticks and labels.

Bryant's avatar
Bryant committed
77
  \return Radius of the scale
pixhawk's avatar
pixhawk committed
78 79
  \sa setRadius(), extent()
*/
Bryant's avatar
Bryant committed
80
double QwtRoundScaleDraw::radius() const
pixhawk's avatar
pixhawk committed
81 82 83 84 85 86 87 88
{
    return d_data->radius;
}

/*!
   Move the center of the scale draw, leaving the radius unchanged

   \param center New center
Bryant's avatar
Bryant committed
89
   \sa setRadius()
pixhawk's avatar
pixhawk committed
90
*/
Bryant's avatar
Bryant committed
91
void QwtRoundScaleDraw::moveCenter( const QPointF &center )
pixhawk's avatar
pixhawk committed
92 93 94 95 96
{
    d_data->center = center;
}

//! Get the center of the scale
Bryant's avatar
Bryant committed
97
QPointF QwtRoundScaleDraw::center() const
pixhawk's avatar
pixhawk committed
98 99 100 101 102 103 104 105 106 107 108 109 110
{
    return d_data->center;
}

/*!
  \brief Adjust the baseline circle segment for round scales.

  The baseline will be drawn from min(angle1,angle2) to max(angle1, angle2).
  The default setting is [ -135, 135 ].
  An angle of 0 degrees corresponds to the 12 o'clock position,
  and positive angles count in a clockwise direction.
  \param angle1
  \param angle2 boundaries of the angle interval in degrees.
111
  \warning <ul>
pixhawk's avatar
pixhawk committed
112 113
  <li>The angle range is limited to [-360, 360] degrees. Angles exceeding
      this range will be clipped.
Bryant's avatar
Bryant committed
114
  <li>For angles more or equal than 360 degrees above or below min(angle1, angle2),
pixhawk's avatar
pixhawk committed
115
      scale marks will not be drawn.
Bryant's avatar
Bryant committed
116
  <li>If you need a counterclockwise scale, use QwtScaleDiv::setInterval()
pixhawk's avatar
pixhawk committed
117 118
  </ul>
*/
Bryant's avatar
Bryant committed
119
void QwtRoundScaleDraw::setAngleRange( double angle1, double angle2 )
pixhawk's avatar
pixhawk committed
120
{
Bryant's avatar
Bryant committed
121 122 123 124
#if 0
    angle1 = qBound( -360.0, angle1, 360.0 );
    angle2 = qBound( -360.0, angle2, 360.0 );
#endif
pixhawk's avatar
pixhawk committed
125

Bryant's avatar
Bryant committed
126 127
    d_data->startAngle = angle1;
    d_data->endAngle = angle2;
128

Bryant's avatar
Bryant committed
129 130
    if ( d_data->startAngle == d_data->endAngle )
    {
pixhawk's avatar
pixhawk committed
131 132 133
        d_data->startAngle -= 1;
        d_data->endAngle += 1;
    }
134

Bryant's avatar
Bryant committed
135
    scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
pixhawk's avatar
pixhawk committed
136 137 138 139 140 141 142 143 144 145
}

/*!
   Draws the label for a major scale tick

   \param painter Painter
   \param value Value

   \sa drawTick(), drawBackbone()
*/
Bryant's avatar
Bryant committed
146
void QwtRoundScaleDraw::drawLabel( QPainter *painter, double value ) const
pixhawk's avatar
pixhawk committed
147
{
Bryant's avatar
Bryant committed
148
    const QwtText label = tickLabel( painter->font(), value );
pixhawk's avatar
pixhawk committed
149
    if ( label.isEmpty() )
150
        return;
pixhawk's avatar
pixhawk committed
151

Bryant's avatar
Bryant committed
152 153 154 155
    const double tval = scaleMap().transform( value );
    if ( ( tval >= d_data->startAngle + 360.0 )
        || ( tval <= d_data->startAngle - 360.0 ) )
    {
156
        return;
pixhawk's avatar
pixhawk committed
157 158 159
    }

    double radius = d_data->radius;
Bryant's avatar
Bryant committed
160 161 162
    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
        hasComponent( QwtAbstractScaleDraw::Backbone ) )
    {
pixhawk's avatar
pixhawk committed
163 164 165
        radius += spacing();
    }

Bryant's avatar
Bryant committed
166 167
    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
        radius += tickLength( QwtScaleDiv::MajorTick );
pixhawk's avatar
pixhawk committed
168

Bryant's avatar
Bryant committed
169 170
    const QSizeF sz = label.textSize( painter->font() );
    const double arc = qwtRadians( tval );
pixhawk's avatar
pixhawk committed
171

Bryant's avatar
Bryant committed
172 173 174 175
    const double x = d_data->center.x() +
        ( radius + sz.width() / 2.0 ) * qSin( arc );
    const double y = d_data->center.y() -
        ( radius + sz.height() / 2.0 ) * qCos( arc );
pixhawk's avatar
pixhawk committed
176

Bryant's avatar
Bryant committed
177 178 179
    const QRectF r( x - sz.width() / 2, y - sz.height() / 2,
        sz.width(), sz.height() );
    label.draw( painter, r );
pixhawk's avatar
pixhawk committed
180 181 182 183
}

/*!
   Draw a tick
184

pixhawk's avatar
pixhawk committed
185 186 187 188
   \param painter Painter
   \param value Value of the tick
   \param len Lenght of the tick

189
   \sa drawBackbone(), drawLabel()
pixhawk's avatar
pixhawk committed
190
*/
Bryant's avatar
Bryant committed
191
void QwtRoundScaleDraw::drawTick( QPainter *painter, double value, double len ) const
pixhawk's avatar
pixhawk committed
192 193 194 195
{
    if ( len <= 0 )
        return;

Bryant's avatar
Bryant committed
196
    const double tval = scaleMap().transform( value );
pixhawk's avatar
pixhawk committed
197

Bryant's avatar
Bryant committed
198 199 200
    const double cx = d_data->center.x();
    const double cy = d_data->center.y();
    const double radius = d_data->radius;
pixhawk's avatar
pixhawk committed
201

Bryant's avatar
Bryant committed
202 203 204 205
    if ( ( tval < d_data->startAngle + 360.0 )
        || ( tval > d_data->startAngle - 360.0 ) )
    {
        const double arc = qwtRadians( tval );
pixhawk's avatar
pixhawk committed
206

Bryant's avatar
Bryant committed
207 208
        const double sinArc = qSin( arc );
        const double cosArc = qCos( arc );
pixhawk's avatar
pixhawk committed
209

Bryant's avatar
Bryant committed
210 211 212 213
        const double x1 = cx + radius * sinArc;
        const double x2 = cx + ( radius + len ) * sinArc;
        const double y1 = cy - radius * cosArc;
        const double y2 = cy - ( radius + len ) * cosArc;
pixhawk's avatar
pixhawk committed
214

Bryant's avatar
Bryant committed
215
        QwtPainter::drawLine( painter, x1, y1, x2, y2 );
pixhawk's avatar
pixhawk committed
216 217 218 219 220 221 222 223 224
    }
}

/*!
   Draws the baseline of the scale
   \param painter Painter

   \sa drawTick(), drawLabel()
*/
Bryant's avatar
Bryant committed
225
void QwtRoundScaleDraw::drawBackbone( QPainter *painter ) const
pixhawk's avatar
pixhawk committed
226
{
Bryant's avatar
Bryant committed
227 228 229 230 231
    const double deg1 = scaleMap().p1();
    const double deg2 = scaleMap().p2();

    const int a1 = qRound( qMin( deg1, deg2 ) - 90 );
    const int a2 = qRound( qMax( deg1, deg2 ) - 90 );
pixhawk's avatar
pixhawk committed
232

Bryant's avatar
Bryant committed
233 234 235
    const double radius = d_data->radius;
    const double x = d_data->center.x() - radius;
    const double y = d_data->center.y() - radius;
pixhawk's avatar
pixhawk committed
236

Bryant's avatar
Bryant committed
237 238
    painter->drawArc( QRectF( x, y, 2 * radius, 2 * radius ),
        -a2 * 16, ( a2 - a1 + 1 ) * 16 );          // counterclockwise
pixhawk's avatar
pixhawk committed
239 240 241 242 243
}

/*!
   Calculate the extent of the scale

Bryant's avatar
Bryant committed
244
   The extent is the distance between the baseline to the outermost
245
   pixel of the scale draw. radius() + extent() is an upper limit
pixhawk's avatar
pixhawk committed
246 247 248
   for the radius of the bounding circle.

   \param font Font used for painting the labels
Bryant's avatar
Bryant committed
249
   \return Calculated extent
pixhawk's avatar
pixhawk committed
250 251

   \sa setMinimumExtent(), minimumExtent()
Bryant's avatar
Bryant committed
252
   \warning The implemented algorithm is not too smart and
pixhawk's avatar
pixhawk committed
253 254 255
            calculates only an upper limit, that might be a
            few pixels too large
*/
Bryant's avatar
Bryant committed
256
double QwtRoundScaleDraw::extent( const QFont &font ) const
pixhawk's avatar
pixhawk committed
257
{
Bryant's avatar
Bryant committed
258
    double d = 0.0;
pixhawk's avatar
pixhawk committed
259

Bryant's avatar
Bryant committed
260 261
    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
    {
pixhawk's avatar
pixhawk committed
262
        const QwtScaleDiv &sd = scaleDiv();
Bryant's avatar
Bryant committed
263 264 265
        const QList<double> &ticks = sd.ticks( QwtScaleDiv::MajorTick );
        for ( int i = 0; i < ticks.count(); i++ )
        {
pixhawk's avatar
pixhawk committed
266
            const double value = ticks[i];
Bryant's avatar
Bryant committed
267
            if ( !sd.contains( value ) )
pixhawk's avatar
pixhawk committed
268 269
                continue;

Bryant's avatar
Bryant committed
270
            const QwtText label = tickLabel( font, value );
pixhawk's avatar
pixhawk committed
271 272
            if ( label.isEmpty() )
                continue;
273

Bryant's avatar
Bryant committed
274 275 276 277 278
            const double tval = scaleMap().transform( value );
            if ( ( tval < d_data->startAngle + 360 )
                && ( tval > d_data->startAngle - 360 ) )
            {
                const double arc = qwtRadians( tval );
pixhawk's avatar
pixhawk committed
279

Bryant's avatar
Bryant committed
280 281
                const QSizeF sz = label.textSize( font );
                const double off = qMax( sz.width(), sz.height() );
pixhawk's avatar
pixhawk committed
282

Bryant's avatar
Bryant committed
283 284
                double x = off * qSin( arc );
                double y = off * qCos( arc );
pixhawk's avatar
pixhawk committed
285

Bryant's avatar
Bryant committed
286
                const double dist = qSqrt( x * x + y * y );
pixhawk's avatar
pixhawk committed
287 288 289 290 291 292
                if ( dist > d )
                    d = dist;
            }
        }
    }

Bryant's avatar
Bryant committed
293 294 295
    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
    {
        d += maxTickLength();
pixhawk's avatar
pixhawk committed
296 297
    }

Bryant's avatar
Bryant committed
298 299 300
    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
    {
        const double pw = qMax( 1, penWidth() );  // pen width can be zero
pixhawk's avatar
pixhawk committed
301 302 303
        d += pw;
    }

Bryant's avatar
Bryant committed
304 305 306 307
    if ( hasComponent( QwtAbstractScaleDraw::Labels ) &&
        ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
            hasComponent( QwtAbstractScaleDraw::Backbone ) ) )
    {
pixhawk's avatar
pixhawk committed
308 309 310
        d += spacing();
    }

Bryant's avatar
Bryant committed
311
    d = qMax( d, minimumExtent() );
pixhawk's avatar
pixhawk committed
312 313 314

    return d;
}