00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_plot_multi_barchart.h"
00011 #include "qwt_scale_map.h"
00012 #include "qwt_column_symbol.h"
00013 #include "qwt_painter.h"
00014 #include <qpainter.h>
00015 #include <qpalette.h>
00016 #include <qmap.h>
00017
00018 inline static bool qwtIsIncreasing(
00019 const QwtScaleMap &map, const QVector<double> &values )
00020 {
00021 bool isInverting = map.isInverting();
00022
00023 for ( int i = 0; i < values.size(); i++ )
00024 {
00025 const double y = values[ i ];
00026 if ( y != 0.0 )
00027 return ( map.isInverting() != ( y > 0.0 ) );
00028 }
00029
00030 return !isInverting;
00031 }
00032
00033 class QwtPlotMultiBarChart::PrivateData
00034 {
00035 public:
00036 PrivateData():
00037 style( QwtPlotMultiBarChart::Grouped )
00038 {
00039 }
00040
00041 QwtPlotMultiBarChart::ChartStyle style;
00042 QList<QwtText> barTitles;
00043 QMap<int, QwtColumnSymbol *> symbolMap;
00044 };
00045
00050 QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QwtText &title ):
00051 QwtPlotAbstractBarChart( title )
00052 {
00053 init();
00054 }
00055
00060 QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QString &title ):
00061 QwtPlotAbstractBarChart( QwtText( title ) )
00062 {
00063 init();
00064 }
00065
00067 QwtPlotMultiBarChart::~QwtPlotMultiBarChart()
00068 {
00069 resetSymbolMap();
00070 delete d_data;
00071 }
00072
00073 void QwtPlotMultiBarChart::init()
00074 {
00075 d_data = new PrivateData;
00076 setData( new QwtSetSeriesData() );
00077 }
00078
00080 int QwtPlotMultiBarChart::rtti() const
00081 {
00082 return QwtPlotItem::Rtti_PlotMultiBarChart;
00083 }
00084
00089 void QwtPlotMultiBarChart::setSamples(
00090 const QVector<QwtSetSample> &samples )
00091 {
00092 setData( new QwtSetSeriesData( samples ) );
00093 }
00094
00099 void QwtPlotMultiBarChart::setSamples(
00100 const QVector< QVector<double> > &samples )
00101 {
00102 QVector<QwtSetSample> s;
00103 s.reserve( samples.size() );
00104
00105 for ( int i = 0; i < samples.size(); i++ )
00106 s += QwtSetSample( i, samples[ i ] );
00107
00108 setData( new QwtSetSeriesData( s ) );
00109 }
00110
00121 void QwtPlotMultiBarChart::setSamples(
00122 QwtSeriesData<QwtSetSample> *data )
00123 {
00124 setData( data );
00125 }
00126
00136 void QwtPlotMultiBarChart::setBarTitles( const QList<QwtText> &titles )
00137 {
00138 d_data->barTitles = titles;
00139 itemChanged();
00140 }
00141
00146 QList<QwtText> QwtPlotMultiBarChart::barTitles() const
00147 {
00148 return d_data->barTitles;
00149 }
00150
00162 void QwtPlotMultiBarChart::setSymbol( int valueIndex, QwtColumnSymbol *symbol )
00163 {
00164 if ( valueIndex < 0 )
00165 return;
00166
00167 QMap<int, QwtColumnSymbol *>::iterator it =
00168 d_data->symbolMap.find(valueIndex);
00169 if ( it == d_data->symbolMap.end() )
00170 {
00171 if ( symbol != NULL )
00172 {
00173 d_data->symbolMap.insert( valueIndex, symbol );
00174
00175 legendChanged();
00176 itemChanged();
00177 }
00178 }
00179 else
00180 {
00181 if ( symbol != it.value() )
00182 {
00183 delete it.value();
00184
00185 if ( symbol == NULL )
00186 {
00187 d_data->symbolMap.remove( valueIndex );
00188 }
00189 else
00190 {
00191 it.value() = symbol;
00192 }
00193
00194 legendChanged();
00195 itemChanged();
00196 }
00197 }
00198 }
00199
00208 const QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex ) const
00209 {
00210 QMap<int, QwtColumnSymbol *>::const_iterator it =
00211 d_data->symbolMap.constFind( valueIndex );
00212
00213 return ( it == d_data->symbolMap.constEnd() ) ? NULL : it.value();
00214 }
00215
00224 QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex )
00225 {
00226 QMap<int, QwtColumnSymbol *>::const_iterator it =
00227 d_data->symbolMap.constFind( valueIndex );
00228
00229 return ( it == d_data->symbolMap.constEnd() ) ? NULL : it.value();
00230 }
00231
00235 void QwtPlotMultiBarChart::resetSymbolMap()
00236 {
00237 qDeleteAll( d_data->symbolMap );
00238 d_data->symbolMap.clear();
00239 }
00240
00261 QwtColumnSymbol *QwtPlotMultiBarChart::specialSymbol(
00262 int sampleIndex, int valueIndex ) const
00263 {
00264 Q_UNUSED( sampleIndex );
00265 Q_UNUSED( valueIndex );
00266
00267 return NULL;
00268 }
00269
00276 void QwtPlotMultiBarChart::setStyle( ChartStyle style )
00277 {
00278 if ( style != d_data->style )
00279 {
00280 d_data->style = style;
00281
00282 legendChanged();
00283 itemChanged();
00284 }
00285 }
00286
00291 QwtPlotMultiBarChart::ChartStyle QwtPlotMultiBarChart::style() const
00292 {
00293 return d_data->style;
00294 }
00295
00300 QRectF QwtPlotMultiBarChart::boundingRect() const
00301 {
00302 const size_t numSamples = dataSize();
00303
00304 if ( numSamples == 0 )
00305 return QwtPlotSeriesItem::boundingRect();
00306
00307 const double baseLine = baseline();
00308
00309 QRectF rect;
00310
00311 if ( d_data->style != QwtPlotMultiBarChart::Stacked )
00312 {
00313 rect = QwtPlotSeriesItem::boundingRect();
00314
00315 if ( rect.height() >= 0 )
00316 {
00317 if ( rect.bottom() < baseLine )
00318 rect.setBottom( baseLine );
00319 if ( rect.top() > baseLine )
00320 rect.setTop( baseLine );
00321 }
00322 }
00323 else
00324 {
00325 double xMin, xMax, yMin, yMax;
00326
00327 xMin = xMax = 0.0;
00328 yMin = yMax = baseLine;
00329
00330 const QwtSeriesData<QwtSetSample> *series = data();
00331
00332 for ( size_t i = 0; i < numSamples; i++ )
00333 {
00334 const QwtSetSample sample = series->sample( i );
00335 if ( i == 0 )
00336 {
00337 xMin = xMax = sample.value;
00338 }
00339 else
00340 {
00341 xMin = qMin( xMin, sample.value );
00342 xMax = qMax( xMax, sample.value );
00343 }
00344
00345 const double y = baseLine + sample.added();
00346
00347 yMin = qMin( yMin, y );
00348 yMax = qMax( yMax, y );
00349 }
00350 rect.setRect( xMin, yMin, xMax - xMin, yMax - yMin );
00351 }
00352
00353 if ( orientation() == Qt::Horizontal )
00354 rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
00355
00356 return rect;
00357 }
00358
00372 void QwtPlotMultiBarChart::drawSeries( QPainter *painter,
00373 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00374 const QRectF &canvasRect, int from, int to ) const
00375 {
00376 if ( to < 0 )
00377 to = dataSize() - 1;
00378
00379 if ( from < 0 )
00380 from = 0;
00381
00382 if ( from > to )
00383 return;
00384
00385
00386 const QRectF br = data()->boundingRect();
00387 const QwtInterval interval( br.left(), br.right() );
00388
00389 painter->save();
00390
00391 for ( int i = from; i <= to; i++ )
00392 {
00393 drawSample( painter, xMap, yMap,
00394 canvasRect, interval, i, sample( i ) );
00395 }
00396
00397 painter->restore();
00398 }
00399
00413 void QwtPlotMultiBarChart::drawSample( QPainter *painter,
00414 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00415 const QRectF &canvasRect, const QwtInterval &boundingInterval,
00416 int index, const QwtSetSample& sample ) const
00417 {
00418 if ( sample.set.size() <= 0 )
00419 return;
00420
00421 double sampleW;
00422
00423 if ( orientation() == Qt::Horizontal )
00424 {
00425 sampleW = sampleWidth( yMap, canvasRect.height(),
00426 boundingInterval.width(), sample.value );
00427 }
00428 else
00429 {
00430 sampleW = sampleWidth( xMap, canvasRect.width(),
00431 boundingInterval.width(), sample.value );
00432 }
00433
00434 if ( d_data->style == Stacked )
00435 {
00436 drawStackedBars( painter, xMap, yMap,
00437 canvasRect, index, sampleW, sample );
00438 }
00439 else
00440 {
00441 drawGroupedBars( painter, xMap, yMap,
00442 canvasRect, index, sampleW, sample );
00443 }
00444 }
00445
00459 void QwtPlotMultiBarChart::drawGroupedBars( QPainter *painter,
00460 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00461 const QRectF &canvasRect, int index, double sampleWidth,
00462 const QwtSetSample& sample ) const
00463 {
00464 Q_UNUSED( canvasRect );
00465
00466 const int numBars = sample.set.size();
00467 if ( numBars == 0 )
00468 return;
00469
00470 if ( orientation() == Qt::Vertical )
00471 {
00472 const double barWidth = sampleWidth / numBars;
00473
00474 const double y1 = yMap.transform( baseline() );
00475 const double x0 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
00476
00477 for ( int i = 0; i < numBars; i++ )
00478 {
00479 const double x1 = x0 + i * barWidth;
00480 const double x2 = x1 + barWidth;
00481
00482 const double y2 = yMap.transform( sample.set[i] );
00483
00484 QwtColumnRect barRect;
00485 barRect.direction = ( y1 < y2 ) ?
00486 QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
00487
00488 barRect.hInterval = QwtInterval( x1, x2 ).normalized();
00489 if ( i != 0 )
00490 barRect.hInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
00491
00492 barRect.vInterval = QwtInterval( y1, y2 ).normalized();
00493
00494 drawBar( painter, index, i, barRect );
00495 }
00496 }
00497 else
00498 {
00499 const double barHeight = sampleWidth / numBars;
00500
00501 const double x1 = xMap.transform( baseline() );
00502 const double y0 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
00503
00504 for ( int i = 0; i < numBars; i++ )
00505 {
00506 double y1 = y0 + i * barHeight;
00507 double y2 = y1 + barHeight;
00508
00509 double x2 = xMap.transform( sample.set[i] );
00510
00511 QwtColumnRect barRect;
00512 barRect.direction = x1 < x2 ?
00513 QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
00514
00515 barRect.hInterval = QwtInterval( x1, x2 ).normalized();
00516
00517 barRect.vInterval = QwtInterval( y1, y2 );
00518 if ( i != 0 )
00519 barRect.vInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
00520
00521 drawBar( painter, index, i, barRect );
00522 }
00523 }
00524 }
00525
00539 void QwtPlotMultiBarChart::drawStackedBars( QPainter *painter,
00540 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00541 const QRectF &canvasRect, int index,
00542 double sampleWidth, const QwtSetSample& sample ) const
00543 {
00544 Q_UNUSED( canvasRect );
00545
00546 const int numBars = sample.set.size();
00547 if ( numBars == 0 )
00548 return;
00549
00550 QwtInterval::BorderFlag borderFlags = QwtInterval::IncludeBorders;
00551
00552 if ( orientation() == Qt::Vertical )
00553 {
00554 const double x1 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
00555 const double x2 = x1 + sampleWidth;
00556
00557 const bool increasing = qwtIsIncreasing( yMap, sample.set );
00558
00559 QwtColumnRect bar;
00560 bar.direction = increasing ?
00561 QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
00562
00563 bar.hInterval = QwtInterval( x1, x2 ).normalized();
00564
00565 double sum = baseline();
00566
00567 const int numBars = sample.set.size();
00568 for ( int i = 0; i < numBars; i++ )
00569 {
00570 const double si = sample.set[ i ];
00571 if ( si == 0.0 )
00572 continue;
00573
00574 const double y1 = yMap.transform( sum );
00575 const double y2 = yMap.transform( sum + si );
00576
00577 if ( ( y2 > y1 ) != increasing )
00578 {
00579
00580 continue;
00581 }
00582
00583 bar.vInterval = QwtInterval( y1, y2 ).normalized();
00584 bar.vInterval.setBorderFlags( borderFlags );
00585
00586 drawBar( painter, index, i, bar );
00587
00588 sum += si;
00589
00590 if ( increasing )
00591 borderFlags = QwtInterval::ExcludeMinimum;
00592 else
00593 borderFlags = QwtInterval::ExcludeMaximum;
00594 }
00595 }
00596 else
00597 {
00598 const double y1 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
00599 const double y2 = y1 + sampleWidth;
00600
00601 const bool increasing = qwtIsIncreasing( xMap, sample.set );
00602
00603 QwtColumnRect bar;
00604 bar.direction = increasing ?
00605 QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
00606 bar.vInterval = QwtInterval( y1, y2 ).normalized();
00607
00608 double sum = baseline();
00609
00610 for ( int i = 0; i < sample.set.size(); i++ )
00611 {
00612 const double si = sample.set[ i ];
00613 if ( si == 0.0 )
00614 continue;
00615
00616 const double x1 = xMap.transform( sum );
00617 const double x2 = xMap.transform( sum + si );
00618
00619 if ( ( x2 > x1 ) != increasing )
00620 {
00621
00622 continue;
00623 }
00624
00625 bar.hInterval = QwtInterval( x1, x2 ).normalized();
00626 bar.hInterval.setBorderFlags( borderFlags );
00627
00628 drawBar( painter, index, i, bar );
00629
00630 sum += si;
00631
00632 if ( increasing )
00633 borderFlags = QwtInterval::ExcludeMinimum;
00634 else
00635 borderFlags = QwtInterval::ExcludeMaximum;
00636 }
00637 }
00638 }
00639
00651 void QwtPlotMultiBarChart::drawBar( QPainter *painter,
00652 int sampleIndex, int valueIndex, const QwtColumnRect &rect ) const
00653 {
00654 const QwtColumnSymbol *specialSym = NULL;
00655 if ( sampleIndex >= 0 )
00656 specialSym = specialSymbol( sampleIndex, valueIndex );
00657
00658 const QwtColumnSymbol *sym = specialSym;
00659 if ( sym == NULL )
00660 sym = symbol( valueIndex );
00661
00662 if ( sym )
00663 {
00664 sym->draw( painter, rect );
00665 }
00666 else
00667 {
00668
00669 QwtColumnSymbol sym( QwtColumnSymbol::Box );
00670 sym.setLineWidth( 1 );
00671 sym.setFrameStyle( QwtColumnSymbol::Plain );
00672 sym.draw( painter, rect );
00673 }
00674
00675 delete specialSym;
00676 }
00677
00686 QList<QwtLegendData> QwtPlotMultiBarChart::legendData() const
00687 {
00688 QList<QwtLegendData> list;
00689 #if QT_VERSION >= 0x040700
00690 list.reserve( d_data->barTitles.size() );
00691 #endif
00692
00693 for ( int i = 0; i < d_data->barTitles.size(); i++ )
00694 {
00695 QwtLegendData data;
00696
00697 QVariant titleValue;
00698 qVariantSetValue( titleValue, d_data->barTitles[i] );
00699 data.setValue( QwtLegendData::TitleRole, titleValue );
00700
00701 if ( !legendIconSize().isEmpty() )
00702 {
00703 QVariant iconValue;
00704 qVariantSetValue( iconValue,
00705 legendIcon( i, legendIconSize() ) );
00706
00707 data.setValue( QwtLegendData::IconRole, iconValue );
00708 }
00709
00710 list += data;
00711 }
00712
00713 return list;
00714 }
00715
00725 QwtGraphic QwtPlotMultiBarChart::legendIcon( int index,
00726 const QSizeF &size ) const
00727 {
00728 QwtColumnRect column;
00729 column.hInterval = QwtInterval( 0.0, size.width() - 1.0 );
00730 column.vInterval = QwtInterval( 0.0, size.height() - 1.0 );
00731
00732 QwtGraphic icon;
00733 icon.setDefaultSize( size );
00734 icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
00735
00736 QPainter painter( &icon );
00737 painter.setRenderHint( QPainter::Antialiasing,
00738 testRenderHint( QwtPlotItem::RenderAntialiased ) );
00739
00740 drawBar( &painter, -1, index, column );
00741
00742 return icon;
00743 }
00744