1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2  * Qwt Widget Library
3  * Copyright (C) 1997   Josef Wilgen
4  * Copyright (C) 2002   Uwe Rathmann
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the Qwt License, Version 1.0
8  *****************************************************************************/
9 
10 #include "qwt_round_scale_draw.h"
11 #include "qwt_painter.h"
12 #include "qwt_scale_div.h"
13 #include "qwt_scale_map.h"
14 #include "qwt_math.h"
15 #include <qpen.h>
16 #include <qpainter.h>
17 #include <qfontmetrics.h>
18 #include <qmath.h>
19 
20 class QwtRoundScaleDraw::PrivateData
21 {
22 public:
PrivateData()23     PrivateData():
24         center( 50.0, 50.0 ),
25         radius( 50.0 ),
26         startAngle( -135.0 ),
27         endAngle( 135.0 )
28     {
29     }
30 
31     QPointF center;
32     double radius;
33 
34     double startAngle;
35     double endAngle;
36 };
37 
38 /*!
39   \brief Constructor
40 
41   The range of the scale is initialized to [0, 100],
42   The center is set to (50, 50) with a radius of 50.
43   The angle range is set to [-135, 135].
44 */
QwtRoundScaleDraw()45 QwtRoundScaleDraw::QwtRoundScaleDraw()
46 {
47     d_data = new QwtRoundScaleDraw::PrivateData;
48 
49     setRadius( 50 );
50     scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
51 }
52 
53 //! Destructor
~QwtRoundScaleDraw()54 QwtRoundScaleDraw::~QwtRoundScaleDraw()
55 {
56     delete d_data;
57 }
58 
59 /*!
60   Change of radius the scale
61 
62   Radius is the radius of the backbone without ticks and labels.
63 
64   \param radius New Radius
65   \sa moveCenter()
66 */
setRadius(double radius)67 void QwtRoundScaleDraw::setRadius( double radius )
68 {
69     d_data->radius = radius;
70 }
71 
72 /*!
73   Get the radius
74 
75   Radius is the radius of the backbone without ticks and labels.
76 
77   \return Radius of the scale
78   \sa setRadius(), extent()
79 */
radius() const80 double QwtRoundScaleDraw::radius() const
81 {
82     return d_data->radius;
83 }
84 
85 /*!
86    Move the center of the scale draw, leaving the radius unchanged
87 
88    \param center New center
89    \sa setRadius()
90 */
moveCenter(const QPointF & center)91 void QwtRoundScaleDraw::moveCenter( const QPointF &center )
92 {
93     d_data->center = center;
94 }
95 
96 //! Get the center of the scale
center() const97 QPointF QwtRoundScaleDraw::center() const
98 {
99     return d_data->center;
100 }
101 
102 /*!
103   \brief Adjust the baseline circle segment for round scales.
104 
105   The baseline will be drawn from min(angle1,angle2) to max(angle1, angle2).
106   The default setting is [ -135, 135 ].
107   An angle of 0 degrees corresponds to the 12 o'clock position,
108   and positive angles count in a clockwise direction.
109   \param angle1
110   \param angle2 boundaries of the angle interval in degrees.
111   \warning <ul>
112   <li>The angle range is limited to [-360, 360] degrees. Angles exceeding
113       this range will be clipped.
114   <li>For angles more or equal than 360 degrees above or below min(angle1, angle2),
115       scale marks will not be drawn.
116   <li>If you need a counterclockwise scale, use QwtScaleDiv::setInterval()
117   </ul>
118 */
setAngleRange(double angle1,double angle2)119 void QwtRoundScaleDraw::setAngleRange( double angle1, double angle2 )
120 {
121 #if 0
122     angle1 = qBound( -360.0, angle1, 360.0 );
123     angle2 = qBound( -360.0, angle2, 360.0 );
124 #endif
125 
126     d_data->startAngle = angle1;
127     d_data->endAngle = angle2;
128 
129     if ( d_data->startAngle == d_data->endAngle )
130     {
131         d_data->startAngle -= 1;
132         d_data->endAngle += 1;
133     }
134 
135     scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
136 }
137 
138 /*!
139    Draws the label for a major scale tick
140 
141    \param painter Painter
142    \param value Value
143 
144    \sa drawTick(), drawBackbone()
145 */
drawLabel(QPainter * painter,double value) const146 void QwtRoundScaleDraw::drawLabel( QPainter *painter, double value ) const
147 {
148     const double tval = scaleMap().transform( value );
149     if ( ( tval >= d_data->startAngle + 360.0 )
150         || ( tval <= d_data->startAngle - 360.0 ) )
151     {
152         return;
153     }
154 
155     const QwtText label = tickLabel( painter->font(), value );
156     if ( label.isEmpty() )
157         return;
158 
159     double radius = d_data->radius;
160     if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
161         hasComponent( QwtAbstractScaleDraw::Backbone ) )
162     {
163         radius += spacing();
164     }
165 
166     if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
167         radius += tickLength( QwtScaleDiv::MajorTick );
168 
169     const QSizeF sz = label.textSize( painter->font() );
170     const double arc = qwtRadians( tval );
171 
172     const double x = d_data->center.x() +
173         ( radius + sz.width() / 2.0 ) * qSin( arc );
174     const double y = d_data->center.y() -
175         ( radius + sz.height() / 2.0 ) * qCos( arc );
176 
177     const QRectF r( x - sz.width() / 2, y - sz.height() / 2,
178         sz.width(), sz.height() );
179     label.draw( painter, r );
180 }
181 
182 /*!
183    Draw a tick
184 
185    \param painter Painter
186    \param value Value of the tick
187    \param len Length of the tick
188 
189    \sa drawBackbone(), drawLabel()
190 */
drawTick(QPainter * painter,double value,double len) const191 void QwtRoundScaleDraw::drawTick( QPainter *painter, double value, double len ) const
192 {
193     if ( len <= 0 )
194         return;
195 
196     const double tval = scaleMap().transform( value );
197 
198     const double cx = d_data->center.x();
199     const double cy = d_data->center.y();
200     const double radius = d_data->radius;
201 
202     if ( ( tval < d_data->startAngle + 360.0 )
203         && ( tval > d_data->startAngle - 360.0 ) )
204     {
205         const double arc = qwtRadians( tval );
206 
207         const double sinArc = qSin( arc );
208         const double cosArc = qCos( arc );
209 
210         const double x1 = cx + radius * sinArc;
211         const double x2 = cx + ( radius + len ) * sinArc;
212         const double y1 = cy - radius * cosArc;
213         const double y2 = cy - ( radius + len ) * cosArc;
214 
215         QwtPainter::drawLine( painter, x1, y1, x2, y2 );
216     }
217 }
218 
219 /*!
220    Draws the baseline of the scale
221    \param painter Painter
222 
223    \sa drawTick(), drawLabel()
224 */
drawBackbone(QPainter * painter) const225 void QwtRoundScaleDraw::drawBackbone( QPainter *painter ) const
226 {
227     const double deg1 = scaleMap().p1();
228     const double deg2 = scaleMap().p2();
229 
230     const int a1 = qRound( qMin( deg1, deg2 ) - 90 );
231     const int a2 = qRound( qMax( deg1, deg2 ) - 90 );
232 
233     const double radius = d_data->radius;
234     const double x = d_data->center.x() - radius;
235     const double y = d_data->center.y() - radius;
236 
237     painter->drawArc( QRectF( x, y, 2 * radius, 2 * radius ),
238         -a2 * 16, ( a2 - a1 + 1 ) * 16 );          // counterclockwise
239 }
240 
241 /*!
242    Calculate the extent of the scale
243 
244    The extent is the distance between the baseline to the outermost
245    pixel of the scale draw. radius() + extent() is an upper limit
246    for the radius of the bounding circle.
247 
248    \param font Font used for painting the labels
249    \return Calculated extent
250 
251    \sa setMinimumExtent(), minimumExtent()
252    \warning The implemented algorithm is not too smart and
253             calculates only an upper limit, that might be a
254             few pixels too large
255 */
extent(const QFont & font) const256 double QwtRoundScaleDraw::extent( const QFont &font ) const
257 {
258     double d = 0.0;
259 
260     if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
261     {
262         const QwtScaleDiv &sd = scaleDiv();
263         const QList<double> &ticks = sd.ticks( QwtScaleDiv::MajorTick );
264         for ( int i = 0; i < ticks.count(); i++ )
265         {
266             const double value = ticks[i];
267             if ( !sd.contains( value ) )
268                 continue;
269 
270             const double tval = scaleMap().transform( value );
271             if ( ( tval < d_data->startAngle + 360 )
272                 && ( tval > d_data->startAngle - 360 ) )
273             {
274                 const QwtText label = tickLabel( font, value );
275                 if ( label.isEmpty() )
276                     continue;
277 
278                 const double arc = qwtRadians( tval );
279 
280                 const QSizeF sz = label.textSize( font );
281                 const double off = qMax( sz.width(), sz.height() );
282 
283                 double x = off * qSin( arc );
284                 double y = off * qCos( arc );
285 
286                 const double dist = qSqrt( x * x + y * y );
287                 if ( dist > d )
288                     d = dist;
289             }
290         }
291     }
292 
293     if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
294     {
295         d += maxTickLength();
296     }
297 
298     if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
299     {
300         const double pw = qMax( 1, penWidth() );  // pen width can be zero
301         d += pw;
302     }
303 
304     if ( hasComponent( QwtAbstractScaleDraw::Labels ) &&
305         ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
306             hasComponent( QwtAbstractScaleDraw::Backbone ) ) )
307     {
308         d += spacing();
309     }
310 
311     d = qMax( d, minimumExtent() );
312 
313     return d;
314 }
315