source: ntrip/trunk/BNC/qwt/qwt_plot_histogram.cpp@ 10415

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

update to qwt verion 6.1.1 to fix build with newer Qt5

File size: 17.8 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_histogram.h"
11#include "qwt_plot.h"
12#include "qwt_painter.h"
13#include "qwt_column_symbol.h"
14#include "qwt_scale_map.h"
15#include <qstring.h>
16#include <qpainter.h>
17
18static inline bool qwtIsCombinable( const QwtInterval &d1,
19 const QwtInterval &d2 )
20{
21 if ( d1.isValid() && d2.isValid() )
22 {
23 if ( d1.maxValue() == d2.minValue() )
24 {
25 if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum
26 && d2.borderFlags() & QwtInterval::ExcludeMinimum ) )
27 {
28 return true;
29 }
30 }
31 }
32
33 return false;
34}
35
36class QwtPlotHistogram::PrivateData
37{
38public:
39 PrivateData():
40 baseline( 0.0 ),
41 style( Columns ),
42 symbol( NULL )
43 {
44 }
45
46 ~PrivateData()
47 {
48 delete symbol;
49 }
50
51 double baseline;
52
53 QPen pen;
54 QBrush brush;
55 QwtPlotHistogram::HistogramStyle style;
56 const QwtColumnSymbol *symbol;
57};
58
59/*!
60 Constructor
61 \param title Title of the histogram.
62*/
63QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ):
64 QwtPlotSeriesItem( title )
65{
66 init();
67}
68
69/*!
70 Constructor
71 \param title Title of the histogram.
72*/
73QwtPlotHistogram::QwtPlotHistogram( const QString &title ):
74 QwtPlotSeriesItem( title )
75{
76 init();
77}
78
79//! Destructor
80QwtPlotHistogram::~QwtPlotHistogram()
81{
82 delete d_data;
83}
84
85//! Initialize data members
86void QwtPlotHistogram::init()
87{
88 d_data = new PrivateData();
89 setData( new QwtIntervalSeriesData() );
90
91 setItemAttribute( QwtPlotItem::AutoScale, true );
92 setItemAttribute( QwtPlotItem::Legend, true );
93
94 setZ( 20.0 );
95}
96
97/*!
98 Set the histogram's drawing style
99
100 \param style Histogram style
101 \sa HistogramStyle, style()
102*/
103void QwtPlotHistogram::setStyle( HistogramStyle style )
104{
105 if ( style != d_data->style )
106 {
107 d_data->style = style;
108
109 legendChanged();
110 itemChanged();
111 }
112}
113
114/*!
115 \return Style of the histogram
116 \sa HistogramStyle, setStyle()
117*/
118QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const
119{
120 return d_data->style;
121}
122
123/*!
124 Build and assign a pen
125
126 In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
127 non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
128 to hide this incompatibility.
129
130 \param color Pen color
131 \param width Pen width
132 \param style Pen style
133
134 \sa pen(), brush()
135 */
136void QwtPlotHistogram::setPen( const QColor &color, qreal width, Qt::PenStyle style )
137{
138 setPen( QPen( color, width, style ) );
139}
140
141/*!
142 Assign a pen, that is used in a style() depending way.
143
144 \param pen New pen
145 \sa pen(), brush()
146*/
147void QwtPlotHistogram::setPen( const QPen &pen )
148{
149 if ( pen != d_data->pen )
150 {
151 d_data->pen = pen;
152
153 legendChanged();
154 itemChanged();
155 }
156}
157
158/*!
159 \return Pen used in a style() depending way.
160 \sa setPen(), brush()
161*/
162const QPen &QwtPlotHistogram::pen() const
163{
164 return d_data->pen;
165}
166
167/*!
168 Assign a brush, that is used in a style() depending way.
169
170 \param brush New brush
171 \sa pen(), brush()
172*/
173void QwtPlotHistogram::setBrush( const QBrush &brush )
174{
175 if ( brush != d_data->brush )
176 {
177 d_data->brush = brush;
178
179 legendChanged();
180 itemChanged();
181 }
182}
183
184/*!
185 \return Brush used in a style() depending way.
186 \sa setPen(), brush()
187*/
188const QBrush &QwtPlotHistogram::brush() const
189{
190 return d_data->brush;
191}
192
193/*!
194 \brief Assign a symbol
195
196 In Column style an optional symbol can be assigned, that is responsible
197 for displaying the rectangle that is defined by the interval and
198 the distance between baseline() and value. When no symbol has been
199 defined the area is displayed as plain rectangle using pen() and brush().
200
201 \sa style(), symbol(), drawColumn(), pen(), brush()
202
203 \note In applications, where different intervals need to be displayed
204 in a different way ( f.e different colors or even using different symbols)
205 it is recommended to overload drawColumn().
206*/
207void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol )
208{
209 if ( symbol != d_data->symbol )
210 {
211 delete d_data->symbol;
212 d_data->symbol = symbol;
213
214 legendChanged();
215 itemChanged();
216 }
217}
218
219/*!
220 \return Current symbol or NULL, when no symbol has been assigned
221 \sa setSymbol()
222*/
223const QwtColumnSymbol *QwtPlotHistogram::symbol() const
224{
225 return d_data->symbol;
226}
227
228/*!
229 \brief Set the value of the baseline
230
231 Each column representing an QwtIntervalSample is defined by its
232 interval and the interval between baseline and the value of the sample.
233
234 The default value of the baseline is 0.0.
235
236 \param value Value of the baseline
237 \sa baseline()
238*/
239void QwtPlotHistogram::setBaseline( double value )
240{
241 if ( d_data->baseline != value )
242 {
243 d_data->baseline = value;
244 itemChanged();
245 }
246}
247
248/*!
249 \return Value of the baseline
250 \sa setBaseline()
251*/
252double QwtPlotHistogram::baseline() const
253{
254 return d_data->baseline;
255}
256
257/*!
258 \return Bounding rectangle of all samples.
259 For an empty series the rectangle is invalid.
260*/
261QRectF QwtPlotHistogram::boundingRect() const
262{
263 QRectF rect = data()->boundingRect();
264 if ( !rect.isValid() )
265 return rect;
266
267 if ( orientation() == Qt::Horizontal )
268 {
269 rect = QRectF( rect.y(), rect.x(),
270 rect.height(), rect.width() );
271
272 if ( rect.left() > d_data->baseline )
273 rect.setLeft( d_data->baseline );
274 else if ( rect.right() < d_data->baseline )
275 rect.setRight( d_data->baseline );
276 }
277 else
278 {
279 if ( rect.bottom() < d_data->baseline )
280 rect.setBottom( d_data->baseline );
281 else if ( rect.top() > d_data->baseline )
282 rect.setTop( d_data->baseline );
283 }
284
285 return rect;
286}
287
288//! \return QwtPlotItem::Rtti_PlotHistogram
289int QwtPlotHistogram::rtti() const
290{
291 return QwtPlotItem::Rtti_PlotHistogram;
292}
293
294/*!
295 Initialize data with an array of samples.
296 \param samples Vector of points
297*/
298void QwtPlotHistogram::setSamples(
299 const QVector<QwtIntervalSample> &samples )
300{
301 setData( new QwtIntervalSeriesData( samples ) );
302}
303
304/*!
305 Assign a series of samples
306
307 setSamples() is just a wrapper for setData() without any additional
308 value - beside that it is easier to find for the developer.
309
310 \param data Data
311 \warning The item takes ownership of the data object, deleting
312 it when its not used anymore.
313*/
314void QwtPlotHistogram::setSamples(
315 QwtSeriesData<QwtIntervalSample> *data )
316{
317 setData( data );
318}
319
320/*!
321 Draw a subset of the histogram samples
322
323 \param painter Painter
324 \param xMap Maps x-values into pixel coordinates.
325 \param yMap Maps y-values into pixel coordinates.
326 \param canvasRect Contents rectangle of the canvas
327 \param from Index of the first sample to be painted
328 \param to Index of the last sample to be painted. If to < 0 the
329 series will be painted to its last sample.
330
331 \sa drawOutline(), drawLines(), drawColumns
332*/
333void QwtPlotHistogram::drawSeries( QPainter *painter,
334 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
335 const QRectF &canvasRect, int from, int to ) const
336{
337 Q_UNUSED( canvasRect )
338
339 if ( !painter || dataSize() <= 0 )
340 return;
341
342 if ( to < 0 )
343 to = dataSize() - 1;
344
345 switch ( d_data->style )
346 {
347 case Outline:
348 drawOutline( painter, xMap, yMap, from, to );
349 break;
350 case Lines:
351 drawLines( painter, xMap, yMap, from, to );
352 break;
353 case Columns:
354 drawColumns( painter, xMap, yMap, from, to );
355 break;
356 default:
357 break;
358 }
359}
360
361/*!
362 Draw a histogram in Outline style()
363
364 \param painter Painter
365 \param xMap Maps x-values into pixel coordinates.
366 \param yMap Maps y-values into pixel coordinates.
367 \param from Index of the first sample to be painted
368 \param to Index of the last sample to be painted. If to < 0 the
369 histogram will be painted to its last point.
370
371 \sa setStyle(), style()
372 \warning The outline style requires, that the intervals are in increasing
373 order and not overlapping.
374*/
375void QwtPlotHistogram::drawOutline( QPainter *painter,
376 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
377 int from, int to ) const
378{
379 const bool doAlign = QwtPainter::roundingAlignment( painter );
380
381 double v0 = ( orientation() == Qt::Horizontal ) ?
382 xMap.transform( baseline() ) : yMap.transform( baseline() );
383 if ( doAlign )
384 v0 = qRound( v0 );
385
386 QwtIntervalSample previous;
387
388 QPolygonF polygon;
389 for ( int i = from; i <= to; i++ )
390 {
391 const QwtIntervalSample sample = this->sample( i );
392
393 if ( !sample.interval.isValid() )
394 {
395 flushPolygon( painter, v0, polygon );
396 previous = sample;
397 continue;
398 }
399
400 if ( previous.interval.isValid() )
401 {
402 if ( !qwtIsCombinable( previous.interval, sample.interval ) )
403 flushPolygon( painter, v0, polygon );
404 }
405
406 if ( orientation() == Qt::Vertical )
407 {
408 double x1 = xMap.transform( sample.interval.minValue() );
409 double x2 = xMap.transform( sample.interval.maxValue() );
410 double y = yMap.transform( sample.value );
411 if ( doAlign )
412 {
413 x1 = qRound( x1 );
414 x2 = qRound( x2 );
415 y = qRound( y );
416 }
417
418 if ( polygon.size() == 0 )
419 polygon += QPointF( x1, v0 );
420
421 polygon += QPointF( x1, y );
422 polygon += QPointF( x2, y );
423 }
424 else
425 {
426 double y1 = yMap.transform( sample.interval.minValue() );
427 double y2 = yMap.transform( sample.interval.maxValue() );
428 double x = xMap.transform( sample.value );
429 if ( doAlign )
430 {
431 y1 = qRound( y1 );
432 y2 = qRound( y2 );
433 x = qRound( x );
434 }
435
436 if ( polygon.size() == 0 )
437 polygon += QPointF( v0, y1 );
438
439 polygon += QPointF( x, y1 );
440 polygon += QPointF( x, y2 );
441 }
442 previous = sample;
443 }
444
445 flushPolygon( painter, v0, polygon );
446}
447
448/*!
449 Draw a histogram in Columns style()
450
451 \param painter Painter
452 \param xMap Maps x-values into pixel coordinates.
453 \param yMap Maps y-values into pixel coordinates.
454 \param from Index of the first sample to be painted
455 \param to Index of the last sample to be painted. If to < 0 the
456 histogram will be painted to its last point.
457
458 \sa setStyle(), style(), setSymbol(), drawColumn()
459*/
460void QwtPlotHistogram::drawColumns( QPainter *painter,
461 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
462 int from, int to ) const
463{
464 painter->setPen( d_data->pen );
465 painter->setBrush( d_data->brush );
466
467 const QwtSeriesData<QwtIntervalSample> *series = data();
468
469 for ( int i = from; i <= to; i++ )
470 {
471 const QwtIntervalSample sample = series->sample( i );
472 if ( !sample.interval.isNull() )
473 {
474 const QwtColumnRect rect = columnRect( sample, xMap, yMap );
475 drawColumn( painter, rect, sample );
476 }
477 }
478}
479
480/*!
481 Draw a histogram in Lines style()
482
483 \param painter Painter
484 \param xMap Maps x-values into pixel coordinates.
485 \param yMap Maps y-values into pixel coordinates.
486 \param from Index of the first sample to be painted
487 \param to Index of the last sample to be painted. If to < 0 the
488 histogram will be painted to its last point.
489
490 \sa setStyle(), style(), setPen()
491*/
492void QwtPlotHistogram::drawLines( QPainter *painter,
493 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
494 int from, int to ) const
495{
496 const bool doAlign = QwtPainter::roundingAlignment( painter );
497
498 painter->setPen( d_data->pen );
499 painter->setBrush( Qt::NoBrush );
500
501 const QwtSeriesData<QwtIntervalSample> *series = data();
502
503 for ( int i = from; i <= to; i++ )
504 {
505 const QwtIntervalSample sample = series->sample( i );
506 if ( !sample.interval.isNull() )
507 {
508 const QwtColumnRect rect = columnRect( sample, xMap, yMap );
509
510 QRectF r = rect.toRect();
511 if ( doAlign )
512 {
513 r.setLeft( qRound( r.left() ) );
514 r.setRight( qRound( r.right() ) );
515 r.setTop( qRound( r.top() ) );
516 r.setBottom( qRound( r.bottom() ) );
517 }
518
519 switch ( rect.direction )
520 {
521 case QwtColumnRect::LeftToRight:
522 {
523 QwtPainter::drawLine( painter,
524 r.topRight(), r.bottomRight() );
525 break;
526 }
527 case QwtColumnRect::RightToLeft:
528 {
529 QwtPainter::drawLine( painter,
530 r.topLeft(), r.bottomLeft() );
531 break;
532 }
533 case QwtColumnRect::TopToBottom:
534 {
535 QwtPainter::drawLine( painter,
536 r.bottomRight(), r.bottomLeft() );
537 break;
538 }
539 case QwtColumnRect::BottomToTop:
540 {
541 QwtPainter::drawLine( painter,
542 r.topRight(), r.topLeft() );
543 break;
544 }
545 }
546 }
547 }
548}
549
550//! Internal, used by the Outline style.
551void QwtPlotHistogram::flushPolygon( QPainter *painter,
552 double baseLine, QPolygonF &polygon ) const
553{
554 if ( polygon.size() == 0 )
555 return;
556
557 if ( orientation() == Qt::Horizontal )
558 polygon += QPointF( baseLine, polygon.last().y() );
559 else
560 polygon += QPointF( polygon.last().x(), baseLine );
561
562 if ( d_data->brush.style() != Qt::NoBrush )
563 {
564 painter->setPen( Qt::NoPen );
565 painter->setBrush( d_data->brush );
566
567 if ( orientation() == Qt::Horizontal )
568 {
569 polygon += QPointF( polygon.last().x(), baseLine );
570 polygon += QPointF( polygon.first().x(), baseLine );
571 }
572 else
573 {
574 polygon += QPointF( baseLine, polygon.last().y() );
575 polygon += QPointF( baseLine, polygon.first().y() );
576 }
577
578 QwtPainter::drawPolygon( painter, polygon );
579
580 polygon.pop_back();
581 polygon.pop_back();
582 }
583 if ( d_data->pen.style() != Qt::NoPen )
584 {
585 painter->setBrush( Qt::NoBrush );
586 painter->setPen( d_data->pen );
587 QwtPainter::drawPolyline( painter, polygon );
588 }
589 polygon.clear();
590}
591
592/*!
593 Calculate the area that is covered by a sample
594
595 \param sample Sample
596 \param xMap Maps x-values into pixel coordinates.
597 \param yMap Maps y-values into pixel coordinates.
598
599 \return Rectangle, that is covered by a sample
600*/
601QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample,
602 const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const
603{
604 QwtColumnRect rect;
605
606 const QwtInterval &iv = sample.interval;
607 if ( !iv.isValid() )
608 return rect;
609
610 if ( orientation() == Qt::Horizontal )
611 {
612 const double x0 = xMap.transform( baseline() );
613 const double x = xMap.transform( sample.value );
614 const double y1 = yMap.transform( iv.minValue() );
615 const double y2 = yMap.transform( iv.maxValue() );
616
617 rect.hInterval.setInterval( x0, x );
618 rect.vInterval.setInterval( y1, y2, iv.borderFlags() );
619 rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft :
620 QwtColumnRect::LeftToRight;
621 }
622 else
623 {
624 const double x1 = xMap.transform( iv.minValue() );
625 const double x2 = xMap.transform( iv.maxValue() );
626 const double y0 = yMap.transform( baseline() );
627 const double y = yMap.transform( sample.value );
628
629 rect.hInterval.setInterval( x1, x2, iv.borderFlags() );
630 rect.vInterval.setInterval( y0, y );
631 rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop :
632 QwtColumnRect::TopToBottom;
633 }
634
635 return rect;
636}
637
638/*!
639 Draw a column for a sample in Columns style().
640
641 When a symbol() has been set the symbol is used otherwise the
642 column is displayed as plain rectangle using pen() and brush().
643
644 \param painter Painter
645 \param rect Rectangle where to paint the column in paint device coordinates
646 \param sample Sample to be displayed
647
648 \note In applications, where different intervals need to be displayed
649 in a different way ( f.e different colors or even using different symbols)
650 it is recommended to overload drawColumn().
651*/
652void QwtPlotHistogram::drawColumn( QPainter *painter,
653 const QwtColumnRect &rect, const QwtIntervalSample &sample ) const
654{
655 Q_UNUSED( sample );
656
657 if ( d_data->symbol &&
658 ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) )
659 {
660 d_data->symbol->draw( painter, rect );
661 }
662 else
663 {
664 QRectF r = rect.toRect();
665 if ( QwtPainter::roundingAlignment( painter ) )
666 {
667 r.setLeft( qRound( r.left() ) );
668 r.setRight( qRound( r.right() ) );
669 r.setTop( qRound( r.top() ) );
670 r.setBottom( qRound( r.bottom() ) );
671 }
672
673 QwtPainter::drawRect( painter, r );
674 }
675}
676
677/*!
678 A plain rectangle without pen using the brush()
679
680 \param index Index of the legend entry
681 ( ignored as there is only one )
682 \param size Icon size
683 \return A graphic displaying the icon
684
685 \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
686*/
687QwtGraphic QwtPlotHistogram::legendIcon( int index,
688 const QSizeF &size ) const
689{
690 Q_UNUSED( index );
691 return defaultIcon( d_data->brush, size );
692}
Note: See TracBrowser for help on using the repository browser.