1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2  * QwtPolar Widget Library
3  * Copyright (C) 2008   Uwe Rathmann
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the Qwt License, Version 1.0
7  *****************************************************************************/
8 
9 #include "qwt_polar_curve.h"
10 #include "qwt_polar.h"
11 #include <qwt_painter.h>
12 #include <qwt_scale_map.h>
13 #include <qwt_math.h>
14 #include <qwt_symbol.h>
15 #include <qwt_legend.h>
16 #include <qwt_curve_fitter.h>
17 #include <qwt_clipper.h>
18 #include <qpainter.h>
19 
qwtInsidePole(const QwtScaleMap & map,double radius)20 static inline bool qwtInsidePole( const QwtScaleMap &map, double radius )
21 {
22     return map.isInverting() ? ( radius > map.s1() ) : ( radius < map.s1() );
23 }
24 
qwtVerifyRange(int size,int & i1,int & i2)25 static int qwtVerifyRange( int size, int &i1, int &i2 )
26 {
27     if ( size < 1 )
28         return 0;
29 
30     i1 = qBound( 0, i1, size - 1 );
31     i2 = qBound( 0, i2, size - 1 );
32 
33     if ( i1 > i2 )
34         qSwap( i1, i2 );
35 
36     return ( i2 - i1 + 1 );
37 }
38 
39 class QwtPolarCurve::PrivateData
40 {
41 public:
PrivateData()42     PrivateData():
43         style( QwtPolarCurve::Lines ),
44         curveFitter( NULL ),
45         legendAttributes( 0 )
46     {
47         symbol = new QwtSymbol();
48         pen = QPen( Qt::black );
49     }
50 
~PrivateData()51     ~PrivateData()
52     {
53         delete symbol;
54         delete curveFitter;
55     }
56 
57     QwtPolarCurve::CurveStyle style;
58     const QwtSymbol *symbol;
59     QPen pen;
60     QwtCurveFitter *curveFitter;
61 
62     QwtPolarCurve::LegendAttributes legendAttributes;
63 };
64 
65 //! Constructor
QwtPolarCurve()66 QwtPolarCurve::QwtPolarCurve():
67     QwtPolarItem( QwtText() )
68 {
69     init();
70 }
71 
72 /*!
73   Constructor
74   \param title title of the curve
75 */
QwtPolarCurve(const QwtText & title)76 QwtPolarCurve::QwtPolarCurve( const QwtText &title ):
77     QwtPolarItem( title )
78 {
79     init();
80 }
81 
82 /*!
83   Constructor
84   \param title title of the curve
85 */
QwtPolarCurve(const QString & title)86 QwtPolarCurve::QwtPolarCurve( const QString &title ):
87     QwtPolarItem( QwtText( title ) )
88 {
89     init();
90 }
91 
92 //! Destructor
~QwtPolarCurve()93 QwtPolarCurve::~QwtPolarCurve()
94 {
95     delete d_series;
96     delete d_data;
97 }
98 
99 //! Initialize data members
init()100 void QwtPolarCurve::init()
101 {
102     d_data = new PrivateData;
103     d_series = NULL;
104 
105     setItemAttribute( QwtPolarItem::AutoScale );
106     setItemAttribute( QwtPolarItem::Legend );
107     setZ( 20.0 );
108 
109     setRenderHint( RenderAntialiased, true );
110 }
111 
112 //! \return QwtPolarCurve::Rtti_PolarCurve
rtti() const113 int QwtPolarCurve::rtti() const
114 {
115     return QwtPolarItem::Rtti_PolarCurve;
116 }
117 
118 /*!
119   Specify an attribute how to draw the legend identifier
120 
121   \param attribute Attribute
122   \param on On/Off
123   /sa LegendAttribute, testLegendAttribute()
124 */
setLegendAttribute(LegendAttribute attribute,bool on)125 void QwtPolarCurve::setLegendAttribute( LegendAttribute attribute, bool on )
126 {
127     if ( on )
128         d_data->legendAttributes |= attribute;
129     else
130         d_data->legendAttributes &= ~attribute;
131 }
132 
133 /*!
134     \brief Test if a lefend attribute is enables
135 
136     \param attribute Legend attribute
137 
138     \return True if attribute is enabled
139     \sa LegendAttribute, setLegendAttribute()
140 */
testLegendAttribute(LegendAttribute attribute) const141 bool QwtPolarCurve::testLegendAttribute( LegendAttribute attribute ) const
142 {
143     return ( d_data->legendAttributes & attribute );
144 }
145 
146 /*!
147   Set the curve's drawing style
148 
149   \param style Curve style
150   \sa CurveStyle, style()
151 */
setStyle(CurveStyle style)152 void QwtPolarCurve::setStyle( CurveStyle style )
153 {
154     if ( style != d_data->style )
155     {
156         d_data->style = style;
157         itemChanged();
158     }
159 }
160 
161 /*!
162     \return Current style
163     \sa CurveStyle, setStyle()
164 */
style() const165 QwtPolarCurve::CurveStyle QwtPolarCurve::style() const
166 {
167     return d_data->style;
168 }
169 
170 /*!
171   \brief Assign a symbol
172   \param symbol Symbol
173   \sa symbol()
174 */
setSymbol(QwtSymbol * symbol)175 void QwtPolarCurve::setSymbol( QwtSymbol *symbol )
176 {
177     if ( symbol != d_data->symbol )
178     {
179         delete d_data->symbol;
180         d_data->symbol = symbol;
181         itemChanged();
182     }
183 }
184 
185 /*!
186     \return The current symbol
187     \sa setSymbol()
188 */
symbol() const189 const QwtSymbol *QwtPolarCurve::symbol() const
190 {
191     return d_data->symbol;
192 }
193 
194 /*!
195   \brief Assign a pen
196   \param pen New pen
197   \sa pen()
198 */
setPen(const QPen & pen)199 void QwtPolarCurve::setPen( const QPen &pen )
200 {
201     if ( pen != d_data->pen )
202     {
203         d_data->pen = pen;
204         itemChanged();
205     }
206 }
207 
208 /*!
209     \return Pen used to draw the lines
210     \sa setPen()
211 */
pen() const212 const QPen& QwtPolarCurve::pen() const
213 {
214     return d_data->pen;
215 }
216 
217 /*!
218   Initialize data with a pointer to QwtSeriesData<QwtPointPolar>.
219 
220   The x-values of the data object represent the azimuth,
221   the y-value respresent the radius.
222 
223   \param data Data
224 */
setData(QwtSeriesData<QwtPointPolar> * data)225 void QwtPolarCurve::setData( QwtSeriesData<QwtPointPolar> *data )
226 {
227     if ( d_series != data )
228     {
229         delete d_series;
230         d_series = data;
231         itemChanged();
232     }
233 }
234 
235 /*!
236   \brief Insert a curve fitter
237 
238   \param curveFitter Curve fitter
239 
240   A curve fitter interpolates the curve points. F.e QwtPolarFitter
241   adds equidistant points so that the connection gets rounded instead
242   of having straight lines. If curveFitter is NULL fitting is disabled.
243 
244   \sa curveFitter()
245 */
setCurveFitter(QwtCurveFitter * curveFitter)246 void QwtPolarCurve::setCurveFitter( QwtCurveFitter *curveFitter )
247 {
248     if ( curveFitter != d_data->curveFitter )
249     {
250         delete d_data->curveFitter;
251         d_data->curveFitter = curveFitter;
252 
253         itemChanged();
254     }
255 }
256 
257 /*!
258   \return The curve fitter
259   \sa setCurveFitter()
260 */
curveFitter() const261 QwtCurveFitter *QwtPolarCurve::curveFitter() const
262 {
263     return d_data->curveFitter;
264 }
265 
266 /*!
267   Draw the curve
268 
269   \param painter Painter
270   \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
271   \param radialMap Maps radius values into painter coordinates.
272   \param pole Position of the pole in painter coordinates
273   \param radius Radius of the complete plot area in painter coordinates
274   \param canvasRect Contents rect of the canvas in painter coordinates
275 */
draw(QPainter * painter,const QwtScaleMap & azimuthMap,const QwtScaleMap & radialMap,const QPointF & pole,double radius,const QRectF & canvasRect) const276 void QwtPolarCurve::draw( QPainter *painter,
277     const QwtScaleMap &azimuthMap, const QwtScaleMap &radialMap,
278     const QPointF &pole, double radius,
279     const QRectF &canvasRect ) const
280 {
281     Q_UNUSED( radius );
282     Q_UNUSED( canvasRect );
283 
284     draw( painter, azimuthMap, radialMap, pole, 0, -1 );
285 }
286 
287 /*!
288   \brief Draw an interval of the curve
289   \param painter Painter
290   \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
291   \param radialMap Maps radius values into painter coordinates.
292   \param pole Position of the pole in painter coordinates
293   \param from index of the first point to be painted
294   \param to index of the last point to be painted. If to < 0 the
295          curve will be painted to its last point.
296 
297   \sa drawCurve(), drawSymbols(),
298 */
draw(QPainter * painter,const QwtScaleMap & azimuthMap,const QwtScaleMap & radialMap,const QPointF & pole,int from,int to) const299 void QwtPolarCurve::draw( QPainter *painter,
300     const QwtScaleMap &azimuthMap, const QwtScaleMap &radialMap,
301     const QPointF &pole, int from, int to ) const
302 {
303     if ( !painter || dataSize() <= 0 )
304         return;
305 
306     if ( to < 0 )
307         to = dataSize() - 1;
308 
309     if ( qwtVerifyRange( dataSize(), from, to ) > 0 )
310     {
311         painter->save();
312         painter->setPen( d_data->pen );
313 
314         drawCurve( painter, d_data->style,
315             azimuthMap, radialMap, pole, from, to );
316 
317         painter->restore();
318 
319         if ( d_data->symbol->style() != QwtSymbol::NoSymbol )
320         {
321             painter->save();
322             drawSymbols( painter, *d_data->symbol,
323                 azimuthMap, radialMap, pole, from, to );
324             painter->restore();
325         }
326     }
327 }
328 
329 /*!
330   Draw the line part (without symbols) of a curve interval.
331 
332   \param painter Painter
333   \param style Curve style, see QwtPolarCurve::CurveStyle
334   \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
335   \param radialMap Maps radius values into painter coordinates.
336   \param pole Position of the pole in painter coordinates
337   \param from index of the first point to be painted
338   \param to index of the last point to be painted.
339   \sa draw(), drawLines()
340 */
drawCurve(QPainter * painter,int style,const QwtScaleMap & azimuthMap,const QwtScaleMap & radialMap,const QPointF & pole,int from,int to) const341 void QwtPolarCurve::drawCurve( QPainter *painter, int style,
342     const QwtScaleMap &azimuthMap, const QwtScaleMap &radialMap,
343     const QPointF &pole, int from, int to ) const
344 {
345     switch ( style )
346     {
347         case Lines:
348             drawLines( painter, azimuthMap, radialMap, pole, from, to );
349             break;
350         case NoCurve:
351         default:
352             break;
353     }
354 }
355 
356 /*!
357   Draw lines
358 
359   \param painter Painter
360   \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
361   \param radialMap Maps radius values into painter coordinates.
362   \param pole Position of the pole in painter coordinates
363   \param from index of the first point to be painted
364   \param to index of the last point to be painted.
365   \sa draw(), drawLines(), setCurveFitter()
366 */
drawLines(QPainter * painter,const QwtScaleMap & azimuthMap,const QwtScaleMap & radialMap,const QPointF & pole,int from,int to) const367 void QwtPolarCurve::drawLines( QPainter *painter,
368     const QwtScaleMap &azimuthMap, const QwtScaleMap &radialMap,
369     const QPointF &pole, int from, int to ) const
370 {
371     int size = to - from + 1;
372     if ( size <= 0 )
373         return;
374 
375     QPolygonF polyline;
376     if ( d_data->curveFitter )
377     {
378         QPolygonF points( size );
379         for ( int j = from; j <= to; j++ )
380         {
381             const QwtPointPolar point = sample( j );
382             points[j - from] = QPointF( point.azimuth(), point.radius() );
383         }
384 
385         points = d_data->curveFitter->fitCurve( points );
386 
387         polyline.resize( points.size() );
388 
389         QPointF *polylineData = polyline.data();
390         QPointF *pointsData = points.data();
391 
392         for ( int i = 0; i < points.size(); i++ )
393         {
394             const QwtPointPolar point( pointsData[i].x(), pointsData[i].y() );
395 
396             double r = radialMap.transform( point.radius() );
397             const double a = azimuthMap.transform( point.azimuth() );
398 
399             polylineData[i] = qwtPolar2Pos( pole, r, a );
400         }
401     }
402     else
403     {
404         polyline.resize( size );
405         QPointF *polylineData = polyline.data();
406 
407         for ( int i = from; i <= to; i++ )
408         {
409             QwtPointPolar point = sample( i );
410             if ( !qwtInsidePole( radialMap, point.radius() ) )
411             {
412                 double r = radialMap.transform( point.radius() );
413                 const double a = azimuthMap.transform( point.azimuth() );
414                 polylineData[i - from] = qwtPolar2Pos( pole, r, a );
415             }
416             else
417             {
418                 polylineData[i - from] = pole;
419             }
420         }
421     }
422 
423     QRectF clipRect;
424     if ( painter->hasClipping() )
425         clipRect = painter->clipRegion().boundingRect();
426     else
427     {
428         clipRect = painter->window();
429         if ( !clipRect.isEmpty() )
430             clipRect = painter->transform().inverted().mapRect( clipRect );
431     }
432 
433     if ( !clipRect.isEmpty() )
434     {
435         double off = qCeil( qMax( qreal( 1.0 ), painter->pen().widthF() ) );
436         clipRect = clipRect.toRect().adjusted( -off, -off, off, off );
437         polyline = QwtClipper::clipPolygonF( clipRect, polyline );
438     }
439 
440     QwtPainter::drawPolyline( painter, polyline );
441     painter->drawPolyline( polyline );
442 }
443 
444 /*!
445   Draw symbols
446 
447   \param painter Painter
448   \param symbol Curve symbol
449   \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
450   \param radialMap Maps radius values into painter coordinates.
451   \param pole Position of the pole in painter coordinates
452   \param from index of the first point to be painted
453   \param to index of the last point to be painted.
454 
455   \sa setSymbol(), draw(), drawCurve()
456 */
drawSymbols(QPainter * painter,const QwtSymbol & symbol,const QwtScaleMap & azimuthMap,const QwtScaleMap & radialMap,const QPointF & pole,int from,int to) const457 void QwtPolarCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol,
458     const QwtScaleMap &azimuthMap, const QwtScaleMap &radialMap,
459     const QPointF &pole, int from, int to ) const
460 {
461     painter->setBrush( symbol.brush() );
462     painter->setPen( symbol.pen() );
463 
464     const int chunkSize = 500;
465 
466     for ( int i = from; i <= to; i += chunkSize )
467     {
468         const int n = qMin( chunkSize, to - i + 1 );
469 
470         QPolygonF points;
471         for ( int j = 0; j < n; j++ )
472         {
473             const QwtPointPolar point = sample( i + j );
474 
475             if ( !qwtInsidePole( radialMap, point.radius() ) )
476             {
477                 const double r = radialMap.transform( point.radius() );
478                 const double a = azimuthMap.transform( point.azimuth() );
479 
480                 points += qwtPolar2Pos( pole, r, a );
481             }
482             else
483             {
484                 points += pole;
485             }
486         }
487 
488         if ( points.size() > 0 )
489             symbol.drawSymbols( painter, points );
490     }
491 }
492 
493 /*!
494   \return Number of points
495   \sa setData()
496 */
dataSize() const497 size_t QwtPolarCurve::dataSize() const
498 {
499     return d_series->size();
500 }
501 
502 /*!
503    \return Icon representing the curve on the legend
504 
505    \param index Index of the legend entry
506                 ( ignored as there is only one )
507    \param size Icon size
508 
509    \sa QwtPolarItem::setLegendIconSize(), QwtPolarItem::legendData()
510  */
legendIcon(int index,const QSizeF & size) const511 QwtGraphic QwtPolarCurve::legendIcon( int index,
512     const QSizeF &size ) const
513 {
514     Q_UNUSED( index );
515 
516     if ( size.isEmpty() )
517         return QwtGraphic();
518 
519     QwtGraphic graphic;
520     graphic.setDefaultSize( size );
521     graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
522 
523     QPainter painter( &graphic );
524     painter.setRenderHint( QPainter::Antialiasing,
525         testRenderHint( QwtPolarItem::RenderAntialiased ) );
526 
527     if ( d_data->legendAttributes == 0 )
528     {
529         QBrush brush;
530 
531         if ( style() != QwtPolarCurve::NoCurve )
532         {
533             brush = QBrush( pen().color() );
534         }
535         else if ( d_data->symbol &&
536             ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
537         {
538             brush = QBrush( d_data->symbol->pen().color() );
539         }
540 
541         if ( brush.style() != Qt::NoBrush )
542         {
543             QRectF r( 0, 0, size.width(), size.height() );
544             painter.fillRect( r, brush );
545         }
546     }
547 
548     if ( d_data->legendAttributes & QwtPolarCurve::LegendShowLine )
549     {
550         if ( pen() != Qt::NoPen )
551         {
552             QPen pn = pen();
553             pn.setCapStyle( Qt::FlatCap );
554 
555             painter.setPen( pn );
556 
557             const double y = 0.5 * size.height();
558             QwtPainter::drawLine( &painter, 0.0, y, size.width(), y );
559         }
560     }
561 
562     if ( d_data->legendAttributes & QwtPolarCurve::LegendShowSymbol )
563     {
564         if ( d_data->symbol )
565         {
566             QRectF r( 0, 0, size.width(), size.height() );
567             d_data->symbol->drawSymbol( &painter, r );
568         }
569     }
570 
571     return graphic;
572 }
573 
574 /*!
575    Interval, that is necessary to display the item
576    This interval can be useful for operations like clipping or autoscaling
577 
578    \param scaleId Scale index
579    \return bounding interval
580 
581    \sa QwtData::boundingRect()
582 */
boundingInterval(int scaleId) const583 QwtInterval QwtPolarCurve::boundingInterval( int scaleId ) const
584 {
585     const QRectF boundingRect = d_series->boundingRect();
586 
587     if ( scaleId == QwtPolar::ScaleAzimuth )
588         return QwtInterval( boundingRect.left(), boundingRect.right() );
589 
590     if ( scaleId == QwtPolar::ScaleRadius )
591         return QwtInterval( boundingRect.top(), boundingRect.bottom() );
592 
593     return QwtInterval();
594 }
595 
596 
597