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

Last change on this file since 11158 was 11158, checked in by stuerze, 9 days ago

deprecated functions exchanged libs updated

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