source: ntrip/branches/BNC_2.11.0/qwtpolar/qwt_polar_curve.cpp@ 10006

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