source: ntrip/trunk/BNC/qwt/qwt_plot_intervalcurve.cpp@ 8713

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

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 15.1 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_intervalcurve.h"
11#include "qwt_interval_symbol.h"
12#include "qwt_scale_map.h"
13#include "qwt_clipper.h"
14#include "qwt_painter.h"
[8127]15#include <string.h>
[4271]16
17#include <qpainter.h>
18
19static inline bool qwtIsHSampleInside( const QwtIntervalSample &sample,
20 double xMin, double xMax, double yMin, double yMax )
21{
22 const double y = sample.value;
23 const double x1 = sample.interval.minValue();
24 const double x2 = sample.interval.maxValue();
25
26 const bool isOffScreen = ( y < yMin ) || ( y > yMax )
[8127]27 || ( x1 < xMin && x2 < xMin ) || ( x1 > xMax && x2 > xMax );
[4271]28
29 return !isOffScreen;
30}
31
32static inline bool qwtIsVSampleInside( const QwtIntervalSample &sample,
33 double xMin, double xMax, double yMin, double yMax )
34{
35 const double x = sample.value;
36 const double y1 = sample.interval.minValue();
37 const double y2 = sample.interval.maxValue();
38
39 const bool isOffScreen = ( x < xMin ) || ( x > xMax )
40 || ( y1 < yMin && y2 < yMin ) || ( y1 > yMax && y2 > yMax );
41
42 return !isOffScreen;
43}
44
45class QwtPlotIntervalCurve::PrivateData
46{
47public:
48 PrivateData():
[8127]49 style( QwtPlotIntervalCurve::Tube ),
[4271]50 symbol( NULL ),
51 pen( Qt::black ),
52 brush( Qt::white )
53 {
54 paintAttributes = QwtPlotIntervalCurve::ClipPolygons;
55 paintAttributes |= QwtPlotIntervalCurve::ClipSymbol;
[8127]56
[4271]57 pen.setCapStyle( Qt::FlatCap );
58 }
59
60 ~PrivateData()
61 {
62 delete symbol;
63 }
64
[8127]65 QwtPlotIntervalCurve::CurveStyle style;
[4271]66 const QwtIntervalSymbol *symbol;
67
68 QPen pen;
69 QBrush brush;
70
71 QwtPlotIntervalCurve::PaintAttributes paintAttributes;
72};
73
74/*!
75 Constructor
76 \param title Title of the curve
77*/
78QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QwtText &title ):
[8127]79 QwtPlotSeriesItem( title )
[4271]80{
81 init();
82}
83
84/*!
85 Constructor
86 \param title Title of the curve
87*/
88QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QString &title ):
[8127]89 QwtPlotSeriesItem( QwtText( title ) )
[4271]90{
91 init();
92}
93
94//! Destructor
95QwtPlotIntervalCurve::~QwtPlotIntervalCurve()
96{
97 delete d_data;
98}
99
100//! Initialize internal members
101void QwtPlotIntervalCurve::init()
102{
103 setItemAttribute( QwtPlotItem::Legend, true );
104 setItemAttribute( QwtPlotItem::AutoScale, true );
105
106 d_data = new PrivateData;
[8127]107 setData( new QwtIntervalSeriesData() );
[4271]108
109 setZ( 19.0 );
110}
111
112//! \return QwtPlotItem::Rtti_PlotIntervalCurve
113int QwtPlotIntervalCurve::rtti() const
114{
115 return QwtPlotIntervalCurve::Rtti_PlotIntervalCurve;
116}
117
118/*!
119 Specify an attribute how to draw the curve
120
121 \param attribute Paint attribute
122 \param on On/Off
123 \sa testPaintAttribute()
124*/
[8127]125void QwtPlotIntervalCurve::setPaintAttribute(
[4271]126 PaintAttribute attribute, bool on )
127{
128 if ( on )
129 d_data->paintAttributes |= attribute;
130 else
131 d_data->paintAttributes &= ~attribute;
132}
133
134/*!
[8127]135 \return True, when attribute is enabled
[4271]136 \sa PaintAttribute, setPaintAttribute()
137*/
[8127]138bool QwtPlotIntervalCurve::testPaintAttribute(
[4271]139 PaintAttribute attribute ) const
140{
141 return ( d_data->paintAttributes & attribute );
142}
143
144/*!
145 Initialize data with an array of samples.
146 \param samples Vector of samples
147*/
148void QwtPlotIntervalCurve::setSamples(
149 const QVector<QwtIntervalSample> &samples )
150{
[8127]151 setData( new QwtIntervalSeriesData( samples ) );
[4271]152}
153
154/*!
[8127]155 Assign a series of samples
156
157 setSamples() is just a wrapper for setData() without any additional
158 value - beside that it is easier to find for the developer.
159
160 \param data Data
161 \warning The item takes ownership of the data object, deleting
162 it when its not used anymore.
163*/
164void QwtPlotIntervalCurve::setSamples(
165 QwtSeriesData<QwtIntervalSample> *data )
166{
167 setData( data );
168}
169
170/*!
[4271]171 Set the curve's drawing style
172
173 \param style Curve style
174 \sa CurveStyle, style()
175*/
176void QwtPlotIntervalCurve::setStyle( CurveStyle style )
177{
178 if ( style != d_data->style )
179 {
180 d_data->style = style;
[8127]181
182 legendChanged();
[4271]183 itemChanged();
184 }
185}
186
187/*!
[8127]188 \return Style of the curve
[4271]189 \sa setStyle()
190*/
191QwtPlotIntervalCurve::CurveStyle QwtPlotIntervalCurve::style() const
192{
193 return d_data->style;
194}
195
196/*!
197 Assign a symbol.
198
199 \param symbol Symbol
200 \sa symbol()
201*/
202void QwtPlotIntervalCurve::setSymbol( const QwtIntervalSymbol *symbol )
203{
204 if ( symbol != d_data->symbol )
205 {
206 delete d_data->symbol;
207 d_data->symbol = symbol;
[8127]208
209 legendChanged();
[4271]210 itemChanged();
211 }
212}
213
214/*!
215 \return Current symbol or NULL, when no symbol has been assigned
216 \sa setSymbol()
217*/
218const QwtIntervalSymbol *QwtPlotIntervalCurve::symbol() const
219{
220 return d_data->symbol;
221}
222
223/*!
[8127]224 Build and assign a pen
225
226 In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
227 non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
228 to hide this incompatibility.
229
230 \param color Pen color
231 \param width Pen width
232 \param style Pen style
233
234 \sa pen(), brush()
235 */
236void QwtPlotIntervalCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style )
237{
238 setPen( QPen( color, width, style ) );
239}
240
241/*!
[4271]242 \brief Assign a pen
243 \param pen New pen
244 \sa pen(), brush()
245*/
246void QwtPlotIntervalCurve::setPen( const QPen &pen )
247{
248 if ( pen != d_data->pen )
249 {
250 d_data->pen = pen;
[8127]251
252 legendChanged();
[4271]253 itemChanged();
254 }
255}
256
257/*!
[8127]258 \return Pen used to draw the lines
[4271]259 \sa setPen(), brush()
260*/
261const QPen& QwtPlotIntervalCurve::pen() const
262{
263 return d_data->pen;
264}
265
266/*!
267 Assign a brush.
268
269 The brush is used to fill the area in Tube style().
270
271 \param brush Brush
272 \sa brush(), pen(), setStyle(), CurveStyle
273*/
274void QwtPlotIntervalCurve::setBrush( const QBrush &brush )
275{
276 if ( brush != d_data->brush )
277 {
278 d_data->brush = brush;
[8127]279
280 legendChanged();
[4271]281 itemChanged();
282 }
283}
284
285/*!
286 \return Brush used to fill the area in Tube style()
287 \sa setBrush(), setStyle(), CurveStyle
288*/
289const QBrush& QwtPlotIntervalCurve::brush() const
290{
291 return d_data->brush;
292}
293
294/*!
295 \return Bounding rectangle of all samples.
296 For an empty series the rectangle is invalid.
297*/
298QRectF QwtPlotIntervalCurve::boundingRect() const
299{
[8127]300 QRectF rect = QwtPlotSeriesItem::boundingRect();
[4271]301 if ( rect.isValid() && orientation() == Qt::Vertical )
302 rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
303
304 return rect;
305}
306
307/*!
308 Draw a subset of the samples
309
310 \param painter Painter
311 \param xMap Maps x-values into pixel coordinates.
312 \param yMap Maps y-values into pixel coordinates.
[8127]313 \param canvasRect Contents rectangle of the canvas
[4271]314 \param from Index of the first sample to be painted
315 \param to Index of the last sample to be painted. If to < 0 the
316 series will be painted to its last sample.
317
318 \sa drawTube(), drawSymbols()
319*/
320void QwtPlotIntervalCurve::drawSeries( QPainter *painter,
321 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
322 const QRectF &canvasRect, int from, int to ) const
323{
324 if ( to < 0 )
325 to = dataSize() - 1;
326
327 if ( from < 0 )
328 from = 0;
329
330 if ( from > to )
331 return;
332
333 switch ( d_data->style )
334 {
335 case Tube:
336 drawTube( painter, xMap, yMap, canvasRect, from, to );
337 break;
338
339 case NoCurve:
340 default:
341 break;
342 }
343
344 if ( d_data->symbol &&
345 ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) )
346 {
[8127]347 drawSymbols( painter, *d_data->symbol,
[4271]348 xMap, yMap, canvasRect, from, to );
349 }
350}
351
352/*!
353 Draw a tube
354
355 Builds 2 curves from the upper and lower limits of the intervals
356 and draws them with the pen(). The area between the curves is
357 filled with the brush().
358
359 \param painter Painter
360 \param xMap Maps x-values into pixel coordinates.
361 \param yMap Maps y-values into pixel coordinates.
[8127]362 \param canvasRect Contents rectangle of the canvas
[4271]363 \param from Index of the first sample to be painted
364 \param to Index of the last sample to be painted. If to < 0 the
365 series will be painted to its last sample.
366
367 \sa drawSeries(), drawSymbols()
368*/
369void QwtPlotIntervalCurve::drawTube( QPainter *painter,
370 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
371 const QRectF &canvasRect, int from, int to ) const
372{
373 const bool doAlign = QwtPainter::roundingAlignment( painter );
374
375 painter->save();
376
377 const size_t size = to - from + 1;
378 QPolygonF polygon( 2 * size );
379 QPointF *points = polygon.data();
380
381 for ( uint i = 0; i < size; i++ )
382 {
383 QPointF &minValue = points[i];
384 QPointF &maxValue = points[2 * size - 1 - i];
385
386 const QwtIntervalSample intervalSample = sample( from + i );
387 if ( orientation() == Qt::Vertical )
388 {
389 double x = xMap.transform( intervalSample.value );
390 double y1 = yMap.transform( intervalSample.interval.minValue() );
391 double y2 = yMap.transform( intervalSample.interval.maxValue() );
392 if ( doAlign )
393 {
394 x = qRound( x );
395 y1 = qRound( y1 );
396 y2 = qRound( y2 );
397 }
398
399 minValue.rx() = x;
400 minValue.ry() = y1;
401 maxValue.rx() = x;
402 maxValue.ry() = y2;
403 }
404 else
405 {
406 double y = yMap.transform( intervalSample.value );
407 double x1 = xMap.transform( intervalSample.interval.minValue() );
408 double x2 = xMap.transform( intervalSample.interval.maxValue() );
409 if ( doAlign )
410 {
411 y = qRound( y );
412 x1 = qRound( x1 );
413 x2 = qRound( x2 );
414 }
415
416 minValue.rx() = x1;
417 minValue.ry() = y;
418 maxValue.rx() = x2;
419 maxValue.ry() = y;
420 }
421 }
422
423 if ( d_data->brush.style() != Qt::NoBrush )
424 {
425 painter->setPen( QPen( Qt::NoPen ) );
426 painter->setBrush( d_data->brush );
427
428 if ( d_data->paintAttributes & ClipPolygons )
429 {
430 const qreal m = 1.0;
[8127]431 const QPolygonF p = QwtClipper::clipPolygonF(
432 canvasRect.adjusted( -m, -m, m, m ), polygon, true );
[4271]433
434 QwtPainter::drawPolygon( painter, p );
435 }
436 else
437 {
438 QwtPainter::drawPolygon( painter, polygon );
439 }
440 }
441
442 if ( d_data->pen.style() != Qt::NoPen )
443 {
444 painter->setPen( d_data->pen );
445 painter->setBrush( Qt::NoBrush );
446
447 if ( d_data->paintAttributes & ClipPolygons )
448 {
[8127]449 qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF() );
450 const QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw );
[4271]451
452 QPolygonF p;
453
454 p.resize( size );
[8127]455 ::memcpy( p.data(), points, size * sizeof( QPointF ) );
456 p = QwtClipper::clipPolygonF( clipRect, p );
[4271]457 QwtPainter::drawPolyline( painter, p );
458
459 p.resize( size );
[8127]460 ::memcpy( p.data(), points + size, size * sizeof( QPointF ) );
461 p = QwtClipper::clipPolygonF( clipRect, p );
[4271]462 QwtPainter::drawPolyline( painter, p );
463 }
464 else
465 {
466 QwtPainter::drawPolyline( painter, points, size );
467 QwtPainter::drawPolyline( painter, points + size, size );
468 }
469 }
470
471 painter->restore();
472}
473
474/*!
475 Draw symbols for a subset of the samples
476
477 \param painter Painter
478 \param symbol Interval symbol
479 \param xMap x map
480 \param yMap y map
[8127]481 \param canvasRect Contents rectangle of the canvas
[4271]482 \param from Index of the first sample to be painted
483 \param to Index of the last sample to be painted
484
485 \sa setSymbol(), drawSeries(), drawTube()
486*/
487void QwtPlotIntervalCurve::drawSymbols(
488 QPainter *painter, const QwtIntervalSymbol &symbol,
489 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
490 const QRectF &canvasRect, int from, int to ) const
491{
492 painter->save();
493
494 QPen pen = symbol.pen();
495 pen.setCapStyle( Qt::FlatCap );
496
497 painter->setPen( pen );
498 painter->setBrush( symbol.brush() );
499
[8127]500 const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect );
[4271]501
502 const double xMin = tr.left();
503 const double xMax = tr.right();
504 const double yMin = tr.top();
505 const double yMax = tr.bottom();
506
[8127]507 const bool doClip = d_data->paintAttributes & ClipSymbol;
[4271]508
509 for ( int i = from; i <= to; i++ )
510 {
511 const QwtIntervalSample s = sample( i );
512
513 if ( orientation() == Qt::Vertical )
514 {
515 if ( !doClip || qwtIsVSampleInside( s, xMin, xMax, yMin, yMax ) )
516 {
517 const double x = xMap.transform( s.value );
518 const double y1 = yMap.transform( s.interval.minValue() );
519 const double y2 = yMap.transform( s.interval.maxValue() );
520
521 symbol.draw( painter, orientation(),
522 QPointF( x, y1 ), QPointF( x, y2 ) );
523 }
524 }
525 else
526 {
527 if ( !doClip || qwtIsHSampleInside( s, xMin, xMax, yMin, yMax ) )
528 {
529 const double y = yMap.transform( s.value );
530 const double x1 = xMap.transform( s.interval.minValue() );
531 const double x2 = xMap.transform( s.interval.maxValue() );
532
533 symbol.draw( painter, orientation(),
534 QPointF( x1, y ), QPointF( x2, y ) );
535 }
536 }
537 }
538
539 painter->restore();
540}
541
542/*!
[8127]543 \return Icon for the legend
[4271]544
[8127]545 In case of Tube style() the icon is a plain rectangle filled with the brush().
546 If a symbol is assigned it is scaled to size.
547
548 \param index Index of the legend entry
549 ( ignored as there is only one )
550 \param size Icon size
551
552 \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
[4271]553*/
[8127]554QwtGraphic QwtPlotIntervalCurve::legendIcon(
555 int index, const QSizeF &size ) const
[4271]556{
[8127]557 Q_UNUSED( index );
[4271]558
[8127]559 if ( size.isEmpty() )
560 return QwtGraphic();
[4271]561
[8127]562 QwtGraphic icon;
563 icon.setDefaultSize( size );
564 icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
[4271]565
[8127]566 QPainter painter( &icon );
567 painter.setRenderHint( QPainter::Antialiasing,
568 testRenderHint( QwtPlotItem::RenderAntialiased ) );
569
[4271]570 if ( d_data->style == Tube )
571 {
[8127]572 QRectF r( 0, 0, size.width(), size.height() );
573 painter.fillRect( r, d_data->brush );
[4271]574 }
575
576 if ( d_data->symbol &&
577 ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) )
578 {
579 QPen pen = d_data->symbol->pen();
580 pen.setWidthF( pen.widthF() );
581 pen.setCapStyle( Qt::FlatCap );
582
[8127]583 painter.setPen( pen );
584 painter.setBrush( d_data->symbol->brush() );
[4271]585
586 if ( orientation() == Qt::Vertical )
587 {
[8127]588 const double x = 0.5 * size.width();
589
590 d_data->symbol->draw( &painter, orientation(),
591 QPointF( x, 0 ), QPointF( x, size.height() - 1.0 ) );
[4271]592 }
593 else
594 {
[8127]595 const double y = 0.5 * size.height();
596
597 d_data->symbol->draw( &painter, orientation(),
598 QPointF( 0.0, y ), QPointF( size.width() - 1.0, y ) );
[4271]599 }
600 }
[8127]601
602 return icon;
[4271]603}
Note: See TracBrowser for help on using the repository browser.