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

Last change on this file since 9742 was 9383, checked in by stoecker, 4 years ago

update to qwt verion 6.1.1 to fix build with newer Qt5

File size: 30.5 KB
RevLine 
[4271]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"
[8127]11#include "qwt_point_data.h"
[4271]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"
[8127]19#include "qwt_point_mapper.h"
[4271]20#include <qpainter.h>
21#include <qpixmap.h>
22#include <qalgorithms.h>
23#include <qmath.h>
24
[8127]25static void qwtUpdateLegendIconSize( QwtPlotCurve *curve )
[4271]26{
[9383]27 if ( curve->symbol() &&
[8127]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{
[4271]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 ),
[9383]70 paintAttributes(
[8127]71 QwtPlotCurve::ClipPolygons | QwtPlotCurve::FilterPoints ),
[4271]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 ):
[8127]104 QwtPlotSeriesItem( title )
[4271]105{
106 init();
107}
108
109/*!
110 Constructor
111 \param title Title of the curve
112*/
113QwtPlotCurve::QwtPlotCurve( const QString &title ):
[8127]114 QwtPlotSeriesItem( QwtText( title ) )
[4271]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;
[8127]132 setData( new QwtPointSeriesData() );
[4271]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/*!
[8127]159 \return True, when attribute is enabled
[4271]160 \sa setPaintAttribute()
161*/
162bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const
163{
164 return ( d_data->paintAttributes & attribute );
165}
166
167/*!
[8127]168 Specify an attribute how to draw the legend icon
[4271]169
170 \param attribute Attribute
171 \param on On/Off
[8127]172 /sa testLegendAttribute(). legendIcon()
[4271]173*/
174void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on )
175{
[8127]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 }
[4271]186}
187
188/*!
[8127]189 \return True, when attribute is enabled
190 \sa setLegendAttribute()
[4271]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;
[8127]208
209 legendChanged();
[4271]210 itemChanged();
211 }
212}
213
214/*!
[8127]215 \return Style of the curve
216 \sa setStyle()
[4271]217*/
218QwtPlotCurve::CurveStyle QwtPlotCurve::style() const
219{
220 return d_data->style;
221}
222
223/*!
[8127]224 \brief Assign a symbol
[4271]225
[8127]226 The curve will take the ownership of the symbol, hence the previously
[9383]227 set symbol will be delete by setting a new one. If \p symbol is
[8127]228 \c NULL no symbol will be drawn.
229
[4271]230 \param symbol Symbol
231 \sa symbol()
232*/
[8127]233void QwtPlotCurve::setSymbol( QwtSymbol *symbol )
[4271]234{
235 if ( symbol != d_data->symbol )
236 {
237 delete d_data->symbol;
238 d_data->symbol = symbol;
[8127]239
240 qwtUpdateLegendIconSize( this );
241
242 legendChanged();
[4271]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/*!
[8127]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/*!
[4271]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;
[8127]285
286 legendChanged();
[4271]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;
[8127]320
321 legendChanged();
[4271]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.
[8127]341 \param canvasRect Contents rectangle of the canvas
[4271]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{
[8127]352 const size_t numSamples = dataSize();
353
354 if ( !painter || numSamples <= 0 )
[4271]355 return;
356
357 if ( to < 0 )
[8127]358 to = numSamples - 1;
[4271]359
[8127]360 if ( qwtVerifyRange( numSamples, from, to ) > 0 )
[4271]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
[8127]391 \param canvasRect Contents rectangle of the canvas
[4271]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
[8127]436 \param canvasRect Contents rectangle of the canvas
[4271]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{
[8127]447 if ( from > to )
[4271]448 return;
449
450 const bool doAlign = QwtPainter::roundingAlignment( painter );
[8127]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 );
[4271]454
[8127]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 }
[4271]461
[8127]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 )
[4271]471 {
[8127]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
[4271]475
[8127]476 if ( !doFit && !doFill )
[9383]477 doIntegers = true;
[4271]478 }
[8127]479#endif
[4271]480
[8127]481 const bool noDuplicates = d_data->paintAttributes & FilterPoints;
[4271]482
[8127]483 QwtPointMapper mapper;
484 mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
485 mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates );
486 mapper.setBoundingRect( canvasRect );
487
488 if ( doIntegers )
[4271]489 {
[9383]490 QPolygon polyline = mapper.toPolygon(
[8127]491 xMap, yMap, data(), from, to );
[4271]492
[8127]493 if ( d_data->paintAttributes & ClipPolygons )
494 {
[9383]495 polyline = QwtClipper::clipPolygon(
[8127]496 clipRect.toAlignedRect(), polyline, false );
497 }
498
499 QwtPainter::drawPolyline( painter, polyline );
[4271]500 }
501 else
502 {
[8127]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 {
[9383]521 polyline = QwtClipper::clipPolygonF(
[8127]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 }
[4271]542 }
543}
544
545/*!
546 Draw sticks
547
548 \param painter Painter
549 \param xMap x map
550 \param yMap y map
[8127]551 \param canvasRect Contents rectangle of the canvas
[4271]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,
[9383]559 const QRectF &canvasRect, int from, int to ) const
[4271]560{
[9383]561 Q_UNUSED( canvasRect )
562
[4271]563 painter->save();
564 painter->setRenderHint( QPainter::Antialiasing, false );
565
566 const bool doAlign = QwtPainter::roundingAlignment( painter );
567
568 double x0 = xMap.transform( d_data->baseline );
569 double y0 = yMap.transform( d_data->baseline );
570 if ( doAlign )
571 {
572 x0 = qRound( x0 );
573 y0 = qRound( y0 );
574 }
575
576 const Qt::Orientation o = orientation();
577
[8127]578 const QwtSeriesData<QPointF> *series = data();
579
[4271]580 for ( int i = from; i <= to; i++ )
581 {
[8127]582 const QPointF sample = series->sample( i );
[4271]583 double xi = xMap.transform( sample.x() );
584 double yi = yMap.transform( sample.y() );
585 if ( doAlign )
586 {
587 xi = qRound( xi );
588 yi = qRound( yi );
589 }
590
591 if ( o == Qt::Horizontal )
592 QwtPainter::drawLine( painter, x0, yi, xi, yi );
593 else
594 QwtPainter::drawLine( painter, xi, y0, xi, yi );
595 }
596
597 painter->restore();
598}
599
600/*!
601 Draw dots
602
603 \param painter Painter
604 \param xMap x map
605 \param yMap y map
[8127]606 \param canvasRect Contents rectangle of the canvas
[4271]607 \param from index of the first point to be painted
608 \param to index of the last point to be painted
609
610 \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps()
611*/
612void QwtPlotCurve::drawDots( QPainter *painter,
613 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
614 const QRectF &canvasRect, int from, int to ) const
615{
[8127]616 const QColor color = painter->pen().color();
617
618 if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 )
619 {
620 return;
621 }
622
623 const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
624 && ( d_data->brush.color().alpha() > 0 );
[4271]625 const bool doAlign = QwtPainter::roundingAlignment( painter );
626
[8127]627 QwtPointMapper mapper;
628 mapper.setBoundingRect( canvasRect );
629 mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
630
631 if ( d_data->paintAttributes & FilterPoints )
632 {
633 if ( ( color.alpha() == 255 )
634 && !( painter->renderHints() & QPainter::Antialiasing ) )
635 {
636 mapper.setFlag( QwtPointMapper::WeedOutPoints, true );
637 }
638 }
639
[4271]640 if ( doFill )
[8127]641 {
642 mapper.setFlag( QwtPointMapper::WeedOutPoints, false );
[4271]643
[9383]644 QPolygonF points = mapper.toPointsF(
[8127]645 xMap, yMap, data(), from, to );
[4271]646
[8127]647 QwtPainter::drawPoints( painter, points );
648 fillCurve( painter, xMap, yMap, canvasRect, points );
649 }
650 else if ( d_data->paintAttributes & ImageBuffer )
[4271]651 {
[8127]652 const QImage image = mapper.toImage( xMap, yMap,
[9383]653 data(), from, to, d_data->pen,
[8127]654 painter->testRenderHint( QPainter::Antialiasing ),
655 renderThreadCount() );
656
657 painter->drawImage( canvasRect.toAlignedRect(), image );
658 }
659 else if ( d_data->paintAttributes & MinimizeMemory )
660 {
661 const QwtSeriesData<QPointF> *series = data();
662
663 for ( int i = from; i <= to; i++ )
[4271]664 {
[8127]665 const QPointF sample = series->sample( i );
[4271]666
[8127]667 double xi = xMap.transform( sample.x() );
668 double yi = yMap.transform( sample.y() );
[4271]669
[8127]670 if ( doAlign )
671 {
672 xi = qRound( xi );
673 yi = qRound( yi );
674 }
675
676 QwtPainter::drawPoint( painter, QPointF( xi, yi ) );
677 }
678 }
679 else
680 {
681 if ( doAlign )
[4271]682 {
[8127]683 const QPolygon points = mapper.toPoints(
[9383]684 xMap, yMap, data(), from, to );
[8127]685
686 QwtPainter::drawPoints( painter, points );
[4271]687 }
[8127]688 else
689 {
[9383]690 const QPolygonF points = mapper.toPointsF(
[8127]691 xMap, yMap, data(), from, to );
692
693 QwtPainter::drawPoints( painter, points );
694 }
[4271]695 }
696}
697
698/*!
699 Draw step function
700
701 The direction of the steps depends on Inverted attribute.
702
703 \param painter Painter
704 \param xMap x map
705 \param yMap y map
[8127]706 \param canvasRect Contents rectangle of the canvas
[4271]707 \param from index of the first point to be painted
708 \param to index of the last point to be painted
709
710 \sa CurveAttribute, setCurveAttribute(),
711 draw(), drawCurve(), drawDots(), drawLines(), drawSticks()
712*/
713void QwtPlotCurve::drawSteps( QPainter *painter,
714 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
715 const QRectF &canvasRect, int from, int to ) const
716{
717 const bool doAlign = QwtPainter::roundingAlignment( painter );
718
719 QPolygonF polygon( 2 * ( to - from ) + 1 );
720 QPointF *points = polygon.data();
721
722 bool inverted = orientation() == Qt::Vertical;
723 if ( d_data->attributes & Inverted )
724 inverted = !inverted;
725
[8127]726 const QwtSeriesData<QPointF> *series = data();
727
[4271]728 int i, ip;
729 for ( i = from, ip = 0; i <= to; i++, ip += 2 )
730 {
[8127]731 const QPointF sample = series->sample( i );
[4271]732 double xi = xMap.transform( sample.x() );
733 double yi = yMap.transform( sample.y() );
734 if ( doAlign )
735 {
736 xi = qRound( xi );
737 yi = qRound( yi );
738 }
739
740 if ( ip > 0 )
741 {
742 const QPointF &p0 = points[ip - 2];
743 QPointF &p = points[ip - 1];
744
745 if ( inverted )
746 {
747 p.rx() = p0.x();
748 p.ry() = yi;
749 }
750 else
751 {
752 p.rx() = xi;
753 p.ry() = p0.y();
754 }
755 }
756
757 points[ip].rx() = xi;
758 points[ip].ry() = yi;
759 }
760
761 if ( d_data->paintAttributes & ClipPolygons )
762 {
[8127]763 qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
764 const QRectF clipRect = canvasRect.adjusted(-pw, -pw, pw, pw);
[9383]765
766 const QPolygonF clipped = QwtClipper::clipPolygonF(
[8127]767 clipRect, polygon, false );
[4271]768
769 QwtPainter::drawPolyline( painter, clipped );
770 }
771 else
772 {
773 QwtPainter::drawPolyline( painter, polygon );
774 }
775
776 if ( d_data->brush.style() != Qt::NoBrush )
777 fillCurve( painter, xMap, yMap, canvasRect, polygon );
778}
779
780
781/*!
782 Specify an attribute for drawing the curve
783
784 \param attribute Curve attribute
785 \param on On/Off
786
787 /sa testCurveAttribute(), setCurveFitter()
788*/
789void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on )
790{
791 if ( bool( d_data->attributes & attribute ) == on )
792 return;
793
794 if ( on )
795 d_data->attributes |= attribute;
796 else
797 d_data->attributes &= ~attribute;
798
799 itemChanged();
800}
801
802/*!
803 \return true, if attribute is enabled
804 \sa setCurveAttribute()
805*/
806bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const
807{
808 return d_data->attributes & attribute;
809}
810
811/*!
812 Assign a curve fitter
813
814 The curve fitter "smooths" the curve points, when the Fitted
815 CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting.
816
817 The curve fitter operates on the translated points ( = widget coordinates)
818 to be functional for logarithmic scales. Obviously this is less performant
819 for fitting algorithms, that reduce the number of points.
820
821 For situations, where curve fitting is used to improve the performance
822 of painting huge series of points it might be better to execute the fitter
823 on the curve points once and to cache the result in the QwtSeriesData object.
824
825 \param curveFitter() Curve fitter
826 \sa Fitted
827*/
828void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter )
829{
830 delete d_data->curveFitter;
831 d_data->curveFitter = curveFitter;
832
833 itemChanged();
834}
835
836/*!
837 Get the curve fitter. If curve fitting is disabled NULL is returned.
838
839 \return Curve fitter
840 \sa setCurveFitter(), Fitted
841*/
842QwtCurveFitter *QwtPlotCurve::curveFitter() const
843{
844 return d_data->curveFitter;
845}
846
847/*!
848 Fill the area between the curve and the baseline with
849 the curve brush
850
851 \param painter Painter
852 \param xMap x map
853 \param yMap y map
[8127]854 \param canvasRect Contents rectangle of the canvas
[4271]855 \param polygon Polygon - will be modified !
856
857 \sa setBrush(), setBaseline(), setStyle()
858*/
859void QwtPlotCurve::fillCurve( QPainter *painter,
860 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
861 const QRectF &canvasRect, QPolygonF &polygon ) const
862{
863 if ( d_data->brush.style() == Qt::NoBrush )
864 return;
865
866 closePolyline( painter, xMap, yMap, polygon );
867 if ( polygon.count() <= 2 ) // a line can't be filled
868 return;
869
870 QBrush brush = d_data->brush;
871 if ( !brush.color().isValid() )
872 brush.setColor( d_data->pen.color() );
873
874 if ( d_data->paintAttributes & ClipPolygons )
875 polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true );
876
877 painter->save();
878
879 painter->setPen( Qt::NoPen );
880 painter->setBrush( brush );
881
882 QwtPainter::drawPolygon( painter, polygon );
883
884 painter->restore();
885}
886
887/*!
[9383]888 \brief Complete a polygon to be a closed polygon including the
[4271]889 area between the original polygon and the baseline.
890
891 \param painter Painter
892 \param xMap X map
893 \param yMap Y map
894 \param polygon Polygon to be completed
895*/
896void QwtPlotCurve::closePolyline( QPainter *painter,
897 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
898 QPolygonF &polygon ) const
899{
900 if ( polygon.size() < 2 )
901 return;
902
903 const bool doAlign = QwtPainter::roundingAlignment( painter );
904
905 double baseline = d_data->baseline;
[9383]906
[4271]907 if ( orientation() == Qt::Vertical )
908 {
[8127]909 if ( yMap.transformation() )
910 baseline = yMap.transformation()->bounded( baseline );
[4271]911
912 double refY = yMap.transform( baseline );
913 if ( doAlign )
914 refY = qRound( refY );
915
916 polygon += QPointF( polygon.last().x(), refY );
917 polygon += QPointF( polygon.first().x(), refY );
918 }
919 else
920 {
[8127]921 if ( xMap.transformation() )
922 baseline = xMap.transformation()->bounded( baseline );
[4271]923
924 double refX = xMap.transform( baseline );
925 if ( doAlign )
926 refX = qRound( refX );
927
928 polygon += QPointF( refX, polygon.last().y() );
929 polygon += QPointF( refX, polygon.first().y() );
930 }
931}
932
933/*!
934 Draw symbols
935
936 \param painter Painter
937 \param symbol Curve symbol
938 \param xMap x map
939 \param yMap y map
[8127]940 \param canvasRect Contents rectangle of the canvas
[4271]941 \param from Index of the first point to be painted
942 \param to Index of the last point to be painted
943
944 \sa setSymbol(), drawSeries(), drawCurve()
945*/
946void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol,
947 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
948 const QRectF &canvasRect, int from, int to ) const
949{
[8127]950 QwtPointMapper mapper;
[9383]951 mapper.setFlag( QwtPointMapper::RoundPoints,
[8127]952 QwtPainter::roundingAlignment( painter ) );
[9383]953 mapper.setFlag( QwtPointMapper::WeedOutPoints,
[8127]954 testPaintAttribute( QwtPlotCurve::FilterPoints ) );
955 mapper.setBoundingRect( canvasRect );
[4271]956
[8127]957 const int chunkSize = 500;
[4271]958
[8127]959 for ( int i = from; i <= to; i += chunkSize )
[4271]960 {
[8127]961 const int n = qMin( chunkSize, to - i + 1 );
[4271]962
[8127]963 const QPolygonF points = mapper.toPointsF( xMap, yMap,
964 data(), i, i + n - 1 );
[4271]965
[8127]966 if ( points.size() > 0 )
967 symbol.drawSymbols( painter, points );
[4271]968 }
969}
970
971/*!
972 \brief Set the value of the baseline
973
974 The baseline is needed for filling the curve with a brush or
975 the Sticks drawing style.
[8127]976
977 The interpretation of the baseline depends on the orientation().
[9383]978 With Qt::Vertical, the baseline is interpreted as a horizontal line
979 at y = baseline(), with Qt::Horizontal, it is interpreted as a vertical
[4271]980 line at x = baseline().
981
982 The default value is 0.0.
983
984 \param value Value of the baseline
[8127]985 \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation()
[4271]986*/
987void QwtPlotCurve::setBaseline( double value )
988{
989 if ( d_data->baseline != value )
990 {
991 d_data->baseline = value;
992 itemChanged();
993 }
994}
995
996/*!
997 \return Value of the baseline
998 \sa setBaseline()
999*/
1000double QwtPlotCurve::baseline() const
1001{
1002 return d_data->baseline;
1003}
1004
1005/*!
1006 Find the closest curve point for a specific position
1007
1008 \param pos Position, where to look for the closest curve point
1009 \param dist If dist != NULL, closestPoint() returns the distance between
[8127]1010 the position and the closest curve point
[4271]1011 \return Index of the closest curve point, or -1 if none can be found
1012 ( f.e when the curve has no points )
1013 \note closestPoint() implements a dumb algorithm, that iterates
1014 over all points
1015*/
1016int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const
1017{
[8127]1018 const size_t numSamples = dataSize();
1019
1020 if ( plot() == NULL || numSamples <= 0 )
[4271]1021 return -1;
1022
[8127]1023 const QwtSeriesData<QPointF> *series = data();
1024
[4271]1025 const QwtScaleMap xMap = plot()->canvasMap( xAxis() );
1026 const QwtScaleMap yMap = plot()->canvasMap( yAxis() );
1027
1028 int index = -1;
1029 double dmin = 1.0e10;
1030
[8127]1031 for ( uint i = 0; i < numSamples; i++ )
[4271]1032 {
[8127]1033 const QPointF sample = series->sample( i );
[4271]1034
1035 const double cx = xMap.transform( sample.x() ) - pos.x();
1036 const double cy = yMap.transform( sample.y() ) - pos.y();
1037
1038 const double f = qwtSqr( cx ) + qwtSqr( cy );
1039 if ( f < dmin )
1040 {
1041 index = i;
1042 dmin = f;
1043 }
1044 }
1045 if ( dist )
1046 *dist = qSqrt( dmin );
1047
1048 return index;
1049}
1050
1051/*!
[8127]1052 \return Icon representing the curve on the legend
[4271]1053
[9383]1054 \param index Index of the legend entry
[8127]1055 ( ignored as there is only one )
1056 \param size Icon size
[4271]1057
[8127]1058 \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
1059 */
[9383]1060QwtGraphic QwtPlotCurve::legendIcon( int index,
[8127]1061 const QSizeF &size ) const
[4271]1062{
[8127]1063 Q_UNUSED( index );
[4271]1064
[8127]1065 if ( size.isEmpty() )
1066 return QwtGraphic();
[4271]1067
[8127]1068 QwtGraphic graphic;
1069 graphic.setDefaultSize( size );
1070 graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
[4271]1071
[8127]1072 QPainter painter( &graphic );
1073 painter.setRenderHint( QPainter::Antialiasing,
1074 testRenderHint( QwtPlotItem::RenderAntialiased ) );
[4271]1075
[8127]1076 if ( d_data->legendAttributes == 0 ||
1077 d_data->legendAttributes & QwtPlotCurve::LegendShowBrush )
[4271]1078 {
1079 QBrush brush = d_data->brush;
[8127]1080
1081 if ( brush.style() == Qt::NoBrush &&
1082 d_data->legendAttributes == 0 )
[4271]1083 {
1084 if ( style() != QwtPlotCurve::NoCurve )
[8127]1085 {
[4271]1086 brush = QBrush( pen().color() );
[8127]1087 }
[4271]1088 else if ( d_data->symbol &&
1089 ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
1090 {
1091 brush = QBrush( d_data->symbol->pen().color() );
1092 }
1093 }
[8127]1094
[4271]1095 if ( brush.style() != Qt::NoBrush )
[8127]1096 {
1097 QRectF r( 0, 0, size.width(), size.height() );
1098 painter.fillRect( r, brush );
1099 }
[4271]1100 }
[8127]1101
[4271]1102 if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine )
1103 {
1104 if ( pen() != Qt::NoPen )
1105 {
[8127]1106 QPen pn = pen();
1107 pn.setCapStyle( Qt::FlatCap );
1108
1109 painter.setPen( pn );
1110
1111 const double y = 0.5 * size.height();
1112 QwtPainter::drawLine( &painter, 0.0, y, size.width(), y );
[4271]1113 }
1114 }
[8127]1115
[4271]1116 if ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol )
1117 {
[8127]1118 if ( d_data->symbol )
[4271]1119 {
[8127]1120 QRectF r( 0, 0, size.width(), size.height() );
1121 d_data->symbol->drawSymbol( &painter, r );
[4271]1122 }
1123 }
[8127]1124
1125 return graphic;
[4271]1126}
1127
1128/*!
[8127]1129 Initialize data with an array of points.
[4271]1130
1131 \param samples Vector of points
[8127]1132 \note QVector is implicitly shared
1133 \note QPolygonF is derived from QVector<QPointF>
[4271]1134*/
1135void QwtPlotCurve::setSamples( const QVector<QPointF> &samples )
1136{
[8127]1137 setData( new QwtPointSeriesData( samples ) );
[4271]1138}
1139
[8127]1140/*!
1141 Assign a series of points
1142
1143 setSamples() is just a wrapper for setData() without any additional
1144 value - beside that it is easier to find for the developer.
1145
1146 \param data Data
1147 \warning The item takes ownership of the data object, deleting
1148 it when its not used anymore.
1149*/
1150void QwtPlotCurve::setSamples( QwtSeriesData<QPointF> *data )
1151{
1152 setData( data );
1153}
1154
[4271]1155#ifndef QWT_NO_COMPAT
1156
1157/*!
[9383]1158 \brief Initialize the data by pointing to memory blocks which
[4271]1159 are not managed by QwtPlotCurve.
1160
[9383]1161 setRawSamples is provided for efficiency.
[4271]1162 It is important to keep the pointers
1163 during the lifetime of the underlying QwtCPointerData class.
1164
1165 \param xData pointer to x data
1166 \param yData pointer to y data
1167 \param size size of x and y
1168
1169 \sa QwtCPointerData
1170*/
[9383]1171void QwtPlotCurve::setRawSamples(
[4271]1172 const double *xData, const double *yData, int size )
1173{
[8127]1174 setData( new QwtCPointerData( xData, yData, size ) );
[4271]1175}
1176
1177/*!
1178 Set data by copying x- and y-values from specified memory blocks.
1179 Contrary to setRawSamples(), this function makes a 'deep copy' of
1180 the data.
1181
1182 \param xData pointer to x values
1183 \param yData pointer to y values
1184 \param size size of xData and yData
1185
1186 \sa QwtPointArrayData
1187*/
[9383]1188void QwtPlotCurve::setSamples(
[4271]1189 const double *xData, const double *yData, int size )
1190{
[8127]1191 setData( new QwtPointArrayData( xData, yData, size ) );
[4271]1192}
1193
1194/*!
1195 \brief Initialize data with x- and y-arrays (explicitly shared)
1196
1197 \param xData x data
1198 \param yData y data
1199
1200 \sa QwtPointArrayData
1201*/
1202void QwtPlotCurve::setSamples( const QVector<double> &xData,
1203 const QVector<double> &yData )
1204{
[8127]1205 setData( new QwtPointArrayData( xData, yData ) );
[4271]1206}
[8127]1207
[4271]1208#endif // !QWT_NO_COMPAT
1209
Note: See TracBrowser for help on using the repository browser.