File indexing completed on 2024-06-09 04:18:28

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "KChartLeveyJenningsDiagram.h"
0010 #include "KChartLeveyJenningsDiagram_p.h"
0011 
0012 #include "KChartChart.h"
0013 #include "KChartTextAttributes.h"
0014 #include "KChartAbstractGrid.h"
0015 #include "KChartPainterSaver_p.h"
0016 
0017 #include <QDateTime>
0018 #include <QFontMetrics>
0019 #include <QPainter>
0020 #include <QSvgRenderer>
0021 #include <QVector>
0022 
0023 using namespace KChart;
0024 using namespace std;
0025 
0026 LeveyJenningsDiagram::Private::Private()
0027 {
0028 }
0029 
0030 LeveyJenningsDiagram::Private::~Private() {}
0031 
0032 
0033 #define d d_func()
0034 
0035 
0036 LeveyJenningsDiagram::LeveyJenningsDiagram( QWidget* parent, LeveyJenningsCoordinatePlane* plane )
0037     : LineDiagram( new Private(), parent, plane )
0038 {
0039     init();
0040 }
0041 
0042 void LeveyJenningsDiagram::init()
0043 {
0044     d->lotChangedPosition = Qt::AlignTop;
0045     d->fluidicsPackChangedPosition = Qt::AlignBottom;
0046     d->sensorChangedPosition = Qt::AlignBottom;
0047 
0048     d->scanLinePen = QPen( Qt::blue );
0049     setPen( d->scanLinePen );
0050 
0051     d->expectedMeanValue = 0.0;
0052     d->expectedStandardDeviation = 0.0;
0053 
0054     d->diagram = this;
0055 
0056     d->icons[ LotChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_black.svg" );
0057     d->icons[ SensorChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_red.svg" );
0058     d->icons[ FluidicsPackChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_blue.svg" );
0059     d->icons[ OkDataPoint ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/circle_blue.svg" );
0060     d->icons[ NotOkDataPoint ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/circle_blue_red.svg" );
0061 
0062     setSelectionMode( QAbstractItemView::SingleSelection );
0063 }
0064 
0065 LeveyJenningsDiagram::~LeveyJenningsDiagram()
0066 {
0067 }
0068 
0069 LineDiagram * LeveyJenningsDiagram::clone() const
0070 {
0071     LeveyJenningsDiagram* newDiagram = new LeveyJenningsDiagram( new Private( *d ) );
0072     return newDiagram;
0073 }
0074 
0075 bool LeveyJenningsDiagram::compare( const LeveyJenningsDiagram* other ) const
0076 {
0077     if ( other == this ) return true;
0078     if ( ! other ) {
0079         return false;
0080     }
0081     /*
0082     qDebug() <<"\n             LineDiagram::compare():";
0083             // compare own properties
0084     qDebug() << (type() == other->type());
0085     */
0086     return  // compare the base class
0087             ( static_cast<const LineDiagram*>(this)->compare( other ) );
0088 }
0089 
0090 void LeveyJenningsDiagram::setLotChangedSymbolPosition( Qt::Alignment pos )
0091 {
0092     if ( d->lotChangedPosition == pos )
0093         return;
0094 
0095     d->lotChangedPosition = pos;
0096     update();
0097 }
0098 
0099 Qt::Alignment LeveyJenningsDiagram::lotChangedSymbolPosition() const
0100 {
0101     return d->lotChangedPosition;
0102 }
0103 
0104 void LeveyJenningsDiagram::setFluidicsPackChangedSymbolPosition( Qt::Alignment pos )
0105 {
0106     if ( d->fluidicsPackChangedPosition == pos )
0107         return;
0108 
0109     d->fluidicsPackChangedPosition = pos;
0110     update();
0111 }
0112 
0113 Qt::Alignment LeveyJenningsDiagram::fluidicsPackChangedSymbolPosition() const
0114 {
0115     return d->fluidicsPackChangedPosition;
0116 }
0117 
0118 void LeveyJenningsDiagram::setSensorChangedSymbolPosition( Qt::Alignment pos )
0119 {
0120     if ( d->sensorChangedPosition == pos )
0121         return;
0122 
0123     d->sensorChangedPosition = pos;
0124     update();
0125 }
0126 
0127 Qt::Alignment LeveyJenningsDiagram::sensorChangedSymbolPosition() const
0128 {
0129     return d->sensorChangedPosition;
0130 }
0131 
0132 void LeveyJenningsDiagram::setFluidicsPackChanges( const QVector< QDateTime >& changes )
0133 {
0134     if ( d->fluidicsPackChanges == changes )
0135         return;
0136 
0137     d->fluidicsPackChanges = changes;
0138     update();
0139 }
0140 
0141 QVector< QDateTime > LeveyJenningsDiagram::fluidicsPackChanges() const
0142 {
0143     return d->fluidicsPackChanges;
0144 }
0145 
0146 void LeveyJenningsDiagram::setSensorChanges( const QVector< QDateTime >& changes )
0147 {
0148     if ( d->sensorChanges == changes )
0149         return;
0150 
0151     d->sensorChanges = changes;
0152     update();
0153 }
0154 
0155 void LeveyJenningsDiagram::setScanLinePen( const QPen& pen )
0156 {
0157     if ( d->scanLinePen == pen )
0158         return;
0159 
0160     d->scanLinePen = pen;
0161     update();
0162 }
0163 
0164 QPen LeveyJenningsDiagram::scanLinePen() const
0165 {
0166     return d->scanLinePen;
0167 }
0168 
0169 QString LeveyJenningsDiagram::symbol( Symbol symbol ) const
0170 {
0171     return d->icons[ symbol ];
0172 }
0173 
0174 void LeveyJenningsDiagram::setSymbol( Symbol symbol, const QString& filename )
0175 {
0176     if ( d->icons[ symbol ] == filename )
0177         return;
0178 
0179     delete d->iconRenderer[ symbol ];
0180     d->iconRenderer[ symbol ] = nullptr;
0181 
0182     d->icons[ symbol ] = filename;
0183 
0184     update();
0185 }
0186 
0187 QVector< QDateTime > LeveyJenningsDiagram::sensorChanges() const
0188 {
0189     return d->sensorChanges;
0190 }
0191 
0192 void LeveyJenningsDiagram::setExpectedMeanValue( float meanValue )
0193 {
0194     if ( d->expectedMeanValue == meanValue )
0195         return;
0196 
0197     d->expectedMeanValue = meanValue;
0198     d->setYAxisRange();
0199     update();
0200 }
0201 
0202 float LeveyJenningsDiagram::expectedMeanValue() const
0203 {
0204     return d->expectedMeanValue;
0205 }
0206 
0207 void LeveyJenningsDiagram::setExpectedStandardDeviation( float sd )
0208 {
0209     if ( d->expectedStandardDeviation == sd )
0210         return;
0211 
0212     d->expectedStandardDeviation = sd;
0213     d->setYAxisRange();
0214     update();
0215 }
0216 
0217 float LeveyJenningsDiagram::expectedStandardDeviation() const
0218 {
0219     return d->expectedStandardDeviation;
0220 }
0221 
0222 float LeveyJenningsDiagram::calculatedMeanValue() const
0223 {
0224     return d->calculatedMeanValue;
0225 }
0226 
0227 float LeveyJenningsDiagram::calculatedStandardDeviation() const
0228 {
0229     return d->calculatedStandardDeviation;
0230 }
0231 
0232 void LeveyJenningsDiagram::setModel( QAbstractItemModel* model )
0233 {
0234     if ( this->model() != nullptr )
0235     {
0236         disconnect( this->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0237                                    this, SLOT(calculateMeanAndStandardDeviation()) );
0238         disconnect( this->model(), SIGNAL(rowsInserted(QModelIndex,int,int)),
0239                                    this, SLOT(calculateMeanAndStandardDeviation()) );
0240         disconnect( this->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
0241                                    this, SLOT(calculateMeanAndStandardDeviation()) );
0242         disconnect( this->model(), SIGNAL(columnsInserted(QModelIndex,int,int)),
0243                                    this, SLOT(calculateMeanAndStandardDeviation()) );
0244         disconnect( this->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)),
0245                                    this, SLOT(calculateMeanAndStandardDeviation()) );
0246         disconnect( this->model(), SIGNAL(modelReset()),
0247                                    this, SLOT(calculateMeanAndStandardDeviation()) );
0248         disconnect( this->model(), SIGNAL(layoutChanged()),
0249                                    this, SLOT(calculateMeanAndStandardDeviation()) );
0250     }
0251     LineDiagram::setModel( model );
0252     if ( this->model() != nullptr )
0253     {
0254         connect( this->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0255                                 this, SLOT(calculateMeanAndStandardDeviation()) );
0256         connect( this->model(), SIGNAL(rowsInserted(QModelIndex,int,int)),
0257                                 this, SLOT(calculateMeanAndStandardDeviation()) );
0258         connect( this->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
0259                                 this, SLOT(calculateMeanAndStandardDeviation()) );
0260         connect( this->model(), SIGNAL(columnsInserted(QModelIndex,int,int)),
0261                                 this, SLOT(calculateMeanAndStandardDeviation()) );
0262         connect( this->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)),
0263                                 this, SLOT(calculateMeanAndStandardDeviation()) );
0264         connect( this->model(), SIGNAL(modelReset()),
0265                                 this, SLOT(calculateMeanAndStandardDeviation()) );
0266         connect( this->model(), SIGNAL(layoutChanged()),
0267                                 this, SLOT(calculateMeanAndStandardDeviation()) );
0268 
0269         calculateMeanAndStandardDeviation();
0270     }
0271 }
0272 
0273 // TODO: This is the 'easy' solution
0274 // evaluate whether this is enough or we need some better one or even boost here
0275 void LeveyJenningsDiagram::calculateMeanAndStandardDeviation() const
0276 {
0277     QVector< qreal > values;
0278     // first fetch all values
0279     const QAbstractItemModel& m = *model();
0280     const int rowCount = m.rowCount( rootIndex() );
0281 
0282     for ( int row = 0; row < rowCount; ++row )
0283     {
0284         const QVariant var = m.data( m.index( row, 1, rootIndex() ) );
0285         if ( !var.isValid() )
0286             continue;
0287         const qreal value = var.toReal();
0288         if ( ISNAN( value ) )
0289             continue;
0290         values << value;
0291     }
0292 
0293     qreal sum = 0.0;
0294     qreal sumSquares = 0.0;
0295     for ( qreal value : qAsConst(values) )
0296     {
0297         sum += value;
0298         sumSquares += value * value;
0299     }
0300 
0301     const int N = values.count();
0302 
0303     d->calculatedMeanValue = sum / N;
0304     d->calculatedStandardDeviation = sqrt( ( static_cast< qreal >( N ) * sumSquares - sum * sum ) / ( N * ( N - 1 ) ) );
0305 }
0306 
0307 // calculates the largest QDate not greater than \a dt.
0308 static QDate floorDay( const QDateTime& dt )
0309 {
0310     return dt.date();
0311 }
0312 
0313 // calculates the smallest QDate not less than \a dt.
0314 static QDate ceilDay( const QDateTime& dt )
0315 {
0316     QDate result = dt.date();
0317 
0318     if ( QDateTime( result, QTime() ) < dt )
0319         result = result.addDays( 1 );
0320 
0321     return result;
0322 }
0323 
0324 // calculates the largest QDateTime like xx:00 not greater than \a dt.
0325 static QDateTime floorHour( const QDateTime& dt )
0326 {
0327     return QDateTime( dt.date(), QTime( dt.time().hour(), 0 ) );
0328 }
0329 
0330 // calculates the smallest QDateTime like xx:00 not less than \a dt.
0331 static QDateTime ceilHour( const QDateTime& dt )
0332 {
0333     QDateTime result( dt.date(), QTime( dt.time().hour(), 0 ) );
0334 
0335     if ( result < dt )
0336         result = result.addSecs( 3600 );
0337 
0338     return result;
0339 }
0340 
0341 const QPair<QPointF, QPointF> LeveyJenningsDiagram::calculateDataBoundaries() const
0342 {
0343     const qreal yMin = d->expectedMeanValue - 4 * d->expectedStandardDeviation;
0344     const qreal yMax = d->expectedMeanValue + 4 * d->expectedStandardDeviation;
0345 
0346     d->setYAxisRange();
0347 
0348     // rounded down/up to the prev/next midnight (at least that's the default)
0349     const QPair< QDateTime, QDateTime > range = timeRange();
0350     const unsigned int minTime = range.first.toSecsSinceEpoch();
0351     const unsigned int maxTime = range.second.toSecsSinceEpoch();
0352 
0353     const qreal xMin = minTime / static_cast< qreal >( 24 * 60 * 60 );
0354     const qreal xMax = maxTime / static_cast< qreal >( 24 * 60 * 60 ) - xMin;
0355 
0356     const QPointF bottomLeft( QPointF( 0, yMin ) );
0357     const QPointF topRight( QPointF( xMax, yMax ) );
0358 
0359     return QPair< QPointF, QPointF >( bottomLeft, topRight );
0360 }
0361 
0362 QPair< QDateTime, QDateTime > LeveyJenningsDiagram::timeRange() const
0363 {
0364     if ( d->timeRange != QPair< QDateTime, QDateTime >() )
0365         return d->timeRange;
0366 
0367     const QAbstractItemModel& m = *model();
0368     const int rowCount = m.rowCount( rootIndex() );
0369 
0370     const QDateTime begin = m.data( m.index( 0, 3, rootIndex() ) ).toDateTime();
0371     const QDateTime end = m.data( m.index( rowCount - 1, 3, rootIndex() ) ).toDateTime();
0372 
0373     if ( begin.secsTo( end ) > 86400 )
0374     {
0375         // if begin to end is more than 24h
0376         // round down/up to the prev/next midnight
0377         const QDate min = floorDay( begin );
0378         const QDate max = ceilDay( end );
0379         return QPair< QDateTime, QDateTime >( QDateTime( min, QTime() ), QDateTime( max, QTime() ) );
0380     }
0381     else if ( begin.secsTo( end ) > 3600 )
0382     {
0383         // more than 1h: rond down up to the prex/next hour
0384         // if begin to end is more than 24h
0385         const QDateTime min = floorHour( begin );
0386         const QDateTime max = ceilHour( end );
0387         return QPair< QDateTime, QDateTime >( min, max );
0388     }
0389     return QPair< QDateTime, QDateTime >( begin, end );
0390 }
0391 
0392 void LeveyJenningsDiagram::setTimeRange( const QPair< QDateTime, QDateTime >& timeRange )
0393 {
0394     if ( d->timeRange == timeRange )
0395         return;
0396 
0397     d->timeRange = timeRange;
0398     update();
0399 }
0400 
0401 void LeveyJenningsDiagram::drawChanges( PaintContext* ctx )
0402 {
0403     const unsigned int minTime = timeRange().first.toSecsSinceEpoch();
0404 
0405     for ( const QDateTime& dt : qAsConst(d->fluidicsPackChanges) )
0406     {
0407         const qreal xValue = ( dt.toSecsSinceEpoch() - minTime ) / static_cast< qreal >( 24 * 60 * 60 );
0408         const QPointF point( xValue, 0.0 );
0409         drawFluidicsPackChangedSymbol( ctx, point );
0410     }
0411 
0412     for ( const QDateTime& dt : qAsConst(d->sensorChanges) )
0413     {
0414         const qreal xValue = ( dt.toSecsSinceEpoch() - minTime ) / static_cast< qreal >( 24 * 60 * 60 );
0415         const QPointF point( xValue, 0.0 );
0416         drawSensorChangedSymbol( ctx, point );
0417     }
0418 }
0419 
0420 void LeveyJenningsDiagram::paint( PaintContext* ctx )
0421 {
0422     d->reverseMapper.clear();
0423 
0424     // note: Not having any data model assigned is no bug
0425     //       but we can not draw a diagram then either.
0426     if ( !checkInvariants( true ) ) return;
0427     if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return;
0428 
0429     QPainter* const painter = ctx->painter();
0430     const PainterSaver p( painter );
0431     if ( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) < 4 )
0432         return; // nothing to paint for us
0433 
0434     AbstractCoordinatePlane* const plane = ctx->coordinatePlane();
0435     ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( painter ) );
0436 
0437     const QAbstractItemModel& m = *model();
0438     const int rowCount = m.rowCount( rootIndex() );
0439 
0440     const unsigned int minTime = timeRange().first.toSecsSinceEpoch();
0441 
0442     painter->setRenderHint( QPainter::Antialiasing, true );
0443 
0444     int prevLot = -1;
0445     QPointF prevPoint;
0446     bool hadMissingValue = false;
0447 
0448     for ( int row = 0; row < rowCount; ++row )
0449     {
0450         const QModelIndex lotIndex = m.index( row, 0, rootIndex() );
0451         const QModelIndex valueIndex = m.index( row, 1, rootIndex() );
0452         const QModelIndex okIndex = m.index( row, 2, rootIndex() );
0453         const QModelIndex timeIndex = m.index( row, 3, rootIndex() );
0454         const QModelIndex expectedMeanIndex = m.index( row, 4, rootIndex() );
0455         const QModelIndex expectedSDIndex = m.index( row, 5, rootIndex() );
0456 
0457         painter->setPen( pen( lotIndex ) );
0458 
0459         QVariant vValue = m.data( valueIndex );
0460         qreal value = vValue.toReal();
0461         const int lot = m.data( lotIndex ).toInt();
0462         const bool ok = m.data( okIndex ).toBool();
0463         const QDateTime time = m.data( timeIndex ).toDateTime();
0464         const qreal xValue = ( time.toSecsSinceEpoch() - minTime ) / static_cast< qreal >( 24 * 60 * 60 );
0465 
0466         QVariant vExpectedMean = m.data( expectedMeanIndex );
0467         const qreal expectedMean = vExpectedMean.toReal();
0468         QVariant vExpectedSD = m.data( expectedSDIndex );
0469         const qreal expectedSD = vExpectedSD.toReal();
0470 
0471         QPointF point = ctx->coordinatePlane()->translate( QPointF( xValue, value ) );
0472 
0473         if ( vValue.isNull() )
0474         {
0475             hadMissingValue = true;
0476         }
0477         else
0478         {
0479             if ( !vExpectedMean.isNull() && !vExpectedSD.isNull() )
0480             {
0481                 // this calculates the 'logical' value relative to the expected mean and SD of this point
0482                 value -= expectedMean;
0483                 value /= expectedSD;
0484                 value *= d->expectedStandardDeviation;
0485                 value += d->expectedMeanValue;
0486                 point = ctx->coordinatePlane()->translate( QPointF( xValue, value ) );
0487             }
0488 
0489             if ( prevLot == lot )
0490             {
0491                 const QPen pen = painter->pen();
0492                 QPen newPen = pen;
0493 
0494                 if ( hadMissingValue )
0495                 {
0496                     newPen.setDashPattern( QVector< qreal >() << 4.0 << 4.0 );
0497                 }
0498 
0499                 painter->setPen( newPen );
0500                 painter->drawLine( prevPoint, point );
0501                 painter->setPen( pen );
0502                 // d->reverseMapper.addLine( valueIndex.row(), valueIndex.column(), prevPoint, point );
0503             }
0504             else if ( row > 0 )
0505             {
0506                 drawLotChangeSymbol( ctx, QPointF( xValue, value ) );
0507             }
0508 
0509             if ( value <= d->expectedMeanValue + 4 * d->expectedStandardDeviation &&
0510                 value >= d->expectedMeanValue - 4 * d->expectedStandardDeviation )
0511             {
0512                 const QPointF location( xValue, value );
0513                 drawDataPointSymbol( ctx, location, ok );
0514                 d->reverseMapper.addCircle( valueIndex.row(),
0515                                             valueIndex.column(),
0516                                             ctx->coordinatePlane()->translate( location ),
0517                                             iconRect().size() );
0518             }
0519             prevLot = lot;
0520             prevPoint = point;
0521             hadMissingValue = false;
0522         }
0523 
0524         const QModelIndex current = selectionModel()->currentIndex();
0525         if ( selectionModel()->rowIntersectsSelection( lotIndex.row(), lotIndex.parent() ) || current.sibling( current.row(), 0 ) == lotIndex )
0526         {
0527             const QPen pen = ctx->painter()->pen();
0528             painter->setPen( d->scanLinePen );
0529             painter->drawLine( ctx->coordinatePlane()->translate( QPointF( xValue, d->expectedMeanValue - 4 *
0530                                                                                    d->expectedStandardDeviation ) ),
0531                                ctx->coordinatePlane()->translate( QPointF( xValue, d->expectedMeanValue + 4 *
0532                                                                                    d->expectedStandardDeviation ) ) );
0533             painter->setPen( pen );
0534         }
0535     }
0536 
0537     drawChanges( ctx );
0538 
0539     ctx->setCoordinatePlane( plane );
0540 }
0541 
0542 void LeveyJenningsDiagram::drawDataPointSymbol( PaintContext* ctx, const QPointF& pos, bool ok )
0543 {
0544     const Symbol type = ok ? OkDataPoint : NotOkDataPoint;
0545 
0546     QPainter* const painter = ctx->painter();
0547     const PainterSaver ps( painter );
0548     const QPointF transPos = ctx->coordinatePlane()->translate( pos ).toPoint();
0549     painter->translate( transPos );
0550 
0551     painter->setClipping( false );
0552     iconRenderer( type )->render( painter, iconRect() );
0553 }
0554 
0555 void LeveyJenningsDiagram::drawLotChangeSymbol( PaintContext* ctx, const QPointF& pos )
0556 {
0557     const QPointF transPos = ctx->coordinatePlane()->translate(
0558         QPointF( pos.x(), d->lotChangedPosition & Qt::AlignTop ? d->expectedMeanValue +
0559                                                                  4 * d->expectedStandardDeviation
0560                                                                : d->expectedMeanValue -
0561                                                                  4 * d->expectedStandardDeviation ) );
0562 
0563 
0564     QPainter* const painter = ctx->painter();
0565     const PainterSaver ps( painter );
0566     painter->setClipping( false );
0567     painter->translate( transPos );
0568     iconRenderer( LotChanged )->render( painter, iconRect() );
0569 }
0570 
0571 void LeveyJenningsDiagram::drawSensorChangedSymbol( PaintContext* ctx, const QPointF& pos )
0572 {
0573     const QPointF transPos = ctx->coordinatePlane()->translate(
0574         QPointF( pos.x(), d->sensorChangedPosition & Qt::AlignTop ? d->expectedMeanValue +
0575                                                                     4 * d->expectedStandardDeviation
0576                                                                   : d->expectedMeanValue -
0577                                                                     4 * d->expectedStandardDeviation ) );
0578 
0579     QPainter* const painter = ctx->painter();
0580     const PainterSaver ps( painter );
0581     painter->setClipping( false );
0582     painter->translate( transPos );
0583     iconRenderer( SensorChanged )->render( painter, iconRect() );
0584 }
0585 
0586 void LeveyJenningsDiagram::drawFluidicsPackChangedSymbol( PaintContext* ctx, const QPointF& pos )
0587 {
0588     const QPointF transPos = ctx->coordinatePlane()->translate(
0589         QPointF( pos.x(), d->fluidicsPackChangedPosition & Qt::AlignTop ? d->expectedMeanValue +
0590                                                                           4 * d->expectedStandardDeviation
0591                                                                         : d->expectedMeanValue -
0592                                                                           4 * d->expectedStandardDeviation ) );
0593 
0594     QPainter* const painter = ctx->painter();
0595     const PainterSaver ps( painter );
0596     painter->setClipping( false );
0597     painter->translate( transPos );
0598     iconRenderer( FluidicsPackChanged )->render( painter, iconRect() );
0599 }
0600 
0601 QRectF LeveyJenningsDiagram::iconRect() const
0602 {
0603     const Measure m( 12.5, KChartEnums::MeasureCalculationModeAuto, KChartEnums::MeasureOrientationAuto );
0604     TextAttributes test;
0605     test.setFontSize( m );
0606     const QFontMetrics fm( test.calculatedFont( coordinatePlane()->parent(), KChartEnums::MeasureOrientationAuto ) );
0607     const qreal height = fm.height() / 1.2;
0608     return QRectF( -height / 2.0, -height / 2.0, height, height );
0609 }
0610 
0611 QSvgRenderer* LeveyJenningsDiagram::iconRenderer( Symbol symbol )
0612 {
0613     if ( d->iconRenderer[ symbol ] == nullptr )
0614         d->iconRenderer[ symbol ] = new QSvgRenderer( d->icons[ symbol ], this );
0615 
0616     return d->iconRenderer[ symbol ];
0617 }