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 // vim: expandtab
11
12 #include <math.h>
13 #include <qpen.h>
14 #include <qpainter.h>
15 #include <qfontmetrics.h>
16 #include "qwt_painter.h"
17 #include "qwt_scale_div.h"
18 #include "qwt_scale_map.h"
19 #include "qwt_round_scale_draw.h"
20
21 class QwtRoundScaleDraw::PrivateData
22 {
23 public:
PrivateData()24 PrivateData():
25 center(50, 50),
26 radius(50),
27 startAngle(-135 * 16),
28 endAngle(135 * 16)
29 {
30 }
31
32 QPoint center;
33 int radius;
34
35 int startAngle;
36 int endAngle;
37 };
38
39 /*!
40 \brief Constructor
41
42 The range of the scale is initialized to [0, 100],
43 The center is set to (50, 50) with a radius of 50.
44 The angle range is set to [-135, 135].
45 */
QwtRoundScaleDraw()46 QwtRoundScaleDraw::QwtRoundScaleDraw()
47 {
48 d_data = new QwtRoundScaleDraw::PrivateData;
49
50 setRadius(50);
51 scaleMap().setPaintInterval(d_data->startAngle, d_data->endAngle);
52 }
53
54 //! Copy constructor
QwtRoundScaleDraw(const QwtRoundScaleDraw & other)55 QwtRoundScaleDraw::QwtRoundScaleDraw(const QwtRoundScaleDraw &other):
56 QwtAbstractScaleDraw(other)
57 {
58 d_data = new QwtRoundScaleDraw::PrivateData(*other.d_data);
59 }
60
61
62 //! Destructor
~QwtRoundScaleDraw()63 QwtRoundScaleDraw::~QwtRoundScaleDraw()
64 {
65 delete d_data;
66 }
67
68 //! Assignment operator
operator =(const QwtRoundScaleDraw & other)69 QwtRoundScaleDraw &QwtRoundScaleDraw::operator=(const QwtRoundScaleDraw &other)
70 {
71 *(QwtAbstractScaleDraw*)this = (const QwtAbstractScaleDraw &)other;
72 *d_data = *other.d_data;
73 return *this;
74 }
75
76 /*!
77 Change of radius the scale
78
79 Radius is the radius of the backbone without ticks and labels.
80
81 \param radius New Radius
82 \sa moveCenter()
83 */
setRadius(int radius)84 void QwtRoundScaleDraw::setRadius(int radius)
85 {
86 d_data->radius = radius;
87 }
88
89 /*!
90 Get the radius
91
92 Radius is the radius of the backbone without ticks and labels.
93
94 \sa setRadius(), extent()
95 */
radius() const96 int QwtRoundScaleDraw::radius() const
97 {
98 return d_data->radius;
99 }
100
101 /*!
102 Move the center of the scale draw, leaving the radius unchanged
103
104 \param center New center
105 \sa setRadius()
106 */
moveCenter(const QPoint & center)107 void QwtRoundScaleDraw::moveCenter(const QPoint ¢er)
108 {
109 d_data->center = center;
110 }
111
112 //! Get the center of the scale
center() const113 QPoint QwtRoundScaleDraw::center() const
114 {
115 return d_data->center;
116 }
117
118 /*!
119 \brief Adjust the baseline circle segment for round scales.
120
121 The baseline will be drawn from min(angle1,angle2) to max(angle1, angle2).
122 The default setting is [ -135, 135 ].
123 An angle of 0 degrees corresponds to the 12 o'clock position,
124 and positive angles count in a clockwise direction.
125 \param angle1
126 \param angle2 boundaries of the angle interval in degrees.
127 \warning <ul>
128 <li>The angle range is limited to [-360, 360] degrees. Angles exceeding
129 this range will be clipped.
130 <li>For angles more than 359 degrees above or below min(angle1, angle2),
131 scale marks will not be drawn.
132 <li>If you need a counterclockwise scale, use QwtScaleDiv::setRange
133 </ul>
134 */
setAngleRange(double angle1,double angle2)135 void QwtRoundScaleDraw::setAngleRange(double angle1, double angle2)
136 {
137 angle1 = qwtLim(angle1, -360.0, 360.0);
138 angle2 = qwtLim(angle2, -360.0, 360.0);
139
140 d_data->startAngle = qRound(angle1 * 16.0);
141 d_data->endAngle = qRound(angle2 * 16.0);
142
143 if (d_data->startAngle == d_data->endAngle)
144 {
145 d_data->startAngle -= 1;
146 d_data->endAngle += 1;
147 }
148
149 scaleMap().setPaintInterval(d_data->startAngle, d_data->endAngle);
150 }
151
152 /*!
153 Draws the label for a major scale tick
154
155 \param painter Painter
156 \param value Value
157
158 \sa drawTick(), drawBackbone()
159 */
drawLabel(QPainter * painter,double value) const160 void QwtRoundScaleDraw::drawLabel(QPainter *painter, double value) const
161 {
162 const QwtText label = tickLabel(painter->font(), value);
163 if ( label.isEmpty() )
164 return;
165
166 const int tval = map().transform(value);
167 if ((tval > d_data->startAngle + 359 * 16)
168 || (tval < d_data->startAngle - 359 * 16))
169 {
170 return;
171 }
172
173 double radius = d_data->radius;
174 if ( hasComponent(QwtAbstractScaleDraw::Ticks) ||
175 hasComponent(QwtAbstractScaleDraw::Backbone) )
176 {
177 radius += spacing();
178 }
179
180 if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
181 radius += majTickLength();
182
183 const QSize sz = label.textSize(painter->font());
184 const double arc = tval / 16.0 / 360.0 * 2 * M_PI;
185
186 const int x = d_data->center.x() +
187 qRound((radius + sz.width() / 2.0) * sin(arc));
188 const int y = d_data->center.y() -
189 qRound( (radius + sz.height() / 2.0) * cos(arc));
190
191 const QRect r(x - sz.width() / 2, y - sz.height() / 2,
192 sz.width(), sz.height() );
193 label.draw(painter, r);
194 }
195
196 /*!
197 Draw a tick
198
199 \param painter Painter
200 \param value Value of the tick
201 \param len Lenght of the tick
202
203 \sa drawBackbone(), drawLabel()
204 */
drawTick(QPainter * painter,double value,int len) const205 void QwtRoundScaleDraw::drawTick(QPainter *painter, double value, int len) const
206 {
207 if ( len <= 0 )
208 return;
209
210 const int tval = map().transform(value);
211
212 const int cx = d_data->center.x();
213 const int cy = d_data->center.y();
214 const int radius = d_data->radius;
215
216 if ((tval <= d_data->startAngle + 359 * 16)
217 || (tval >= d_data->startAngle - 359 * 16))
218 {
219 const double arc = double(tval) / 16.0 * M_PI / 180.0;
220
221 const double sinArc = sin(arc);
222 const double cosArc = cos(arc);
223
224 const int x1 = qRound( cx + radius * sinArc );
225 const int x2 = qRound( cx + (radius + len) * sinArc );
226 const int y1 = qRound( cy - radius * cosArc );
227 const int y2 = qRound( cy - (radius + len) * cosArc );
228
229 QwtPainter::drawLine(painter, x1, y1, x2, y2);
230 }
231 }
232
233 /*!
234 Draws the baseline of the scale
235 \param painter Painter
236
237 \sa drawTick(), drawLabel()
238 */
drawBackbone(QPainter * painter) const239 void QwtRoundScaleDraw::drawBackbone(QPainter *painter) const
240 {
241 const int a1 = qRound(qwtMin(map().p1(), map().p2()) - 90 * 16);
242 const int a2 = qRound(qwtMax(map().p1(), map().p2()) - 90 * 16);
243
244 const int radius = d_data->radius;
245 const int x = d_data->center.x() - radius;
246 const int y = d_data->center.y() - radius;
247
248 painter->drawArc(x, y, 2 * radius, 2 * radius,
249 -a2, a2 - a1 + 1); // counterclockwise
250 }
251
252 /*!
253 Calculate the extent of the scale
254
255 The extent is the distcance between the baseline to the outermost
256 pixel of the scale draw. radius() + extent() is an upper limit
257 for the radius of the bounding circle.
258
259 \param pen Pen that is used for painting backbone and ticks
260 \param font Font used for painting the labels
261
262 \sa setMinimumExtent(), minimumExtent()
263 \warning The implemented algo is not too smart and
264 calculates only an upper limit, that might be a
265 few pixels too large
266 */
extent(const QPen & pen,const QFont & font) const267 int QwtRoundScaleDraw::extent(const QPen &pen, const QFont &font) const
268 {
269 int d = 0;
270
271 if ( hasComponent(QwtAbstractScaleDraw::Labels) )
272 {
273 const QwtScaleDiv &sd = scaleDiv();
274 const QwtValueList &ticks = sd.ticks(QwtScaleDiv::MajorTick);
275 for (uint i = 0; i < (uint)ticks.count(); i++)
276 {
277 const double value = ticks[i];
278 if ( !sd.contains(value) )
279 continue;
280
281 const QwtText label = tickLabel(font, value);
282 if ( label.isEmpty() )
283 continue;
284
285 const int tval = map().transform(value);
286 if ((tval < d_data->startAngle + 360 * 16)
287 && (tval > d_data->startAngle - 360 * 16))
288 {
289 const double arc = tval / 16.0 / 360.0 * 2 * M_PI;
290
291 const QSize sz = label.textSize(font);
292 const double off = qwtMax(sz.width(), sz.height());
293
294 double x = off * sin(arc);
295 double y = off * cos(arc);
296
297 const int dist = (int)ceil(sqrt(x * x + y * y) + 1 );
298 if ( dist > d )
299 d = dist;
300 }
301 }
302 }
303
304 if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
305 {
306 d += majTickLength();
307 }
308
309 if ( hasComponent(QwtAbstractScaleDraw::Backbone) )
310 {
311 const int pw = qwtMax( 1, pen.width() ); // penwidth can be zero
312 d += pw;
313 }
314
315 if ( hasComponent(QwtAbstractScaleDraw::Labels) &&
316 ( hasComponent(QwtAbstractScaleDraw::Ticks) ||
317 hasComponent(QwtAbstractScaleDraw::Backbone) ) )
318 {
319 d += spacing();
320 }
321
322 d = qwtMax(d, minimumExtent());
323
324 return d;
325 }
326