13 #include "KDChartPlotterDiagramCompressor_p.h"
14 #include <QtCore/QPointF>
16 #include <KDABLibFakes>
50 , m_timeOfCreation(QDateTime::currentDateTime())
67 if (m_parent.data()->d->m_timeOfLastInvalidation < m_timeOfCreation)
68 m_parent.data()->d->m_bufferlist[m_dataset] = m_buffer;
74 if (m_parent ==
nullptr)
76 return m_dataset >= 0 && m_index >= 0 && m_parent.data()->rowCount() > m_index;
242 void PlotterDiagramCompressor::Iterator::handleSlopeForward(
const DataPoint &dp)
245 const qreal mergedist = parent->d->m_maxSlopeRadius;
251 if (m_bufferIndex > 1) {
256 qreal accumulatedDist = qAbs(newSlope - oldSlope);
257 qreal olddist = accumulatedDist;
260 while (accumulatedDist < mergedist) {
262 if (m_index >= m_parent.data()->rowCount()) {
270 newdp = parent->
data(CachePosition(m_index, m_dataset));
272 newdist = qAbs(newSlope - oldSlope);
273 if (olddist == newdist) {
279 accumulatedDist += newdist;
282 m_buffer.append(newdp);
291 const int count = parent->
rowCount();
298 if (m_index >= count || (!m_rebuffer && m_bufferIndex == m_buffer.count())) {
299 if (m_bufferIndex == m_buffer.count()) {
309 if (m_bufferIndex == m_buffer.count() && m_index >= 0 && m_rebuffer) {
311 if (parent->d->inBoundaries(Qt::Vertical, dp) && parent->d->inBoundaries(Qt::Horizontal, dp)) {
313 handleSlopeForward(dp);
330 for (
int index = m_index; index + value != m_index; ++(*this)) { };
359 if (m_index == m_parent.data()->rowCount())
360 return m_parent.data()->data(
CachePosition(m_parent.data()->rowCount() - 1, m_dataset));
361 return m_buffer[m_bufferIndex];
366 return m_parent.data() == other.m_parent.data() && m_index == other.m_index && m_dataset == other.m_dataset;
371 return !(*
this == other);
383 , m_maxSlopeRadius(0.1)
384 , m_boundary(qMakePair(QPointF(std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN()), QPointF(std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN())))
385 , m_forcedXBoundaries(qMakePair(std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN()))
386 , m_forcedYBoundaries(qMakePair(std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN()))
391 void PlotterDiagramCompressor::Private::setModelToZero()
396 inline bool inBoundary(
const QPair<qreal, qreal> &bounds, qreal value)
398 return bounds.first <= value && value <= bounds.second;
403 if (orient == Qt::Vertical && forcedBoundaries(Qt::Vertical)) {
405 }
else if (forcedBoundaries(Qt::Horizontal)) {
531 void PlotterDiagramCompressor::Private::rowsInserted(
const QModelIndex & ,
int start,
int end)
536 if (m_bufferlist.count() > 0 && !m_bufferlist[0].isEmpty() && start < m_bufferlist[0].count()) {
537 calculateDataBoundaries();
543 qreal minX = m_boundary.first.x();
544 qreal minY = m_boundary.first.y();
545 qreal maxX = m_boundary.second.x();
546 qreal maxY = m_boundary.second.y();
547 for (
int dataset = 0; dataset < m_bufferlist.size(); ++dataset) {
557 oldSlope =
calculateSlope(m_parent->data(CachePosition(start - 2, dataset)), m_parent->data(CachePosition(start - 1, dataset)));
558 olddp = m_parent->data(CachePosition(start - 1, dataset));
560 m_bufferlist[dataset].append(newdp);
561 minX = qMin(minX, newdp.
key);
562 minY = qMin(minY, newdp.
value);
563 maxX = qMax(newdp.
key, maxX);
564 maxY = qMax(newdp.
value, maxY);
570 for (
int row = start; row <=
end; ++row) {
575 newdist = qAbs(newSlope - oldSlope);
576 m_accumulatedDistances[dataset] += newdist;
577 const bool checkcur = inBoundaries(Qt::Vertical, curdp) && inBoundaries(Qt::Horizontal, curdp);
578 const bool checkpred = inBoundaries(Qt::Vertical, predecessor) && inBoundaries(Qt::Horizontal, predecessor);
579 const bool check = checkcur || checkpred;
581 if (m_accumulatedDistances[dataset] >= m_maxSlopeRadius && check) {
582 if (start > m_bufferlist[dataset].count() && !m_bufferlist[dataset].isEmpty()) {
583 m_bufferlist[dataset].append(curdp);
584 }
else if (!m_bufferlist[dataset].isEmpty()) {
585 m_bufferlist[dataset].insert(row, curdp);
588 m_accumulatedDistances[dataset] = 0;
590 minX = qMin(minX, curdp.
key);
591 minY = qMin(minY, curdp.
value);
592 maxX = qMax(curdp.
key, maxX);
593 maxY = qMax(curdp.
value, maxY);
597 if (olddist == newdist) {
601 m_bufferlist[dataset].append(curdp);
603 m_accumulatedDistances[dataset] = 0;
607 setBoundaries(qMakePair(QPointF(minX, minY), QPointF(maxX, maxY)));
611 for (
int row = start; row <=
end; ++row) {
613 const bool checkcur = inBoundaries(Qt::Vertical, curdp) && inBoundaries(Qt::Horizontal, curdp);
614 const bool checkpred = inBoundaries(Qt::Vertical, predecessor) && inBoundaries(Qt::Horizontal, predecessor);
615 const bool check = checkcur || checkpred;
616 if (predecessor.
distance(curdp) > m_mergeRadius && check) {
617 if (start > m_bufferlist[dataset].count() && !m_bufferlist[dataset].isEmpty()) {
618 m_bufferlist[dataset].append(curdp);
619 }
else if (!m_bufferlist[dataset].isEmpty()) {
620 m_bufferlist[dataset].insert(row, curdp);
623 qreal minX = qMin(curdp.
key, m_boundary.first.x());
624 qreal minY = qMin(curdp.
value, m_boundary.first.y());
625 qreal maxX = qMax(curdp.
key, m_boundary.second.x());
626 qreal maxY = qMax(curdp.
value, m_boundary.second.y());
627 setBoundaries(qMakePair(QPointF(minX, minY), QPointF(maxX, maxY)));
632 Q_EMIT m_parent->rowCountChanged();
638 if (d->m_mode != value) {
645 void PlotterDiagramCompressor::Private::setBoundaries(
const Boundaries &bound)
647 if (bound != m_boundary) {
649 Q_EMIT m_parent->boundariesChanged();
653 void PlotterDiagramCompressor::Private::calculateDataBoundaries()
655 if (!forcedBoundaries(Qt::Vertical) || !forcedBoundaries(Qt::Horizontal)) {
656 qreal minX = std::numeric_limits<qreal>::quiet_NaN();
657 qreal minY = std::numeric_limits<qreal>::quiet_NaN();
658 qreal maxX = std::numeric_limits<qreal>::quiet_NaN();
659 qreal maxY = std::numeric_limits<qreal>::quiet_NaN();
660 for (
int dataset = 0; dataset < m_parent->datasetCount(); ++dataset) {
661 for (
int row = 0; row < m_parent->rowCount(); ++row) {
663 minX = qMin(minX, dp.
key);
664 minY = qMin(minY, dp.
value);
665 maxX = qMax(dp.
key, maxX);
666 maxY = qMax(dp.
value, maxY);
667 Q_ASSERT(!ISNAN(minX));
668 Q_ASSERT(!ISNAN(minY));
669 Q_ASSERT(!ISNAN(maxX));
670 Q_ASSERT(!ISNAN(maxY));
673 if (forcedBoundaries(Qt::Vertical)) {
674 minY = m_forcedYBoundaries.first;
675 maxY = m_forcedYBoundaries.second;
677 if (forcedBoundaries(Qt::Horizontal)) {
678 minX = m_forcedXBoundaries.first;
679 maxX = m_forcedXBoundaries.second;
681 setBoundaries(qMakePair(QPointF(minX, minY), QPointF(maxX, maxY)));
685 QModelIndexList PlotterDiagramCompressor::Private::mapToModel(
const CachePosition &pos)
687 QModelIndexList indexes;
689 index = m_model->index(pos.first, pos.second * 2, QModelIndex());
690 Q_ASSERT(index.isValid());
692 index = m_model->index(pos.first, pos.second * 2 + 1, QModelIndex());
693 Q_ASSERT(index.isValid());
698 bool PlotterDiagramCompressor::Private::forcedBoundaries(Qt::Orientation orient)
const
700 if (orient == Qt::Vertical)
701 return !ISNAN(m_forcedYBoundaries.first) && !ISNAN(m_forcedYBoundaries.second);
703 return !ISNAN(m_forcedXBoundaries.first) && !ISNAN(m_forcedXBoundaries.second);
706 void PlotterDiagramCompressor::Private::clearBuffer()
711 m_bufferlist.clear();
712 m_bufferlist.resize(m_parent->datasetCount());
713 m_accumulatedDistances.clear();
714 m_accumulatedDistances.resize(m_parent->datasetCount());
715 m_timeOfLastInvalidation = QDateTime::currentDateTime();
720 , d(new Private(this))
732 if (direction == Qt::Vertical) {
733 d->m_forcedYBoundaries = bounds;
735 d->m_forcedXBoundaries = bounds;
751 d->m_model->disconnect(
this);
752 d->m_model->disconnect(d);
758 d->calculateDataBoundaries();
759 connect(d->m_model, &QAbstractItemModel::rowsInserted, d, &Private::rowsInserted);
760 connect(d->m_model, &QAbstractItemModel::modelReset, d, &Private::clearBuffer);
761 connect(d->m_model, &QAbstractItemModel::destroyed, d, &Private::setModelToZero);
768 QModelIndexList indexes = d->mapToModel(pos);
769 Q_ASSERT(indexes.count() == 2);
770 QVariant yValue = d->m_model->data(indexes.last());
771 QVariant xValue = d->m_model->data(indexes.first());
772 Q_ASSERT(xValue.isValid());
773 Q_ASSERT(yValue.isValid());
775 point.
key = xValue.toReal(&ok);
778 point.
value = yValue.toReal(&ok);
780 point.
index = indexes.first();
786 if (d->m_mergeRadius != radius) {
787 d->m_mergeRadius = radius;
795 if (d->m_maxSlopeRadius != value) {
796 d->m_maxSlopeRadius = value;
803 return d->m_maxSlopeRadius;
809 const qreal width = radius * (bounds.second.x() - bounds.first.x());
810 const qreal height = radius * (bounds.second.y() - bounds.first.y());
811 const qreal realRadius = std::sqrt(width * height);
817 return d->m_model ? d->m_model->rowCount() : 0;
827 if (d->m_model && d->m_model->columnCount() == 0)
829 return d->m_model ? (d->m_model->columnCount() + 1) / 2 : 0;
834 Boundaries bounds = d->m_boundary;
835 if (d->forcedBoundaries(Qt::Vertical)) {
836 bounds.first.setY(d->m_forcedYBoundaries.first);
837 bounds.second.setY(d->m_forcedYBoundaries.second);
839 if (d->forcedBoundaries(Qt::Horizontal)) {
840 bounds.first.setX(d->m_forcedXBoundaries.first);
841 bounds.second.setX(d->m_forcedXBoundaries.second);
848 Q_ASSERT(dataSet >= 0 && dataSet < d->m_bufferlist.count());
849 return Iterator(dataSet,
this, d->m_bufferlist[dataSet]);
qreal calculateSlope(const PlotterDiagramCompressor::DataPoint &lhs, const PlotterDiagramCompressor::DataPoint &rhs)
bool inBoundary(const QPair< qreal, qreal > &bounds, qreal value)
qreal distance(const DataPoint &other)
bool operator!=(const Iterator &other) const
Iterator & operator-=(int value)
Iterator & operator+=(int value)
bool operator==(const Iterator &other) const
Iterator(int dataSet, PlotterDiagramCompressor *parent)
Iterator begin(int dataSet)
QPair< QPointF, QPointF > dataBoundaries() const
DataPoint data(const CachePosition &pos) const
void setCompressionModel(CompressionMode value)
void setForcedDataBoundaries(const QPair< qreal, qreal > &bounds, Qt::Orientation direction)
Iterator end(int dataSet)
QAbstractItemModel * model() const
void setModel(QAbstractItemModel *model)
void setMergeRadiusPercentage(qreal radius)
PlotterDiagramCompressor(QObject *parent=nullptr)
~PlotterDiagramCompressor() override
void setMaxSlopeChange(qreal value)
void setMergeRadius(qreal radius)
qreal maxSlopeChange() const