9#include "KChartPlotterDiagramCompressor.h"
11#include "KChartPlotterDiagramCompressor_p.h"
12#include "KChartMath_p.h"
18qreal calculateSlope(
const PlotterDiagramCompressor::DataPoint &lhs,
const PlotterDiagramCompressor::DataPoint & rhs )
20 return ( rhs.value - lhs.value ) / ( rhs.key - lhs.key );
23PlotterDiagramCompressor::Iterator::Iterator(
int dataSet, PlotterDiagramCompressor *parent )
26 , m_dataset( dataSet )
32 if ( parent->rowCount() > m_dataset && parent->rowCount() > 0 )
34 m_buffer.append( parent->data( CachePosition( m_index, m_dataset ) ) );
44PlotterDiagramCompressor::Iterator::Iterator(
int dataSet, PlotterDiagramCompressor *parent,
QVector< DataPoint > buffer )
48 , m_dataset( dataSet )
51 , m_timeOfCreation(
QDateTime::currentDateTime() )
61 if ( parent->datasetCount() > m_dataset && parent->rowCount() > 0 && m_buffer.isEmpty() )
63 m_buffer.append( parent->data( CachePosition( m_index, m_dataset ) ) );
69PlotterDiagramCompressor::Iterator::~Iterator()
73 if ( m_parent.
data()->d->m_timeOfLastInvalidation < m_timeOfCreation )
74 m_parent.
data()->d->m_bufferlist[ m_dataset ] = m_buffer;
78bool PlotterDiagramCompressor::Iterator::isValid()
const
80 if ( m_parent ==
nullptr )
82 return m_dataset >= 0 && m_index >= 0 && m_parent.data()->rowCount() > m_index;
248void PlotterDiagramCompressor::Iterator::handleSlopeForward(
const DataPoint &dp )
250 PlotterDiagramCompressor*
parent = m_parent.data();
251 const qreal mergedist =
parent->d->m_maxSlopeRadius;
255 PlotterDiagramCompressor::DataPoint newdp = dp;
256 PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
257 if ( m_bufferIndex > 1 )
261 oldSlope = calculateSlope(
parent->data( CachePosition( m_index - 2, m_dataset ) ) ,
parent->data( CachePosition( m_index - 1, m_dataset ) ) );
262 newSlope = calculateSlope(
parent->data( CachePosition( m_index - 1, m_dataset ) ), newdp );
263 qreal accumulatedDist = qAbs( newSlope - oldSlope );
264 qreal olddist = accumulatedDist;
267 while ( accumulatedDist < mergedist )
270 if ( m_index >= m_parent.data()->rowCount() )
273 if ( m_buffer.last() !=
parent->data( CachePosition(
parent->rowCount() -1, m_dataset ) ) )
274 m_index =
parent->rowCount();
279 newdp =
parent->data( CachePosition( m_index, m_dataset ) );
280 newSlope = calculateSlope( olddp, newdp );
281 newdist = qAbs( newSlope - oldSlope );
282 if ( olddist == newdist )
291 accumulatedDist += newdist;
294 m_buffer.append( newdp );
297 m_buffer.append( dp );
300PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator++()
302 PlotterDiagramCompressor*
parent = m_parent.data();
304 const int count =
parent->rowCount();
311 if ( m_index >= count || ( !m_rebuffer && m_bufferIndex == m_buffer.count() ) )
313 if ( m_bufferIndex == m_buffer.count() )
315 if ( m_buffer.last() !=
parent->data( CachePosition(
parent->rowCount() -1, m_dataset ) ) )
316 m_index =
parent->rowCount();
325 if ( m_bufferIndex == m_buffer.count() && m_index >= 0 && m_rebuffer )
327 PlotterDiagramCompressor::DataPoint dp =
parent->data( CachePosition( m_index, m_dataset ) );
330 if (
parent->d->m_mode == PlotterDiagramCompressor::SLOPE )
331 handleSlopeForward( dp );
341PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::Iterator::operator++(
int )
343 Iterator result = *
this;
348PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator += (
int value )
350 for (
int index = m_index; index + value != m_index; ++( *this ) ) {};
354PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator--()
361PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::Iterator::operator--(
int )
363 Iterator result = *
this;
368PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator-=(
int value )
374PlotterDiagramCompressor::DataPoint PlotterDiagramCompressor::Iterator::operator*()
377 return PlotterDiagramCompressor::DataPoint();
378 Q_ASSERT( m_parent );
379 if ( m_index == m_parent.data()->rowCount() )
380 return m_parent.data()->data( CachePosition( m_parent.data()->rowCount() - 1 , m_dataset ) );
381 return m_buffer[ m_bufferIndex ];
384bool PlotterDiagramCompressor::Iterator::operator==(
const PlotterDiagramCompressor::Iterator &other )
const
386 return m_parent.data() == other.m_parent.
data() && m_index == other.m_index && m_dataset == other.m_dataset;
389bool PlotterDiagramCompressor::Iterator::operator!=(
const PlotterDiagramCompressor::Iterator &other )
const
391 return ! ( *
this == other );
394void PlotterDiagramCompressor::Iterator::invalidate()
399PlotterDiagramCompressor::Private::Private( PlotterDiagramCompressor *
parent )
402 , m_mergeRadius( 0.1 )
403 , m_maxSlopeRadius( 0.1 )
404 , m_boundary( qMakePair(
QPointF( std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN() )
405 ,
QPointF( std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN() ) ) )
406 , m_forcedXBoundaries( qMakePair( std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN() ) )
407 , m_forcedYBoundaries( qMakePair( std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN() ) )
408 , m_mode( PlotterDiagramCompressor::SLOPE )
413void PlotterDiagramCompressor::Private::setModelToZero()
418inline bool inBoundary(
const QPair< qreal, qreal > &bounds, qreal value )
420 return bounds.first <= value && value <= bounds.second;
423bool PlotterDiagramCompressor::Private::inBoundaries(
Qt::Orientation orient,
const PlotterDiagramCompressor::DataPoint &dp )
const
427 return inBoundary( m_forcedYBoundaries, dp.value );
431 return inBoundary( m_forcedXBoundaries, dp.key );
439void PlotterDiagramCompressor::Private::rowsInserted(
const QModelIndex& ,
int start,
int end )
442 if ( m_bufferlist.count() > 0 && !m_bufferlist[ 0 ].isEmpty() &&
start < m_bufferlist[ 0 ].count() )
444 calculateDataBoundaries();
449 qreal minX = std::numeric_limits< qreal >::max();
450 qreal minY = std::numeric_limits< qreal >::max();
451 qreal maxX = std::numeric_limits< qreal >::min();
452 qreal maxY = std::numeric_limits< qreal >::min();
453 for (
int dataset = 0; dataset < m_bufferlist.size(); ++dataset )
455 PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last();
459 PlotterDiagramCompressor::DataPoint newdp = m_parent->data( CachePosition(
start, dataset ) );
460 PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
461 const int datacount = m_bufferlist[ dataset ].count();
462 if ( m_mode != PlotterDiagramCompressor::DISTANCE && m_bufferlist[ dataset ].count() > 1 )
464 oldSlope = calculateSlope( m_bufferlist[ dataset ][ datacount - 2 ], m_bufferlist[ dataset ][ datacount - 1 ] );
465 newSlope = calculateSlope( m_bufferlist[ dataset ][ datacount - 1 ], newdp );
468 for (
int row =
start; row <=
end; ++row )
470 PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) );
473 const bool check = checkcur || checkpred;
476 case( PlotterDiagramCompressor::BOTH ):
478 if ( predecessor.distance( curdp ) > m_mergeRadius && check )
480 if (
start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
482 m_bufferlist[ dataset ].append( curdp );
484 else if ( !m_bufferlist[ dataset ].isEmpty() )
486 m_bufferlist[ dataset ].insert( row, curdp );
489 minX = qMin( curdp.key, m_boundary.first.x() );
490 minY = qMin( curdp.value, m_boundary.first.y() );
491 maxX = qMax( curdp.key, m_boundary.second.x() );
492 maxY = qMax( curdp.value, m_boundary.second.y() );
496 case ( PlotterDiagramCompressor::DISTANCE ):
498 if ( predecessor.distance( curdp ) > m_mergeRadius && check )
500 if (
start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
502 m_bufferlist[ dataset ].append( curdp );
504 else if ( !m_bufferlist[ dataset ].isEmpty() )
506 m_bufferlist[ dataset ].insert( row, curdp );
509 minX = qMin( curdp.key, m_boundary.first.x() );
510 minY = qMin( curdp.value, m_boundary.first.y() );
511 maxX = qMax( curdp.key, m_boundary.second.x() );
512 maxY = qMax( curdp.value, m_boundary.second.y() );
516 case( PlotterDiagramCompressor::SLOPE ):
518 if ( check && qAbs( newSlope - oldSlope ) >= m_maxSlopeRadius )
520 if (
start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
522 m_bufferlist[ dataset ].append( curdp );
525 else if ( !m_bufferlist[ dataset ].isEmpty() )
527 m_bufferlist[ dataset ].insert( row, curdp );
532 minX = qMin( curdp.key, m_boundary.first.x() );
533 minY = qMin( curdp.value, m_boundary.first.y() );
534 maxX = qMax( curdp.key, m_boundary.second.x() );
535 maxY = qMax( curdp.value, m_boundary.second.y() );
543 newdp = m_parent->data( CachePosition( row, dataset ) );
544 newSlope = calculateSlope( olddp, newdp );
551 setBoundaries( qMakePair(
QPointF( minX, minY ),
QPointF( maxX, maxY ) ) );
552 Q_EMIT m_parent->rowCountChanged();
558void PlotterDiagramCompressor::Private::rowsInserted(
const QModelIndex& ,
int start,
int end )
564 if ( m_bufferlist.count() > 0 && !m_bufferlist[ 0 ].isEmpty() &&
start < m_bufferlist[ 0 ].count() )
566 calculateDataBoundaries();
572 qreal minX = m_boundary.first.x();
573 qreal minY = m_boundary.first.y();
574 qreal maxX = m_boundary.second.x();
575 qreal maxY = m_boundary.second.y();
576 for (
int dataset = 0; dataset < m_bufferlist.size(); ++dataset )
578 if ( m_mode == PlotterDiagramCompressor::SLOPE )
580 PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last();
585 PlotterDiagramCompressor::DataPoint newdp = m_parent->data( CachePosition(
start, dataset ) );
586 PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
589 oldSlope = calculateSlope( m_parent->data( CachePosition(
start - 2, dataset ) ), m_parent->data( CachePosition(
start - 1, dataset ) ) );
590 olddp = m_parent->data( CachePosition(
start - 1, dataset ) );
594 m_bufferlist[ dataset ].append( newdp );
595 minX = qMin( minX, newdp.key );
596 minY = qMin( minY, newdp.value );
597 maxX = qMax( newdp.key, maxX );
598 maxY = qMax( newdp.value, maxY );
604 for (
int row =
start; row <=
end; ++row )
606 PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) );
608 newSlope = calculateSlope( olddp, newdp );
610 newdist = qAbs( newSlope - oldSlope );
611 m_accumulatedDistances[ dataset ] += newdist;
614 const bool check = checkcur || checkpred;
616 if ( m_accumulatedDistances[ dataset ] >= m_maxSlopeRadius && check )
618 if (
start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
620 m_bufferlist[ dataset ].append( curdp );
622 else if ( !m_bufferlist[ dataset ].isEmpty() )
624 m_bufferlist[ dataset ].insert( row, curdp );
627 m_accumulatedDistances[ dataset ] = 0;
629 minX = qMin( minX, curdp.key );
630 minY = qMin( minY, curdp.value );
631 maxX = qMax( curdp.key, maxX );
632 maxY = qMax( curdp.value, maxY );
636 if ( olddist == newdist )
644 m_bufferlist[ dataset ].append( curdp );
646 m_accumulatedDistances[ dataset ] = 0;
650 setBoundaries( qMakePair(
QPointF( minX, minY ),
QPointF( maxX, maxY ) ) );
654 PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last();
656 for (
int row =
start; row <=
end; ++row )
658 PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) );
661 const bool check = checkcur || checkpred;
662 if ( predecessor.distance( curdp ) > m_mergeRadius && check )
664 if (
start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
666 m_bufferlist[ dataset ].append( curdp );
668 else if ( !m_bufferlist[ dataset ].isEmpty() )
670 m_bufferlist[ dataset ].insert( row, curdp );
673 qreal minX = qMin( curdp.key, m_boundary.first.x() );
674 qreal minY = qMin( curdp.value, m_boundary.first.y() );
675 qreal maxX = qMax( curdp.key, m_boundary.second.x() );
676 qreal maxY = qMax( curdp.value, m_boundary.second.y() );
677 setBoundaries( qMakePair(
QPointF( minX, minY ),
QPointF( maxX, maxY ) ) );
682 Q_EMIT m_parent->rowCountChanged();
686void PlotterDiagramCompressor::setCompressionModel( CompressionMode value )
689 if ( d->m_mode != value )
697void PlotterDiagramCompressor::Private::setBoundaries(
const Boundaries & bound )
699 if ( bound != m_boundary )
702 Q_EMIT m_parent->boundariesChanged();
706void PlotterDiagramCompressor::Private::calculateDataBoundaries()
710 qreal minX = std::numeric_limits<qreal>::quiet_NaN();
711 qreal minY = std::numeric_limits<qreal>::quiet_NaN();
712 qreal maxX = std::numeric_limits<qreal>::quiet_NaN();
713 qreal maxY = std::numeric_limits<qreal>::quiet_NaN();
714 for (
int dataset = 0; dataset < m_parent->datasetCount(); ++dataset )
716 for (
int row = 0; row < m_parent->rowCount(); ++ row )
718 PlotterDiagramCompressor::DataPoint dp = m_parent->data( CachePosition( row, dataset ) );
719 minX = qMin( minX, dp.key );
720 minY = qMin( minY, dp.value );
721 maxX = qMax( dp.key, maxX );
722 maxY = qMax( dp.value, maxY );
723 Q_ASSERT( !ISNAN( minX ) );
724 Q_ASSERT( !ISNAN( minY ) );
725 Q_ASSERT( !ISNAN( maxX ) );
726 Q_ASSERT( !ISNAN( maxY ) );
731 minY = m_forcedYBoundaries.first;
732 maxY = m_forcedYBoundaries.second;
736 minX = m_forcedXBoundaries.first;
737 maxX = m_forcedXBoundaries.second;
739 setBoundaries( qMakePair(
QPointF( minX, minY ),
QPointF( maxX, maxY ) ) );
743QModelIndexList PlotterDiagramCompressor::Private::mapToModel(
const CachePosition &pos )
745 QModelIndexList indexes;
747 index = m_model->index( pos.first, pos.second * 2,
QModelIndex() );
750 index = m_model->index( pos.first, pos.second * 2 + 1,
QModelIndex() );
751 Q_ASSERT( index.isValid() );
756bool PlotterDiagramCompressor::Private::forcedBoundaries(
Qt::Orientation orient )
const
759 return !ISNAN( m_forcedYBoundaries.first ) && !ISNAN( m_forcedYBoundaries.second );
761 return !ISNAN( m_forcedXBoundaries.first ) && !ISNAN( m_forcedXBoundaries.second );
764void PlotterDiagramCompressor::Private::clearBuffer()
769 m_bufferlist.clear();
770 m_bufferlist.resize( m_parent->datasetCount() );
771 m_accumulatedDistances.clear();
772 m_accumulatedDistances.resize( m_parent->datasetCount() );
776PlotterDiagramCompressor::PlotterDiagramCompressor(
QObject *parent)
778 , d( new Private( this ) )
782PlotterDiagramCompressor::~PlotterDiagramCompressor()
788void PlotterDiagramCompressor::setForcedDataBoundaries(
const QPair< qreal, qreal > &bounds,
Qt::Orientation direction )
792 d->m_forcedYBoundaries = bounds;
796 d->m_forcedXBoundaries = bounds;
799 Q_EMIT boundariesChanged();
813 d->m_model->disconnect(
this );
814 d->m_model->disconnect( d );
819 d->m_bufferlist.resize( datasetCount() );
820 d->m_accumulatedDistances.resize( datasetCount() );
821 d->calculateDataBoundaries();
823 connect( d->m_model, SIGNAL(modelReset()), d, SLOT(clearBuffer()) );
828PlotterDiagramCompressor::DataPoint PlotterDiagramCompressor::data(
const CachePosition& pos )
const
831 QModelIndexList indexes = d->mapToModel( pos );
832 Q_ASSERT( indexes.count() == 2 );
833 QVariant yValue = d->m_model->data( indexes.last() );
834 QVariant xValue = d->m_model->data( indexes.first() );
838 point.key = xValue.
toReal( &ok );
841 point.value = yValue.
toReal( &ok );
843 point.index = indexes.first();
847void PlotterDiagramCompressor::setMergeRadius( qreal radius )
849 if ( d->m_mergeRadius != radius )
851 d->m_mergeRadius = radius;
852 if ( d->m_mode != PlotterDiagramCompressor::SLOPE )
857void PlotterDiagramCompressor::setMaxSlopeChange( qreal value )
859 if ( d->m_maxSlopeRadius != value )
861 d->m_maxSlopeRadius = value;
862 Q_EMIT boundariesChanged();
866qreal PlotterDiagramCompressor::maxSlopeChange()
const
868 return d->m_maxSlopeRadius;
871void PlotterDiagramCompressor::setMergeRadiusPercentage( qreal radius )
873 Boundaries bounds = dataBoundaries();
874 const qreal width = radius * ( bounds.second.x() - bounds.first.x() );
875 const qreal height = radius * ( bounds.second.y() - bounds.first.y() );
876 const qreal realRadius = std::sqrt( width * height );
877 setMergeRadius( realRadius );
880int PlotterDiagramCompressor::rowCount()
const
882 return d->m_model ? d->m_model->rowCount() : 0;
885void PlotterDiagramCompressor::cleanCache()
890int PlotterDiagramCompressor::datasetCount()
const
892 if ( d->m_model && d->m_model->columnCount() == 0 )
894 return d->m_model ? ( d->m_model->columnCount() + 1 ) / 2 : 0;
897QPair< QPointF, QPointF > PlotterDiagramCompressor::dataBoundaries()
const
899 Boundaries bounds = d->m_boundary;
902 bounds.first.setY( d->m_forcedYBoundaries.first );
903 bounds.second.setY( d->m_forcedYBoundaries.second );
907 bounds.first.setX( d->m_forcedXBoundaries.first );
908 bounds.second.setX( d->m_forcedXBoundaries.second );
913PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::begin(
int dataSet )
915 Q_ASSERT( dataSet >= 0 && dataSet < d->m_bufferlist.count() );
916 return Iterator( dataSet,
this, d->m_bufferlist[ dataSet ] );
919PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::end(
int dataSet )
921 Iterator it( dataSet,
this );
Q_SCRIPTABLE Q_NOREPLY void start()
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QDateTime currentDateTime()
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
QObject * parent() const const
bool isValid() const const
qreal toReal(bool *ok) const const