source: ntrip/trunk/BNC/qwt/qwt_plot_curve.cpp@ 8127

Last change on this file since 8127 was 8127, checked in by stoecker, 7 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 30.5 KB
Line 
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_plot_curve.h"
11#include "qwt_point_data.h"
12#include "qwt_math.h"
13#include "qwt_clipper.h"
14#include "qwt_painter.h"
15#include "qwt_scale_map.h"
16#include "qwt_plot.h"
17#include "qwt_curve_fitter.h"
18#include "qwt_symbol.h"
19#include "qwt_point_mapper.h"
20#include <qpainter.h>
21#include <qpixmap.h>
22#include <qalgorithms.h>
23#include <qmath.h>
24
25static void qwtUpdateLegendIconSize( QwtPlotCurve *curve )
26{
27 if ( curve->symbol() &&
28 curve->testLegendAttribute( QwtPlotCurve::LegendShowSymbol ) )
29 {
30 QSize sz = curve->symbol()->boundingRect().size();
31 sz += QSize( 2, 2 ); // margin
32
33 if ( curve->testLegendAttribute( QwtPlotCurve::LegendShowLine ) )
34 {
35 // Avoid, that the line is completely covered by the symbol
36
37 int w = qCeil( 1.5 * sz.width() );
38 if ( w % 2 )
39 w++;
40
41 sz.setWidth( qMax( 8, w ) );
42 }
43
44 curve->setLegendIconSize( sz );
45 }
46}
47
48static int qwtVerifyRange( int size, int &i1, int &i2 )
49{
50 if ( size < 1 )
51 return 0;
52
53 i1 = qBound( 0, i1, size - 1 );
54 i2 = qBound( 0, i2, size - 1 );
55
56 if ( i1 > i2 )
57 qSwap( i1, i2 );
58
59 return ( i2 - i1 + 1 );
60}
61
62class QwtPlotCurve::PrivateData
63{
64public:
65 PrivateData():
66 style( QwtPlotCurve::Lines ),
67 baseline( 0.0 ),
68 symbol( NULL ),
69 attributes( 0 ),
70 paintAttributes(
71 QwtPlotCurve::ClipPolygons | QwtPlotCurve::FilterPoints ),
72 legendAttributes( 0 )
73 {
74 pen = QPen( Qt::black );
75 curveFitter = new QwtSplineCurveFitter;
76 }
77
78 ~PrivateData()
79 {
80 delete symbol;
81 delete curveFitter;
82 }
83
84 QwtPlotCurve::CurveStyle style;
85 double baseline;
86
87 const QwtSymbol *symbol;
88 QwtCurveFitter *curveFitter;
89
90 QPen pen;
91 QBrush brush;
92
93 QwtPlotCurve::CurveAttributes attributes;
94 QwtPlotCurve::PaintAttributes paintAttributes;
95
96 QwtPlotCurve::LegendAttributes legendAttributes;
97};
98
99/*!
100 Constructor
101 \param title Title of the curve
102*/
103QwtPlotCurve::QwtPlotCurve( const QwtText &title ):
104 QwtPlotSeriesItem( title )
105{
106 init();
107}
108
109/*!
110 Constructor
111 \param title Title of the curve
112*/
113QwtPlotCurve::QwtPlotCurve( const QString &title ):
114 QwtPlotSeriesItem( QwtText( title ) )
115{
116 init();
117}
118
119//! Destructor
120QwtPlotCurve::~QwtPlotCurve()
121{
122 delete d_data;
123}
124
125//! Initialize internal members
126void QwtPlotCurve::init()
127{
128 setItemAttribute( QwtPlotItem::Legend );
129 setItemAttribute( QwtPlotItem::AutoScale );
130
131 d_data = new PrivateData;
132 setData( new QwtPointSeriesData() );
133
134 setZ( 20.0 );
135}
136
137//! \return QwtPlotItem::Rtti_PlotCurve
138int QwtPlotCurve::rtti() const
139{
140 return QwtPlotItem::Rtti_PlotCurve;
141}
142
143/*!
144 Specify an attribute how to draw the curve
145
146 \param attribute Paint attribute
147 \param on On/Off
148 \sa testPaintAttribute()
149*/
150void QwtPlotCurve::setPaintAttribute( PaintAttribute attribute, bool on )
151{
152 if ( on )
153 d_data->paintAttributes |= attribute;
154 else
155 d_data->paintAttributes &= ~attribute;
156}
157
158/*!
159 \return True, when attribute is enabled
160 \sa setPaintAttribute()
161*/
162bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const
163{
164 return ( d_data->paintAttributes & attribute );
165}
166
167/*!
168 Specify an attribute how to draw the legend icon
169
170 \param attribute Attribute
171 \param on On/Off
172 /sa testLegendAttribute(). legendIcon()
173*/
174void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on )
175{
176 if ( on != testLegendAttribute( attribute ) )
177 {
178 if ( on )
179 d_data->legendAttributes |= attribute;
180 else
181 d_data->legendAttributes &= ~attribute;
182
183 qwtUpdateLegendIconSize( this );
184 legendChanged();
185 }
186}
187
188/*!
189 \return True, when attribute is enabled
190 \sa setLegendAttribute()
191*/
192bool QwtPlotCurve::testLegendAttribute( LegendAttribute attribute ) const
193{
194 return ( d_data->legendAttributes & attribute );
195}
196
197/*!
198 Set the curve's drawing style
199
200 \param style Curve style
201 \sa style()
202*/
203void QwtPlotCurve::setStyle( CurveStyle style )
204{
205 if ( style != d_data->style )
206 {
207 d_data->style = style;
208
209 legendChanged();
210 itemChanged();
211 }
212}
213
214/*!
215 \return Style of the curve
216 \sa setStyle()
217*/
218QwtPlotCurve::CurveStyle QwtPlotCurve::style() const
219{
220 return d_data->style;
221}
222
223/*!
224 \brief Assign a symbol
225
226 The curve will take the ownership of the symbol, hence the previously
227 set symbol will be delete by setting a new one. If \p symbol is
228 \c NULL no symbol will be drawn.
229
230 \param symbol Symbol
231 \sa symbol()
232*/
233void QwtPlotCurve::setSymbol( QwtSymbol *symbol )
234{
235 if ( symbol != d_data->symbol )
236 {
237 delete d_data->symbol;
238 d_data->symbol = symbol;
239
240 qwtUpdateLegendIconSize( this );
241
242 legendChanged();
243 itemChanged();
244 }
245}
246
247/*!
248 \return Current symbol or NULL, when no symbol has been assigned
249 \sa setSymbol()
250*/
251const QwtSymbol *QwtPlotCurve::symbol() const
252{
253 return d_data->symbol;
254}
255
256/*!
257 Build and assign a pen
258
259 In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
260 non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
261 to hide this incompatibility.
262
263 \param color Pen color
264 \param width Pen width
265 \param style Pen style
266
267 \sa pen(), brush()
268 */
269void QwtPlotCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style )
270{
271 setPen( QPen( color, width, style ) );
272}
273
274/*!
275 Assign a pen
276
277 \param pen New pen
278 \sa pen(), brush()
279*/
280void QwtPlotCurve::setPen( const QPen &pen )
281{
282 if ( pen != d_data->pen )
283 {
284 d_data->pen = pen;
285
286 legendChanged();
287 itemChanged();
288 }
289}
290
291/*!
292 \return Pen used to draw the lines
293 \sa setPen(), brush()
294*/
295const QPen& QwtPlotCurve::pen() const
296{
297 return d_data->pen;
298}
299
300/*!
301 \brief Assign a brush.
302
303 In case of brush.style() != QBrush::NoBrush
304 and style() != QwtPlotCurve::Sticks
305 the area between the curve and the baseline will be filled.
306
307 In case !brush.color().isValid() the area will be filled by
308 pen.color(). The fill algorithm simply connects the first and the
309 last curve point to the baseline. So the curve data has to be sorted
310 (ascending or descending).
311
312 \param brush New brush
313 \sa brush(), setBaseline(), baseline()
314*/
315void QwtPlotCurve::setBrush( const QBrush &brush )
316{
317 if ( brush != d_data->brush )
318 {
319 d_data->brush = brush;
320
321 legendChanged();
322 itemChanged();
323 }
324}
325
326/*!
327 \return Brush used to fill the area between lines and the baseline
328 \sa setBrush(), setBaseline(), baseline()
329*/
330const QBrush& QwtPlotCurve::brush() const
331{
332 return d_data->brush;
333}
334
335/*!
336 Draw an interval of the curve
337
338 \param painter Painter
339 \param xMap Maps x-values into pixel coordinates.
340 \param yMap Maps y-values into pixel coordinates.
341 \param canvasRect Contents rectangle of the canvas
342 \param from Index of the first point to be painted
343 \param to Index of the last point to be painted. If to < 0 the
344 curve will be painted to its last point.
345
346 \sa drawCurve(), drawSymbols(),
347*/
348void QwtPlotCurve::drawSeries( QPainter *painter,
349 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
350 const QRectF &canvasRect, int from, int to ) const
351{
352 const size_t numSamples = dataSize();
353
354 if ( !painter || numSamples <= 0 )
355 return;
356
357 if ( to < 0 )
358 to = numSamples - 1;
359
360 if ( qwtVerifyRange( numSamples, from, to ) > 0 )
361 {
362 painter->save();
363 painter->setPen( d_data->pen );
364
365 /*
366 Qt 4.0.0 is slow when drawing lines, but it's even
367 slower when the painter has a brush. So we don't
368 set the brush before we really need it.
369 */
370
371 drawCurve( painter, d_data->style, xMap, yMap, canvasRect, from, to );
372 painter->restore();
373
374 if ( d_data->symbol &&
375 ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
376 {
377 painter->save();
378 drawSymbols( painter, *d_data->symbol,
379 xMap, yMap, canvasRect, from, to );
380 painter->restore();
381 }
382 }
383}
384
385/*!
386 \brief Draw the line part (without symbols) of a curve interval.
387 \param painter Painter
388 \param style curve style, see QwtPlotCurve::CurveStyle
389 \param xMap x map
390 \param yMap y map
391 \param canvasRect Contents rectangle of the canvas
392 \param from index of the first point to be painted
393 \param to index of the last point to be painted
394 \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks()
395*/
396void QwtPlotCurve::drawCurve( QPainter *painter, int style,
397 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
398 const QRectF &canvasRect, int from, int to ) const
399{
400 switch ( style )
401 {
402 case Lines:
403 if ( testCurveAttribute( Fitted ) )
404 {
405 // we always need the complete
406 // curve for fitting
407 from = 0;
408 to = dataSize() - 1;
409 }
410 drawLines( painter, xMap, yMap, canvasRect, from, to );
411 break;
412 case Sticks:
413 drawSticks( painter, xMap, yMap, canvasRect, from, to );
414 break;
415 case Steps:
416 drawSteps( painter, xMap, yMap, canvasRect, from, to );
417 break;
418 case Dots:
419 drawDots( painter, xMap, yMap, canvasRect, from, to );
420 break;
421 case NoCurve:
422 default:
423 break;
424 }
425}
426
427/*!
428 \brief Draw lines
429
430 If the CurveAttribute Fitted is enabled a QwtCurveFitter tries
431 to interpolate/smooth the curve, before it is painted.
432
433 \param painter Painter
434 \param xMap x map
435 \param yMap y map
436 \param canvasRect Contents rectangle of the canvas
437 \param from index of the first point to be painted
438 \param to index of the last point to be painted
439
440 \sa setCurveAttribute(), setCurveFitter(), draw(),
441 drawLines(), drawDots(), drawSteps(), drawSticks()
442*/
443void QwtPlotCurve::drawLines( QPainter *painter,
444 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
445 const QRectF &canvasRect, int from, int to ) const
446{
447 if ( from > to )
448 return;
449
450 const bool doAlign = QwtPainter::roundingAlignment( painter );
451 const bool doFit = ( d_data->attributes & Fitted ) && d_data->curveFitter;
452 const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
453 && ( d_data->brush.color().alpha() > 0 );
454
455 QRectF clipRect;
456 if ( d_data->paintAttributes & ClipPolygons )
457 {
458 qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
459 clipRect = canvasRect.adjusted(-pw, -pw, pw, pw);
460 }
461
462 bool doIntegers = false;
463
464#if QT_VERSION < 0x040800
465
466 // For Qt <= 4.7 the raster paint engine is significantly faster
467 // for rendering QPolygon than for QPolygonF. So let's
468 // see if we can use it.
469
470 if ( painter->paintEngine()->type() == QPaintEngine::Raster )
471 {
472 // In case of filling or fitting performance doesn't count
473 // because both operations are much more expensive
474 // then drawing the polyline itself
475
476 if ( !doFit && !doFill )
477 doIntegers = true;
478 }
479#endif
480
481 const bool noDuplicates = d_data->paintAttributes & FilterPoints;
482
483 QwtPointMapper mapper;
484 mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
485 mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates );
486 mapper.setBoundingRect( canvasRect );
487
488 if ( doIntegers )
489 {
490 QPolygon polyline = mapper.toPolygon(
491 xMap, yMap, data(), from, to );
492
493 if ( d_data->paintAttributes & ClipPolygons )
494 {
495 polyline = QwtClipper::clipPolygon(
496 clipRect.toAlignedRect(), polyline, false );
497 }
498
499 QwtPainter::drawPolyline( painter, polyline );
500 }
501 else
502 {
503 QPolygonF polyline = mapper.toPolygonF( xMap, yMap, data(), from, to );
504
505 if ( doFit )
506 polyline = d_data->curveFitter->fitCurve( polyline );
507
508 if ( doFill )
509 {
510 if ( painter->pen().style() != Qt::NoPen )
511 {
512 // here we are wasting memory for the filled copy,
513 // do polygon clipping twice etc .. TODO
514
515 QPolygonF filled = polyline;
516 fillCurve( painter, xMap, yMap, canvasRect, filled );
517 filled.clear();
518
519 if ( d_data->paintAttributes & ClipPolygons )
520 {
521 polyline = QwtClipper::clipPolygonF(
522 clipRect, polyline, false );
523 }
524
525 QwtPainter::drawPolyline( painter, polyline );
526 }
527 else
528 {
529 fillCurve( painter, xMap, yMap, canvasRect, polyline );
530 }
531 }
532 else
533 {
534 if ( d_data->paintAttributes & ClipPolygons )
535 {
536 polyline = QwtClipper::clipPolygonF(
537 clipRect, polyline, false );
538 }
539
540 QwtPainter::drawPolyline( painter, polyline );
541 }
542 }
543}
544
545/*!
546 Draw sticks
547
548 \param painter Painter
549 \param xMap x map
550 \param yMap y map
551 \param canvasRect Contents rectangle of the canvas
552 \param from index of the first point to be painted
553 \param to index of the last point to be painted
554
555 \sa draw(), drawCurve(), drawDots(), drawLines(), drawSteps()
556*/
557void QwtPlotCurve::drawSticks( QPainter *painter,
558 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
559 const QRectF &, int from, int to ) const
560{
561 painter->save();
562 painter->setRenderHint( QPainter::Antialiasing, false );
563
564 const bool doAlign = QwtPainter::roundingAlignment( painter );
565
566 double x0 = xMap.transform( d_data->baseline );
567 double y0 = yMap.transform( d_data->baseline );
568 if ( doAlign )
569 {
570 x0 = qRound( x0 );
571 y0 = qRound( y0 );
572 }
573
574 const Qt::Orientation o = orientation();
575
576 const QwtSeriesData<QPointF> *series = data();
577
578 for ( int i = from; i <= to; i++ )
579 {
580 const QPointF sample = series->sample( i );
581 double xi = xMap.transform( sample.x() );
582 double yi = yMap.transform( sample.y() );
583 if ( doAlign )
584 {
585 xi = qRound( xi );
586 yi = qRound( yi );
587 }
588
589 if ( o == Qt::Horizontal )
590 QwtPainter::drawLine( painter, x0, yi, xi, yi );
591 else
592 QwtPainter::drawLine( painter, xi, y0, xi, yi );
593 }
594
595 painter->restore();
596}
597
598/*!
599 Draw dots
600
601 \param painter Painter
602 \param xMap x map
603 \param yMap y map
604 \param canvasRect Contents rectangle of the canvas
605 \param from index of the first point to be painted
606 \param to index of the last point to be painted
607
608 \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps()
609*/
610void QwtPlotCurve::drawDots( QPainter *painter,
611 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
612 const QRectF &canvasRect, int from, int to ) const
613{
614 const QColor color = painter->pen().color();
615
616 if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 )
617 {
618 return;
619 }
620
621 const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
622 && ( d_data->brush.color().alpha() > 0 );
623 const bool doAlign = QwtPainter::roundingAlignment( painter );
624
625 QwtPointMapper mapper;
626 mapper.setBoundingRect( canvasRect );
627 mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
628
629 if ( d_data->paintAttributes & FilterPoints )
630 {
631 if ( ( color.alpha() == 255 )
632 && !( painter->renderHints() & QPainter::Antialiasing ) )
633 {
634 mapper.setFlag( QwtPointMapper::WeedOutPoints, true );
635 }
636 }
637
638 if ( doFill )
639 {
640 mapper.setFlag( QwtPointMapper::WeedOutPoints, false );
641
642 QPolygonF points = mapper.toPointsF(
643 xMap, yMap, data(), from, to );
644
645 QwtPainter::drawPoints( painter, points );
646 fillCurve( painter, xMap, yMap, canvasRect, points );
647 }
648 else if ( d_data->paintAttributes & ImageBuffer )
649 {
650 const QImage image = mapper.toImage( xMap, yMap,
651 data(), from, to, d_data->pen,
652 painter->testRenderHint( QPainter::Antialiasing ),
653 renderThreadCount() );
654
655 painter->drawImage( canvasRect.toAlignedRect(), image );
656 }
657 else if ( d_data->paintAttributes & MinimizeMemory )
658 {
659 const QwtSeriesData<QPointF> *series = data();
660
661 for ( int i = from; i <= to; i++ )
662 {
663 const QPointF sample = series->sample( i );
664
665 double xi = xMap.transform( sample.x() );
666 double yi = yMap.transform( sample.y() );
667
668 if ( doAlign )
669 {
670 xi = qRound( xi );
671 yi = qRound( yi );
672 }
673
674 QwtPainter::drawPoint( painter, QPointF( xi, yi ) );
675 }
676 }
677 else
678 {
679 if ( doAlign )
680 {
681 const QPolygon points = mapper.toPoints(
682 xMap, yMap, data(), from, to );
683
684 QwtPainter::drawPoints( painter, points );
685 }
686 else
687 {
688 const QPolygonF points = mapper.toPointsF(
689 xMap, yMap, data(), from, to );
690
691 QwtPainter::drawPoints( painter, points );
692 }
693 }
694}
695
696/*!
697 Draw step function
698
699 The direction of the steps depends on Inverted attribute.
700
701 \param painter Painter
702 \param xMap x map
703 \param yMap y map
704 \param canvasRect Contents rectangle of the canvas
705 \param from index of the first point to be painted
706 \param to index of the last point to be painted
707
708 \sa CurveAttribute, setCurveAttribute(),
709 draw(), drawCurve(), drawDots(), drawLines(), drawSticks()
710*/
711void QwtPlotCurve::drawSteps( QPainter *painter,
712 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
713 const QRectF &canvasRect, int from, int to ) const
714{
715 const bool doAlign = QwtPainter::roundingAlignment( painter );
716
717 QPolygonF polygon( 2 * ( to - from ) + 1 );
718 QPointF *points = polygon.data();
719
720 bool inverted = orientation() == Qt::Vertical;
721 if ( d_data->attributes & Inverted )
722 inverted = !inverted;
723
724 const QwtSeriesData<QPointF> *series = data();
725
726 int i, ip;
727 for ( i = from, ip = 0; i <= to; i++, ip += 2 )
728 {
729 const QPointF sample = series->sample( i );
730 double xi = xMap.transform( sample.x() );
731 double yi = yMap.transform( sample.y() );
732 if ( doAlign )
733 {
734 xi = qRound( xi );
735 yi = qRound( yi );
736 }
737
738 if ( ip > 0 )
739 {
740 const QPointF &p0 = points[ip - 2];
741 QPointF &p = points[ip - 1];
742
743 if ( inverted )
744 {
745 p.rx() = p0.x();
746 p.ry() = yi;
747 }
748 else
749 {
750 p.rx() = xi;
751 p.ry() = p0.y();
752 }
753 }
754
755 points[ip].rx() = xi;
756 points[ip].ry() = yi;
757 }
758
759 if ( d_data->paintAttributes & ClipPolygons )
760 {
761 qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
762 const QRectF clipRect = canvasRect.adjusted(-pw, -pw, pw, pw);
763
764 const QPolygonF clipped = QwtClipper::clipPolygonF(
765 clipRect, polygon, false );
766
767 QwtPainter::drawPolyline( painter, clipped );
768 }
769 else
770 {
771 QwtPainter::drawPolyline( painter, polygon );
772 }
773
774 if ( d_data->brush.style() != Qt::NoBrush )
775 fillCurve( painter, xMap, yMap, canvasRect, polygon );
776}
777
778
779/*!
780 Specify an attribute for drawing the curve
781
782 \param attribute Curve attribute
783 \param on On/Off
784
785 /sa testCurveAttribute(), setCurveFitter()
786*/
787void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on )
788{
789 if ( bool( d_data->attributes & attribute ) == on )
790 return;
791
792 if ( on )
793 d_data->attributes |= attribute;
794 else
795 d_data->attributes &= ~attribute;
796
797 itemChanged();
798}
799
800/*!
801 \return true, if attribute is enabled
802 \sa setCurveAttribute()
803*/
804bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const
805{
806 return d_data->attributes & attribute;
807}
808
809/*!
810 Assign a curve fitter
811
812 The curve fitter "smooths" the curve points, when the Fitted
813 CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting.
814
815 The curve fitter operates on the translated points ( = widget coordinates)
816 to be functional for logarithmic scales. Obviously this is less performant
817 for fitting algorithms, that reduce the number of points.
818
819 For situations, where curve fitting is used to improve the performance
820 of painting huge series of points it might be better to execute the fitter
821 on the curve points once and to cache the result in the QwtSeriesData object.
822
823 \param curveFitter() Curve fitter
824 \sa Fitted
825*/
826void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter )
827{
828 delete d_data->curveFitter;
829 d_data->curveFitter = curveFitter;
830
831 itemChanged();
832}
833
834/*!
835 Get the curve fitter. If curve fitting is disabled NULL is returned.
836
837 \return Curve fitter
838 \sa setCurveFitter(), Fitted
839*/
840QwtCurveFitter *QwtPlotCurve::curveFitter() const
841{
842 return d_data->curveFitter;
843}
844
845/*!
846 Fill the area between the curve and the baseline with
847 the curve brush
848
849 \param painter Painter
850 \param xMap x map
851 \param yMap y map
852 \param canvasRect Contents rectangle of the canvas
853 \param polygon Polygon - will be modified !
854
855 \sa setBrush(), setBaseline(), setStyle()
856*/
857void QwtPlotCurve::fillCurve( QPainter *painter,
858 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
859 const QRectF &canvasRect, QPolygonF &polygon ) const
860{
861 if ( d_data->brush.style() == Qt::NoBrush )
862 return;
863
864 closePolyline( painter, xMap, yMap, polygon );
865 if ( polygon.count() <= 2 ) // a line can't be filled
866 return;
867
868 QBrush brush = d_data->brush;
869 if ( !brush.color().isValid() )
870 brush.setColor( d_data->pen.color() );
871
872 if ( d_data->paintAttributes & ClipPolygons )
873 polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true );
874
875 painter->save();
876
877 painter->setPen( Qt::NoPen );
878 painter->setBrush( brush );
879
880 QwtPainter::drawPolygon( painter, polygon );
881
882 painter->restore();
883}
884
885/*!
886 \brief Complete a polygon to be a closed polygon including the
887 area between the original polygon and the baseline.
888
889 \param painter Painter
890 \param xMap X map
891 \param yMap Y map
892 \param polygon Polygon to be completed
893*/
894void QwtPlotCurve::closePolyline( QPainter *painter,
895 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
896 QPolygonF &polygon ) const
897{
898 if ( polygon.size() < 2 )
899 return;
900
901 const bool doAlign = QwtPainter::roundingAlignment( painter );
902
903 double baseline = d_data->baseline;
904
905 if ( orientation() == Qt::Vertical )
906 {
907 if ( yMap.transformation() )
908 baseline = yMap.transformation()->bounded( baseline );
909
910 double refY = yMap.transform( baseline );
911 if ( doAlign )
912 refY = qRound( refY );
913
914 polygon += QPointF( polygon.last().x(), refY );
915 polygon += QPointF( polygon.first().x(), refY );
916 }
917 else
918 {
919 if ( xMap.transformation() )
920 baseline = xMap.transformation()->bounded( baseline );
921
922 double refX = xMap.transform( baseline );
923 if ( doAlign )
924 refX = qRound( refX );
925
926 polygon += QPointF( refX, polygon.last().y() );
927 polygon += QPointF( refX, polygon.first().y() );
928 }
929}
930
931/*!
932 Draw symbols
933
934 \param painter Painter
935 \param symbol Curve symbol
936 \param xMap x map
937 \param yMap y map
938 \param canvasRect Contents rectangle of the canvas
939 \param from Index of the first point to be painted
940 \param to Index of the last point to be painted
941
942 \sa setSymbol(), drawSeries(), drawCurve()
943*/
944void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol,
945 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
946 const QRectF &canvasRect, int from, int to ) const
947{
948 QwtPointMapper mapper;
949 mapper.setFlag( QwtPointMapper::RoundPoints,
950 QwtPainter::roundingAlignment( painter ) );
951 mapper.setFlag( QwtPointMapper::WeedOutPoints,
952 testPaintAttribute( QwtPlotCurve::FilterPoints ) );
953 mapper.setBoundingRect( canvasRect );
954
955 const int chunkSize = 500;
956
957 for ( int i = from; i <= to; i += chunkSize )
958 {
959 const int n = qMin( chunkSize, to - i + 1 );
960
961 const QPolygonF points = mapper.toPointsF( xMap, yMap,
962 data(), i, i + n - 1 );
963
964 if ( points.size() > 0 )
965 symbol.drawSymbols( painter, points );
966 }
967}
968
969/*!
970 \brief Set the value of the baseline
971
972 The baseline is needed for filling the curve with a brush or
973 the Sticks drawing style.
974
975 The interpretation of the baseline depends on the orientation().
976 With Qt::Horizontal, the baseline is interpreted as a horizontal line
977 at y = baseline(), with Qt::Vertical, it is interpreted as a vertical
978 line at x = baseline().
979
980 The default value is 0.0.
981
982 \param value Value of the baseline
983 \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation()
984*/
985void QwtPlotCurve::setBaseline( double value )
986{
987 if ( d_data->baseline != value )
988 {
989 d_data->baseline = value;
990 itemChanged();
991 }
992}
993
994/*!
995 \return Value of the baseline
996 \sa setBaseline()
997*/
998double QwtPlotCurve::baseline() const
999{
1000 return d_data->baseline;
1001}
1002
1003/*!
1004 Find the closest curve point for a specific position
1005
1006 \param pos Position, where to look for the closest curve point
1007 \param dist If dist != NULL, closestPoint() returns the distance between
1008 the position and the closest curve point
1009 \return Index of the closest curve point, or -1 if none can be found
1010 ( f.e when the curve has no points )
1011 \note closestPoint() implements a dumb algorithm, that iterates
1012 over all points
1013*/
1014int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const
1015{
1016 const size_t numSamples = dataSize();
1017
1018 if ( plot() == NULL || numSamples <= 0 )
1019 return -1;
1020
1021 const QwtSeriesData<QPointF> *series = data();
1022
1023 const QwtScaleMap xMap = plot()->canvasMap( xAxis() );
1024 const QwtScaleMap yMap = plot()->canvasMap( yAxis() );
1025
1026 int index = -1;
1027 double dmin = 1.0e10;
1028
1029 for ( uint i = 0; i < numSamples; i++ )
1030 {
1031 const QPointF sample = series->sample( i );
1032
1033 const double cx = xMap.transform( sample.x() ) - pos.x();
1034 const double cy = yMap.transform( sample.y() ) - pos.y();
1035
1036 const double f = qwtSqr( cx ) + qwtSqr( cy );
1037 if ( f < dmin )
1038 {
1039 index = i;
1040 dmin = f;
1041 }
1042 }
1043 if ( dist )
1044 *dist = qSqrt( dmin );
1045
1046 return index;
1047}
1048
1049/*!
1050 \return Icon representing the curve on the legend
1051
1052 \param index Index of the legend entry
1053 ( ignored as there is only one )
1054 \param size Icon size
1055
1056 \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
1057 */
1058QwtGraphic QwtPlotCurve::legendIcon( int index,
1059 const QSizeF &size ) const
1060{
1061 Q_UNUSED( index );
1062
1063 if ( size.isEmpty() )
1064 return QwtGraphic();
1065
1066 QwtGraphic graphic;
1067 graphic.setDefaultSize( size );
1068 graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
1069
1070 QPainter painter( &graphic );
1071 painter.setRenderHint( QPainter::Antialiasing,
1072 testRenderHint( QwtPlotItem::RenderAntialiased ) );
1073
1074 if ( d_data->legendAttributes == 0 ||
1075 d_data->legendAttributes & QwtPlotCurve::LegendShowBrush )
1076 {
1077 QBrush brush = d_data->brush;
1078
1079 if ( brush.style() == Qt::NoBrush &&
1080 d_data->legendAttributes == 0 )
1081 {
1082 if ( style() != QwtPlotCurve::NoCurve )
1083 {
1084 brush = QBrush( pen().color() );
1085 }
1086 else if ( d_data->symbol &&
1087 ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
1088 {
1089 brush = QBrush( d_data->symbol->pen().color() );
1090 }
1091 }
1092
1093 if ( brush.style() != Qt::NoBrush )
1094 {
1095 QRectF r( 0, 0, size.width(), size.height() );
1096 painter.fillRect( r, brush );
1097 }
1098 }
1099
1100 if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine )
1101 {
1102 if ( pen() != Qt::NoPen )
1103 {
1104 QPen pn = pen();
1105 pn.setCapStyle( Qt::FlatCap );
1106
1107 painter.setPen( pn );
1108
1109 const double y = 0.5 * size.height();
1110 QwtPainter::drawLine( &painter, 0.0, y, size.width(), y );
1111 }
1112 }
1113
1114 if ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol )
1115 {
1116 if ( d_data->symbol )
1117 {
1118 QRectF r( 0, 0, size.width(), size.height() );
1119 d_data->symbol->drawSymbol( &painter, r );
1120 }
1121 }
1122
1123 return graphic;
1124}
1125
1126/*!
1127 Initialize data with an array of points.
1128
1129 \param samples Vector of points
1130 \note QVector is implicitly shared
1131 \note QPolygonF is derived from QVector<QPointF>
1132*/
1133void QwtPlotCurve::setSamples( const QVector<QPointF> &samples )
1134{
1135 setData( new QwtPointSeriesData( samples ) );
1136}
1137
1138/*!
1139 Assign a series of points
1140
1141 setSamples() is just a wrapper for setData() without any additional
1142 value - beside that it is easier to find for the developer.
1143
1144 \param data Data
1145 \warning The item takes ownership of the data object, deleting
1146 it when its not used anymore.
1147*/
1148void QwtPlotCurve::setSamples( QwtSeriesData<QPointF> *data )
1149{
1150 setData( data );
1151}
1152
1153#ifndef QWT_NO_COMPAT
1154
1155/*!
1156 \brief Initialize the data by pointing to memory blocks which
1157 are not managed by QwtPlotCurve.
1158
1159 setRawSamples is provided for efficiency.
1160 It is important to keep the pointers
1161 during the lifetime of the underlying QwtCPointerData class.
1162
1163 \param xData pointer to x data
1164 \param yData pointer to y data
1165 \param size size of x and y
1166
1167 \sa QwtCPointerData
1168*/
1169void QwtPlotCurve::setRawSamples(
1170 const double *xData, const double *yData, int size )
1171{
1172 setData( new QwtCPointerData( xData, yData, size ) );
1173}
1174
1175/*!
1176 Set data by copying x- and y-values from specified memory blocks.
1177 Contrary to setRawSamples(), this function makes a 'deep copy' of
1178 the data.
1179
1180 \param xData pointer to x values
1181 \param yData pointer to y values
1182 \param size size of xData and yData
1183
1184 \sa QwtPointArrayData
1185*/
1186void QwtPlotCurve::setSamples(
1187 const double *xData, const double *yData, int size )
1188{
1189 setData( new QwtPointArrayData( xData, yData, size ) );
1190}
1191
1192/*!
1193 \brief Initialize data with x- and y-arrays (explicitly shared)
1194
1195 \param xData x data
1196 \param yData y data
1197
1198 \sa QwtPointArrayData
1199*/
1200void QwtPlotCurve::setSamples( const QVector<double> &xData,
1201 const QVector<double> &yData )
1202{
1203 setData( new QwtPointArrayData( xData, yData ) );
1204}
1205
1206#endif // !QWT_NO_COMPAT
1207
Note: See TracBrowser for help on using the repository browser.