00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_plot_histogram.h"
00011 #include "qwt_plot.h"
00012 #include "qwt_painter.h"
00013 #include "qwt_column_symbol.h"
00014 #include "qwt_scale_map.h"
00015 #include <qstring.h>
00016 #include <qpainter.h>
00017
00018 static inline bool qwtIsCombinable( const QwtInterval &d1,
00019 const QwtInterval &d2 )
00020 {
00021 if ( d1.isValid() && d2.isValid() )
00022 {
00023 if ( d1.maxValue() == d2.minValue() )
00024 {
00025 if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum
00026 && d2.borderFlags() & QwtInterval::ExcludeMinimum ) )
00027 {
00028 return true;
00029 }
00030 }
00031 }
00032
00033 return false;
00034 }
00035
00036 class QwtPlotHistogram::PrivateData
00037 {
00038 public:
00039 PrivateData():
00040 baseline( 0.0 ),
00041 style( Columns ),
00042 symbol( NULL )
00043 {
00044 }
00045
00046 ~PrivateData()
00047 {
00048 delete symbol;
00049 }
00050
00051 double baseline;
00052
00053 QPen pen;
00054 QBrush brush;
00055 QwtPlotHistogram::HistogramStyle style;
00056 const QwtColumnSymbol *symbol;
00057 };
00058
00063 QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ):
00064 QwtPlotSeriesItem( title )
00065 {
00066 init();
00067 }
00068
00073 QwtPlotHistogram::QwtPlotHistogram( const QString &title ):
00074 QwtPlotSeriesItem( title )
00075 {
00076 init();
00077 }
00078
00080 QwtPlotHistogram::~QwtPlotHistogram()
00081 {
00082 delete d_data;
00083 }
00084
00086 void QwtPlotHistogram::init()
00087 {
00088 d_data = new PrivateData();
00089 setData( new QwtIntervalSeriesData() );
00090
00091 setItemAttribute( QwtPlotItem::AutoScale, true );
00092 setItemAttribute( QwtPlotItem::Legend, true );
00093
00094 setZ( 20.0 );
00095 }
00096
00103 void QwtPlotHistogram::setStyle( HistogramStyle style )
00104 {
00105 if ( style != d_data->style )
00106 {
00107 d_data->style = style;
00108
00109 legendChanged();
00110 itemChanged();
00111 }
00112 }
00113
00118 QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const
00119 {
00120 return d_data->style;
00121 }
00122
00136 void QwtPlotHistogram::setPen( const QColor &color, qreal width, Qt::PenStyle style )
00137 {
00138 setPen( QPen( color, width, style ) );
00139 }
00140
00147 void QwtPlotHistogram::setPen( const QPen &pen )
00148 {
00149 if ( pen != d_data->pen )
00150 {
00151 d_data->pen = pen;
00152
00153 legendChanged();
00154 itemChanged();
00155 }
00156 }
00157
00162 const QPen &QwtPlotHistogram::pen() const
00163 {
00164 return d_data->pen;
00165 }
00166
00173 void QwtPlotHistogram::setBrush( const QBrush &brush )
00174 {
00175 if ( brush != d_data->brush )
00176 {
00177 d_data->brush = brush;
00178
00179 legendChanged();
00180 itemChanged();
00181 }
00182 }
00183
00188 const QBrush &QwtPlotHistogram::brush() const
00189 {
00190 return d_data->brush;
00191 }
00192
00207 void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol )
00208 {
00209 if ( symbol != d_data->symbol )
00210 {
00211 delete d_data->symbol;
00212 d_data->symbol = symbol;
00213
00214 legendChanged();
00215 itemChanged();
00216 }
00217 }
00218
00223 const QwtColumnSymbol *QwtPlotHistogram::symbol() const
00224 {
00225 return d_data->symbol;
00226 }
00227
00239 void QwtPlotHistogram::setBaseline( double value )
00240 {
00241 if ( d_data->baseline != value )
00242 {
00243 d_data->baseline = value;
00244 itemChanged();
00245 }
00246 }
00247
00252 double QwtPlotHistogram::baseline() const
00253 {
00254 return d_data->baseline;
00255 }
00256
00261 QRectF QwtPlotHistogram::boundingRect() const
00262 {
00263 QRectF rect = data()->boundingRect();
00264 if ( !rect.isValid() )
00265 return rect;
00266
00267 if ( orientation() == Qt::Horizontal )
00268 {
00269 rect = QRectF( rect.y(), rect.x(),
00270 rect.height(), rect.width() );
00271
00272 if ( rect.left() > d_data->baseline )
00273 rect.setLeft( d_data->baseline );
00274 else if ( rect.right() < d_data->baseline )
00275 rect.setRight( d_data->baseline );
00276 }
00277 else
00278 {
00279 if ( rect.bottom() < d_data->baseline )
00280 rect.setBottom( d_data->baseline );
00281 else if ( rect.top() > d_data->baseline )
00282 rect.setTop( d_data->baseline );
00283 }
00284
00285 return rect;
00286 }
00287
00289 int QwtPlotHistogram::rtti() const
00290 {
00291 return QwtPlotItem::Rtti_PlotHistogram;
00292 }
00293
00298 void QwtPlotHistogram::setSamples(
00299 const QVector<QwtIntervalSample> &samples )
00300 {
00301 setData( new QwtIntervalSeriesData( samples ) );
00302 }
00303
00314 void QwtPlotHistogram::setSamples(
00315 QwtSeriesData<QwtIntervalSample> *data )
00316 {
00317 setData( data );
00318 }
00319
00333 void QwtPlotHistogram::drawSeries( QPainter *painter,
00334 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00335 const QRectF &, int from, int to ) const
00336 {
00337 if ( !painter || dataSize() <= 0 )
00338 return;
00339
00340 if ( to < 0 )
00341 to = dataSize() - 1;
00342
00343 switch ( d_data->style )
00344 {
00345 case Outline:
00346 drawOutline( painter, xMap, yMap, from, to );
00347 break;
00348 case Lines:
00349 drawLines( painter, xMap, yMap, from, to );
00350 break;
00351 case Columns:
00352 drawColumns( painter, xMap, yMap, from, to );
00353 break;
00354 default:
00355 break;
00356 }
00357 }
00358
00373 void QwtPlotHistogram::drawOutline( QPainter *painter,
00374 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00375 int from, int to ) const
00376 {
00377 const bool doAlign = QwtPainter::roundingAlignment( painter );
00378
00379 double v0 = ( orientation() == Qt::Horizontal ) ?
00380 xMap.transform( baseline() ) : yMap.transform( baseline() );
00381 if ( doAlign )
00382 v0 = qRound( v0 );
00383
00384 QwtIntervalSample previous;
00385
00386 QPolygonF polygon;
00387 for ( int i = from; i <= to; i++ )
00388 {
00389 const QwtIntervalSample sample = this->sample( i );
00390
00391 if ( !sample.interval.isValid() )
00392 {
00393 flushPolygon( painter, v0, polygon );
00394 previous = sample;
00395 continue;
00396 }
00397
00398 if ( previous.interval.isValid() )
00399 {
00400 if ( !qwtIsCombinable( previous.interval, sample.interval ) )
00401 flushPolygon( painter, v0, polygon );
00402 }
00403
00404 if ( orientation() == Qt::Vertical )
00405 {
00406 double x1 = xMap.transform( sample.interval.minValue() );
00407 double x2 = xMap.transform( sample.interval.maxValue() );
00408 double y = yMap.transform( sample.value );
00409 if ( doAlign )
00410 {
00411 x1 = qRound( x1 );
00412 x2 = qRound( x2 );
00413 y = qRound( y );
00414 }
00415
00416 if ( polygon.size() == 0 )
00417 polygon += QPointF( x1, v0 );
00418
00419 polygon += QPointF( x1, y );
00420 polygon += QPointF( x2, y );
00421 }
00422 else
00423 {
00424 double y1 = yMap.transform( sample.interval.minValue() );
00425 double y2 = yMap.transform( sample.interval.maxValue() );
00426 double x = xMap.transform( sample.value );
00427 if ( doAlign )
00428 {
00429 y1 = qRound( y1 );
00430 y2 = qRound( y2 );
00431 x = qRound( x );
00432 }
00433
00434 if ( polygon.size() == 0 )
00435 polygon += QPointF( v0, y1 );
00436
00437 polygon += QPointF( x, y1 );
00438 polygon += QPointF( x, y2 );
00439 }
00440 previous = sample;
00441 }
00442
00443 flushPolygon( painter, v0, polygon );
00444 }
00445
00458 void QwtPlotHistogram::drawColumns( QPainter *painter,
00459 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00460 int from, int to ) const
00461 {
00462 painter->setPen( d_data->pen );
00463 painter->setBrush( d_data->brush );
00464
00465 const QwtSeriesData<QwtIntervalSample> *series = data();
00466
00467 for ( int i = from; i <= to; i++ )
00468 {
00469 const QwtIntervalSample sample = series->sample( i );
00470 if ( !sample.interval.isNull() )
00471 {
00472 const QwtColumnRect rect = columnRect( sample, xMap, yMap );
00473 drawColumn( painter, rect, sample );
00474 }
00475 }
00476 }
00477
00490 void QwtPlotHistogram::drawLines( QPainter *painter,
00491 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00492 int from, int to ) const
00493 {
00494 const bool doAlign = QwtPainter::roundingAlignment( painter );
00495
00496 painter->setPen( d_data->pen );
00497 painter->setBrush( Qt::NoBrush );
00498
00499 const QwtSeriesData<QwtIntervalSample> *series = data();
00500
00501 for ( int i = from; i <= to; i++ )
00502 {
00503 const QwtIntervalSample sample = series->sample( i );
00504 if ( !sample.interval.isNull() )
00505 {
00506 const QwtColumnRect rect = columnRect( sample, xMap, yMap );
00507
00508 QRectF r = rect.toRect();
00509 if ( doAlign )
00510 {
00511 r.setLeft( qRound( r.left() ) );
00512 r.setRight( qRound( r.right() ) );
00513 r.setTop( qRound( r.top() ) );
00514 r.setBottom( qRound( r.bottom() ) );
00515 }
00516
00517 switch ( rect.direction )
00518 {
00519 case QwtColumnRect::LeftToRight:
00520 {
00521 QwtPainter::drawLine( painter,
00522 r.topRight(), r.bottomRight() );
00523 break;
00524 }
00525 case QwtColumnRect::RightToLeft:
00526 {
00527 QwtPainter::drawLine( painter,
00528 r.topLeft(), r.bottomLeft() );
00529 break;
00530 }
00531 case QwtColumnRect::TopToBottom:
00532 {
00533 QwtPainter::drawLine( painter,
00534 r.bottomRight(), r.bottomLeft() );
00535 break;
00536 }
00537 case QwtColumnRect::BottomToTop:
00538 {
00539 QwtPainter::drawLine( painter,
00540 r.topRight(), r.topLeft() );
00541 break;
00542 }
00543 }
00544 }
00545 }
00546 }
00547
00549 void QwtPlotHistogram::flushPolygon( QPainter *painter,
00550 double baseLine, QPolygonF &polygon ) const
00551 {
00552 if ( polygon.size() == 0 )
00553 return;
00554
00555 if ( orientation() == Qt::Horizontal )
00556 polygon += QPointF( baseLine, polygon.last().y() );
00557 else
00558 polygon += QPointF( polygon.last().x(), baseLine );
00559
00560 if ( d_data->brush.style() != Qt::NoBrush )
00561 {
00562 painter->setPen( Qt::NoPen );
00563 painter->setBrush( d_data->brush );
00564
00565 if ( orientation() == Qt::Horizontal )
00566 {
00567 polygon += QPointF( polygon.last().x(), baseLine );
00568 polygon += QPointF( polygon.first().x(), baseLine );
00569 }
00570 else
00571 {
00572 polygon += QPointF( baseLine, polygon.last().y() );
00573 polygon += QPointF( baseLine, polygon.first().y() );
00574 }
00575
00576 QwtPainter::drawPolygon( painter, polygon );
00577
00578 polygon.pop_back();
00579 polygon.pop_back();
00580 }
00581 if ( d_data->pen.style() != Qt::NoPen )
00582 {
00583 painter->setBrush( Qt::NoBrush );
00584 painter->setPen( d_data->pen );
00585 QwtPainter::drawPolyline( painter, polygon );
00586 }
00587 polygon.clear();
00588 }
00589
00599 QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample,
00600 const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const
00601 {
00602 QwtColumnRect rect;
00603
00604 const QwtInterval &iv = sample.interval;
00605 if ( !iv.isValid() )
00606 return rect;
00607
00608 if ( orientation() == Qt::Horizontal )
00609 {
00610 const double x0 = xMap.transform( baseline() );
00611 const double x = xMap.transform( sample.value );
00612 const double y1 = yMap.transform( iv.minValue() );
00613 const double y2 = yMap.transform( iv.maxValue() );
00614
00615 rect.hInterval.setInterval( x0, x );
00616 rect.vInterval.setInterval( y1, y2, iv.borderFlags() );
00617 rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft :
00618 QwtColumnRect::LeftToRight;
00619 }
00620 else
00621 {
00622 const double x1 = xMap.transform( iv.minValue() );
00623 const double x2 = xMap.transform( iv.maxValue() );
00624 const double y0 = yMap.transform( baseline() );
00625 const double y = yMap.transform( sample.value );
00626
00627 rect.hInterval.setInterval( x1, x2, iv.borderFlags() );
00628 rect.vInterval.setInterval( y0, y );
00629 rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop :
00630 QwtColumnRect::TopToBottom;
00631 }
00632
00633 return rect;
00634 }
00635
00650 void QwtPlotHistogram::drawColumn( QPainter *painter,
00651 const QwtColumnRect &rect, const QwtIntervalSample &sample ) const
00652 {
00653 Q_UNUSED( sample );
00654
00655 if ( d_data->symbol &&
00656 ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) )
00657 {
00658 d_data->symbol->draw( painter, rect );
00659 }
00660 else
00661 {
00662 QRectF r = rect.toRect();
00663 if ( QwtPainter::roundingAlignment( painter ) )
00664 {
00665 r.setLeft( qRound( r.left() ) );
00666 r.setRight( qRound( r.right() ) );
00667 r.setTop( qRound( r.top() ) );
00668 r.setBottom( qRound( r.bottom() ) );
00669 }
00670
00671 QwtPainter::drawRect( painter, r );
00672 }
00673 }
00674
00685 QwtGraphic QwtPlotHistogram::legendIcon( int index,
00686 const QSizeF &size ) const
00687 {
00688 Q_UNUSED( index );
00689 return defaultIcon( d_data->brush, size );
00690 }