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

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

update qwt and qwtpolar, many QT5 fixes (unfinished)

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 &, int from, int to ) const
336{
337 if ( !painter || dataSize() <= 0 )
338 return;
339
340 if ( to < 0 )
341 to = dataSize() - 1;
342
343 switch ( d_data->style )
344 {
345 case Outline:
346 drawOutline( painter, xMap, yMap, from, to );
347 break;
348 case Lines:
349 drawLines( painter, xMap, yMap, from, to );
350 break;
351 case Columns:
352 drawColumns( painter, xMap, yMap, from, to );
353 break;
354 default:
355 break;
356 }
357}
358
359/*!
360 Draw a histogram in Outline style()
361
362 \param painter Painter
363 \param xMap Maps x-values into pixel coordinates.
364 \param yMap Maps y-values into pixel coordinates.
365 \param from Index of the first sample to be painted
366 \param to Index of the last sample to be painted. If to < 0 the
367 histogram will be painted to its last point.
368
369 \sa setStyle(), style()
370 \warning The outline style requires, that the intervals are in increasing
371 order and not overlapping.
372*/
373void QwtPlotHistogram::drawOutline( QPainter *painter,
374 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
375 int from, int to ) const
376{
377 const bool doAlign = QwtPainter::roundingAlignment( painter );
378
379 double v0 = ( orientation() == Qt::Horizontal ) ?
380 xMap.transform( baseline() ) : yMap.transform( baseline() );
381 if ( doAlign )
382 v0 = qRound( v0 );
383
384 QwtIntervalSample previous;
385
386 QPolygonF polygon;
387 for ( int i = from; i <= to; i++ )
388 {
389 const QwtIntervalSample sample = this->sample( i );
390
391 if ( !sample.interval.isValid() )
392 {
393 flushPolygon( painter, v0, polygon );
394 previous = sample;
395 continue;
396 }
397
398 if ( previous.interval.isValid() )
399 {
400 if ( !qwtIsCombinable( previous.interval, sample.interval ) )
401 flushPolygon( painter, v0, polygon );
402 }
403
404 if ( orientation() == Qt::Vertical )
405 {
406 double x1 = xMap.transform( sample.interval.minValue() );
407 double x2 = xMap.transform( sample.interval.maxValue() );
408 double y = yMap.transform( sample.value );
409 if ( doAlign )
410 {
411 x1 = qRound( x1 );
412 x2 = qRound( x2 );
413 y = qRound( y );
414 }
415
416 if ( polygon.size() == 0 )
417 polygon += QPointF( x1, v0 );
418
419 polygon += QPointF( x1, y );
420 polygon += QPointF( x2, y );
421 }
422 else
423 {
424 double y1 = yMap.transform( sample.interval.minValue() );
425 double y2 = yMap.transform( sample.interval.maxValue() );
426 double x = xMap.transform( sample.value );
427 if ( doAlign )
428 {
429 y1 = qRound( y1 );
430 y2 = qRound( y2 );
431 x = qRound( x );
432 }
433
434 if ( polygon.size() == 0 )
435 polygon += QPointF( v0, y1 );
436
437 polygon += QPointF( x, y1 );
438 polygon += QPointF( x, y2 );
439 }
440 previous = sample;
441 }
442
443 flushPolygon( painter, v0, polygon );
444}
445
446/*!
447 Draw a histogram in Columns style()
448
449 \param painter Painter
450 \param xMap Maps x-values into pixel coordinates.
451 \param yMap Maps y-values into pixel coordinates.
452 \param from Index of the first sample to be painted
453 \param to Index of the last sample to be painted. If to < 0 the
454 histogram will be painted to its last point.
455
456 \sa setStyle(), style(), setSymbol(), drawColumn()
457*/
458void QwtPlotHistogram::drawColumns( QPainter *painter,
459 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
460 int from, int to ) const
461{
462 painter->setPen( d_data->pen );
463 painter->setBrush( d_data->brush );
464
465 const QwtSeriesData<QwtIntervalSample> *series = data();
466
467 for ( int i = from; i <= to; i++ )
468 {
469 const QwtIntervalSample sample = series->sample( i );
470 if ( !sample.interval.isNull() )
471 {
472 const QwtColumnRect rect = columnRect( sample, xMap, yMap );
473 drawColumn( painter, rect, sample );
474 }
475 }
476}
477
478/*!
479 Draw a histogram in Lines style()
480
481 \param painter Painter
482 \param xMap Maps x-values into pixel coordinates.
483 \param yMap Maps y-values into pixel coordinates.
484 \param from Index of the first sample to be painted
485 \param to Index of the last sample to be painted. If to < 0 the
486 histogram will be painted to its last point.
487
488 \sa setStyle(), style(), setPen()
489*/
490void QwtPlotHistogram::drawLines( QPainter *painter,
491 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
492 int from, int to ) const
493{
494 const bool doAlign = QwtPainter::roundingAlignment( painter );
495
496 painter->setPen( d_data->pen );
497 painter->setBrush( Qt::NoBrush );
498
499 const QwtSeriesData<QwtIntervalSample> *series = data();
500
501 for ( int i = from; i <= to; i++ )
502 {
503 const QwtIntervalSample sample = series->sample( i );
504 if ( !sample.interval.isNull() )
505 {
506 const QwtColumnRect rect = columnRect( sample, xMap, yMap );
507
508 QRectF r = rect.toRect();
509 if ( doAlign )
510 {
511 r.setLeft( qRound( r.left() ) );
512 r.setRight( qRound( r.right() ) );
513 r.setTop( qRound( r.top() ) );
514 r.setBottom( qRound( r.bottom() ) );
515 }
516
517 switch ( rect.direction )
518 {
519 case QwtColumnRect::LeftToRight:
520 {
521 QwtPainter::drawLine( painter,
522 r.topRight(), r.bottomRight() );
523 break;
524 }
525 case QwtColumnRect::RightToLeft:
526 {
527 QwtPainter::drawLine( painter,
528 r.topLeft(), r.bottomLeft() );
529 break;
530 }
531 case QwtColumnRect::TopToBottom:
532 {
533 QwtPainter::drawLine( painter,
534 r.bottomRight(), r.bottomLeft() );
535 break;
536 }
537 case QwtColumnRect::BottomToTop:
538 {
539 QwtPainter::drawLine( painter,
540 r.topRight(), r.topLeft() );
541 break;
542 }
543 }
544 }
545 }
546}
547
548//! Internal, used by the Outline style.
549void QwtPlotHistogram::flushPolygon( QPainter *painter,
550 double baseLine, QPolygonF &polygon ) const
551{
552 if ( polygon.size() == 0 )
553 return;
554
555 if ( orientation() == Qt::Horizontal )
556 polygon += QPointF( baseLine, polygon.last().y() );
557 else
558 polygon += QPointF( polygon.last().x(), baseLine );
559
560 if ( d_data->brush.style() != Qt::NoBrush )
561 {
562 painter->setPen( Qt::NoPen );
563 painter->setBrush( d_data->brush );
564
565 if ( orientation() == Qt::Horizontal )
566 {
567 polygon += QPointF( polygon.last().x(), baseLine );
568 polygon += QPointF( polygon.first().x(), baseLine );
569 }
570 else
571 {
572 polygon += QPointF( baseLine, polygon.last().y() );
573 polygon += QPointF( baseLine, polygon.first().y() );
574 }
575
576 QwtPainter::drawPolygon( painter, polygon );
577
578 polygon.pop_back();
579 polygon.pop_back();
580 }
581 if ( d_data->pen.style() != Qt::NoPen )
582 {
583 painter->setBrush( Qt::NoBrush );
584 painter->setPen( d_data->pen );
585 QwtPainter::drawPolyline( painter, polygon );
586 }
587 polygon.clear();
588}
589
590/*!
591 Calculate the area that is covered by a sample
592
593 \param sample Sample
594 \param xMap Maps x-values into pixel coordinates.
595 \param yMap Maps y-values into pixel coordinates.
596
597 \return Rectangle, that is covered by a sample
598*/
599QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample,
600 const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const
601{
602 QwtColumnRect rect;
603
604 const QwtInterval &iv = sample.interval;
605 if ( !iv.isValid() )
606 return rect;
607
608 if ( orientation() == Qt::Horizontal )
609 {
610 const double x0 = xMap.transform( baseline() );
611 const double x = xMap.transform( sample.value );
612 const double y1 = yMap.transform( iv.minValue() );
613 const double y2 = yMap.transform( iv.maxValue() );
614
615 rect.hInterval.setInterval( x0, x );
616 rect.vInterval.setInterval( y1, y2, iv.borderFlags() );
617 rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft :
618 QwtColumnRect::LeftToRight;
619 }
620 else
621 {
622 const double x1 = xMap.transform( iv.minValue() );
623 const double x2 = xMap.transform( iv.maxValue() );
624 const double y0 = yMap.transform( baseline() );
625 const double y = yMap.transform( sample.value );
626
627 rect.hInterval.setInterval( x1, x2, iv.borderFlags() );
628 rect.vInterval.setInterval( y0, y );
629 rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop :
630 QwtColumnRect::TopToBottom;
631 }
632
633 return rect;
634}
635
636/*!
637 Draw a column for a sample in Columns style().
638
639 When a symbol() has been set the symbol is used otherwise the
640 column is displayed as plain rectangle using pen() and brush().
641
642 \param painter Painter
643 \param rect Rectangle where to paint the column in paint device coordinates
644 \param sample Sample to be displayed
645
646 \note In applications, where different intervals need to be displayed
647 in a different way ( f.e different colors or even using different symbols)
648 it is recommended to overload drawColumn().
649*/
650void QwtPlotHistogram::drawColumn( QPainter *painter,
651 const QwtColumnRect &rect, const QwtIntervalSample &sample ) const
652{
653 Q_UNUSED( sample );
654
655 if ( d_data->symbol &&
656 ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) )
657 {
658 d_data->symbol->draw( painter, rect );
659 }
660 else
661 {
662 QRectF r = rect.toRect();
663 if ( QwtPainter::roundingAlignment( painter ) )
664 {
665 r.setLeft( qRound( r.left() ) );
666 r.setRight( qRound( r.right() ) );
667 r.setTop( qRound( r.top() ) );
668 r.setBottom( qRound( r.bottom() ) );
669 }
670
671 QwtPainter::drawRect( painter, r );
672 }
673}
674
675/*!
676 A plain rectangle without pen using the brush()
677
678 \param index Index of the legend entry
679 ( ignored as there is only one )
680 \param size Icon size
681 \return A graphic displaying the icon
682
683 \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
684*/
685QwtGraphic QwtPlotHistogram::legendIcon( int index,
686 const QSizeF &size ) const
687{
688 Q_UNUSED( index );
689 return defaultIcon( d_data->brush, size );
690}
Note: See TracBrowser for help on using the repository browser.