1
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
3
* Copyright (C) 1997 Josef Wilgen
4
* Copyright (C) 2002 Uwe Rathmann
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
*****************************************************************************/
10
#include "qwt_plot_histogram.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"
20
static inline bool isCombinable( const QwtInterval &d1,
21
const QwtInterval &d2 )
23
if ( d1.isValid() && d2.isValid() )
25
if ( d1.maxValue() == d2.minValue() )
27
if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum
28
&& d2.borderFlags() & QwtInterval::ExcludeMinimum ) )
38
class QwtPlotHistogram::PrivateData
57
QwtPlotHistogram::HistogramStyle style;
58
const QwtColumnSymbol *symbol;
63
\param title Title of the histogram.
66
QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ):
67
QwtPlotSeriesItem<QwtIntervalSample>( title )
74
\param title Title of the histogram.
76
QwtPlotHistogram::QwtPlotHistogram( const QString &title ):
77
QwtPlotSeriesItem<QwtIntervalSample>( title )
83
QwtPlotHistogram::~QwtPlotHistogram()
88
//! Initialize data members
89
void QwtPlotHistogram::init()
91
d_data = new PrivateData();
92
d_series = new QwtIntervalSeriesData();
94
setItemAttribute( QwtPlotItem::AutoScale, true );
95
setItemAttribute( QwtPlotItem::Legend, true );
101
Set the histogram's drawing style
103
\param style Histogram style
104
\sa HistogramStyle, style()
106
void QwtPlotHistogram::setStyle( HistogramStyle style )
108
if ( style != d_data->style )
110
d_data->style = style;
116
Return the current style
117
\sa HistogramStyle, setStyle()
119
QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const
121
return d_data->style;
125
Assign a pen, that is used in a style() depending way.
130
void QwtPlotHistogram::setPen( const QPen &pen )
132
if ( pen != d_data->pen )
140
\return Pen used in a style() depending way.
141
\sa setPen(), brush()
143
const QPen &QwtPlotHistogram::pen() const
149
Assign a brush, that is used in a style() depending way.
151
\param brush New brush
154
void QwtPlotHistogram::setBrush( const QBrush &brush )
156
if ( brush != d_data->brush )
158
d_data->brush = brush;
164
\return Brush used in a style() depending way.
165
\sa setPen(), brush()
167
const QBrush &QwtPlotHistogram::brush() const
169
return d_data->brush;
173
\brief Assign a symbol
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().
180
\sa style(), symbol(), drawColumn(), pen(), brush()
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().
186
void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol )
188
if ( symbol != d_data->symbol )
190
delete d_data->symbol;
191
d_data->symbol = symbol;
197
\return Current symbol or NULL, when no symbol has been assigned
200
const QwtColumnSymbol *QwtPlotHistogram::symbol() const
202
return d_data->symbol;
206
\brief Set the value of the baseline
208
Each column representing an QwtIntervalSample is defined by its
209
interval and the interval between baseline and the value of the sample.
211
The default value of the baseline is 0.0.
213
\param value Value of the baseline
216
void QwtPlotHistogram::setBaseline( double value )
218
if ( d_data->baseline != value )
220
d_data->baseline = value;
226
\return Value of the baseline
229
double QwtPlotHistogram::baseline() const
231
return d_data->baseline;
235
\return Bounding rectangle of all samples.
236
For an empty series the rectangle is invalid.
238
QRectF QwtPlotHistogram::boundingRect() const
240
QRectF rect = d_series->boundingRect();
241
if ( !rect.isValid() )
244
if ( orientation() == Qt::Horizontal )
246
rect = QRectF( rect.y(), rect.x(),
247
rect.height(), rect.width() );
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 );
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 );
265
//! \return QwtPlotItem::Rtti_PlotHistogram
266
int QwtPlotHistogram::rtti() const
268
return QwtPlotItem::Rtti_PlotHistogram;
272
Initialize data with an array of samples.
273
\param samples Vector of points
275
void QwtPlotHistogram::setSamples(
276
const QVector<QwtIntervalSample> &samples )
279
d_series = new QwtIntervalSeriesData( samples );
284
Draw a subset of the histogram samples
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.
294
\sa drawOutline(), drawLines(), drawColumns
296
void QwtPlotHistogram::drawSeries( QPainter *painter,
297
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
298
const QRectF &, int from, int to ) const
300
if ( !painter || dataSize() <= 0 )
306
switch ( d_data->style )
309
drawOutline( painter, xMap, yMap, from, to );
312
drawLines( painter, xMap, yMap, from, to );
315
drawColumns( painter, xMap, yMap, from, to );
323
Draw a histogram in Outline style()
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.
332
\sa setStyle(), style()
333
\warning The outline style requires, that the intervals are in increasing
334
order and not overlapping.
336
void QwtPlotHistogram::drawOutline( QPainter *painter,
337
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
338
int from, int to ) const
340
const bool doAlign = QwtPainter::roundingAlignment( painter );
342
double v0 = ( orientation() == Qt::Horizontal ) ?
343
xMap.transform( baseline() ) : yMap.transform( baseline() );
347
QwtIntervalSample previous;
350
for ( int i = from; i <= to; i++ )
352
const QwtIntervalSample sample = d_series->sample( i );
354
if ( !sample.interval.isValid() )
356
flushPolygon( painter, v0, polygon );
361
if ( previous.interval.isValid() )
363
if ( !isCombinable( previous.interval, sample.interval ) )
364
flushPolygon( painter, v0, polygon );
367
if ( orientation() == Qt::Vertical )
369
double x1 = xMap.transform( sample.interval.minValue() );
370
double x2 = xMap.transform( sample.interval.maxValue() );
371
double y = yMap.transform( sample.value );
379
if ( polygon.size() == 0 )
380
polygon += QPointF( x1, v0 );
382
polygon += QPointF( x1, y );
383
polygon += QPointF( x2, y );
387
double y1 = yMap.transform( sample.interval.minValue() );
388
double y2 = yMap.transform( sample.interval.maxValue() );
389
double x = xMap.transform( sample.value );
397
if ( polygon.size() == 0 )
398
polygon += QPointF( v0, y1 );
400
polygon += QPointF( x, y1 );
401
polygon += QPointF( x, y2 );
406
flushPolygon( painter, v0, polygon );
410
Draw a histogram in Columns style()
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.
419
\sa setStyle(), style(), setSymbol(), drawColumn()
421
void QwtPlotHistogram::drawColumns( QPainter *painter,
422
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
423
int from, int to ) const
425
painter->setPen( d_data->pen );
426
painter->setBrush( d_data->brush );
428
for ( int i = from; i <= to; i++ )
430
const QwtIntervalSample sample = d_series->sample( i );
431
if ( !sample.interval.isNull() )
433
const QwtColumnRect rect = columnRect( sample, xMap, yMap );
434
drawColumn( painter, rect, sample );
440
Draw a histogram in Lines style()
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.
449
\sa setStyle(), style(), setPen()
451
void QwtPlotHistogram::drawLines( QPainter *painter,
452
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
453
int from, int to ) const
455
const bool doAlign = QwtPainter::roundingAlignment( painter );
457
painter->setPen( d_data->pen );
458
painter->setBrush( Qt::NoBrush );
460
for ( int i = from; i <= to; i++ )
462
const QwtIntervalSample sample = d_series->sample( i );
463
if ( !sample.interval.isNull() )
465
const QwtColumnRect rect = columnRect( sample, xMap, yMap );
467
QRectF r = rect.toRect();
470
r.setLeft( qRound( r.left() ) );
471
r.setRight( qRound( r.right() ) );
472
r.setTop( qRound( r.top() ) );
473
r.setBottom( qRound( r.bottom() ) );
476
switch ( rect.direction )
478
case QwtColumnRect::LeftToRight:
480
QwtPainter::drawLine( painter,
481
r.topRight(), r.bottomRight() );
484
case QwtColumnRect::RightToLeft:
486
QwtPainter::drawLine( painter,
487
r.topLeft(), r.bottomLeft() );
490
case QwtColumnRect::TopToBottom:
492
QwtPainter::drawLine( painter,
493
r.bottomRight(), r.bottomLeft() );
496
case QwtColumnRect::BottomToTop:
498
QwtPainter::drawLine( painter,
499
r.topRight(), r.topLeft() );
507
//! Internal, used by the Outline style.
508
void QwtPlotHistogram::flushPolygon( QPainter *painter,
509
double baseLine, QPolygonF &polygon ) const
511
if ( polygon.size() == 0 )
514
if ( orientation() == Qt::Horizontal )
515
polygon += QPointF( baseLine, polygon.last().y() );
517
polygon += QPointF( polygon.last().x(), baseLine );
519
if ( d_data->brush.style() != Qt::NoBrush )
521
painter->setPen( Qt::NoPen );
522
painter->setBrush( d_data->brush );
524
if ( orientation() == Qt::Horizontal )
526
polygon += QPointF( polygon.last().x(), baseLine );
527
polygon += QPointF( polygon.first().x(), baseLine );
531
polygon += QPointF( baseLine, polygon.last().y() );
532
polygon += QPointF( baseLine, polygon.first().y() );
534
QwtPainter::drawPolygon( painter, polygon );
535
polygon.resize( polygon.size() - 2 );
537
if ( d_data->pen.style() != Qt::NoPen )
539
painter->setBrush( Qt::NoBrush );
540
painter->setPen( d_data->pen );
541
QwtPainter::drawPolyline( painter, polygon );
547
Calculate the area that is covered by a sample
550
\param xMap Maps x-values into pixel coordinates.
551
\param yMap Maps y-values into pixel coordinates.
553
\return Rectangle, that is covered by a sample
555
QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample,
556
const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const
560
const QwtInterval &iv = sample.interval;
564
if ( orientation() == Qt::Horizontal )
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() );
571
rect.hInterval.setInterval( x0, x );
572
rect.vInterval.setInterval( y1, y2, iv.borderFlags() );
573
rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft :
574
QwtColumnRect::LeftToRight;
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 );
583
rect.hInterval.setInterval( x1, x2, iv.borderFlags() );
584
rect.vInterval.setInterval( y0, y );
585
rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop :
586
QwtColumnRect::TopToBottom;
593
Draw a column for a sample in Columns style().
595
When a symbol() has been set the symbol is used otherwise the
596
column is displayed as plain rectangle using pen() and brush().
598
\param painter Painter
599
\param rect Rectangle where to paint the column in paint device coordinates
600
\param sample Sample to be displayed
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().
606
void QwtPlotHistogram::drawColumn( QPainter *painter,
607
const QwtColumnRect &rect, const QwtIntervalSample &sample ) const
611
if ( d_data->symbol &&
612
( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) )
614
d_data->symbol->draw( painter, rect );
618
QRectF r = rect.toRect();
619
if ( QwtPainter::roundingAlignment( painter ) )
621
r.setLeft( qRound( r.left() ) );
622
r.setRight( qRound( r.right() ) );
623
r.setTop( qRound( r.top() ) );
624
r.setBottom( qRound( r.bottom() ) );
627
QwtPainter::drawRect( painter, r );
632
Draw a plain rectangle without pen using the brush() as identifier
634
\param painter Painter
635
\param rect Bounding rectangle for the identifier
637
void QwtPlotHistogram::drawLegendIdentifier(
638
QPainter *painter, const QRectF &rect ) const
640
const double dim = qMin( rect.width(), rect.height() );
642
QSizeF size( dim, dim );
644
QRectF r( 0, 0, size.width(), size.height() );
645
r.moveCenter( rect.center() );
647
painter->fillRect( r, d_data->brush );