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

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