File indexing completed on 2024-12-15 04:02:30
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 #ifndef KCHARTCARTESIANDIAGRAMDATACOMPRESSOR_H 0010 #define KCHARTCARTESIANDIAGRAMDATACOMPRESSOR_H 0011 0012 // 0013 // W A R N I N G 0014 // ------------- 0015 // 0016 // This file is not part of the KD Chart API. It exists purely as an 0017 // implementation detail. This header file may change from version to 0018 // version without notice, or even be removed. 0019 // 0020 // We mean it. 0021 // 0022 0023 #include <limits> 0024 0025 #include <QPair> 0026 #include <QVector> 0027 #include <QObject> 0028 #include <QPointer> 0029 #include <QModelIndex> 0030 0031 #include "KChartDataValueAttributes.h" 0032 #include "KChartModelDataCache_p.h" 0033 0034 #include "kchart_export.h" 0035 0036 class CartesianDiagramDataCompressorTests; 0037 QT_BEGIN_NAMESPACE 0038 class QAbstractItemModel; 0039 QT_END_NAMESPACE 0040 0041 namespace KChart { 0042 0043 class AbstractDiagram; 0044 0045 // - transparently compress table model data if the diagram widget 0046 // size does not allow to display all data points in an acceptable way 0047 // - the class acts much like a proxy model, but is not 0048 // implemented as one, to avoid performance penalties by QVariant 0049 // conversions 0050 // - a wanted side effect is that the compressor will deliver 0051 // more precise values for more precise media, like paper 0052 // (a) this is absolutely strictly seriously private API of KChart 0053 // (b) if possible, this class is going to be templatized for 0054 // different diagram types 0055 0056 // KCHART_EXPORT is needed as long there's a test using 0057 // this class directly 0058 class KCHART_EXPORT CartesianDiagramDataCompressor : public QObject 0059 { 0060 Q_OBJECT 0061 friend class ::CartesianDiagramDataCompressorTests; 0062 0063 public: 0064 class DataPoint { 0065 public: 0066 DataPoint() 0067 : key( std::numeric_limits< qreal >::quiet_NaN() ), 0068 value( std::numeric_limits< qreal >::quiet_NaN() ), 0069 hidden( false ) 0070 {} 0071 qreal key; 0072 qreal value; 0073 bool hidden; 0074 QModelIndex index; 0075 }; 0076 typedef QVector<DataPoint> DataPointVector; 0077 class CachePosition { 0078 public: 0079 CachePosition() 0080 : row( -1 ), 0081 column( -1 ) 0082 {} 0083 CachePosition( int row, int column ) 0084 : row( row ), 0085 column( column ) 0086 {} 0087 int row; 0088 int column; 0089 0090 bool operator==( const CachePosition& rhs ) const 0091 { 0092 return row == rhs.row && 0093 column == rhs.column; 0094 } 0095 bool operator<( const CachePosition& rhs ) const 0096 { 0097 // This function is used to topologically sort all cache positions. 0098 0099 // Think of them as entries in a matrix or table: 0100 // An entry comes before another entry if it is either above the other 0101 // entry, or in the same row and to the left of the other entry. 0102 return row < rhs.row || ( row == rhs.row && column < rhs.column ); 0103 } 0104 }; 0105 0106 typedef QMap< QModelIndex, DataValueAttributes > AggregatedDataValueAttributes; 0107 typedef QMap< CartesianDiagramDataCompressor::CachePosition, AggregatedDataValueAttributes > DataValueAttributesCache; 0108 0109 enum ApproximationMode { 0110 // do not approximate, interpolate by averaging all 0111 // datapoints for a pixel 0112 Precise, 0113 // approximate by averaging out over prime number distances 0114 SamplingSeven 0115 }; 0116 0117 explicit CartesianDiagramDataCompressor( QObject* parent = nullptr ); 0118 0119 // input: model, chart resolution, approximation mode 0120 void setModel( QAbstractItemModel* ); 0121 void setRootIndex( const QModelIndex& root ); 0122 void setResolution( int x, int y ); 0123 void recalcResolution(); 0124 void setApproximationMode( ApproximationMode mode ); 0125 void setDatasetDimension( int dimension ); 0126 0127 // output: resulting model resolution, data points 0128 // FIXME (Mirko) rather stupid naming, Mirko! 0129 int modelDataColumns() const; 0130 int modelDataRows() const; 0131 const DataPoint& data( const CachePosition& ) const; 0132 0133 QPair< QPointF, QPointF > dataBoundaries() const; 0134 0135 AggregatedDataValueAttributes aggregatedAttrs( 0136 const AbstractDiagram* diagram, 0137 const QModelIndex & index, 0138 const CachePosition& position ) const; 0139 0140 public Q_SLOTS: 0141 // FIXME resolution changes and root index changes should all 0142 // be catchable with this method: 0143 void slotDiagramLayoutChanged( KChart::AbstractDiagram* ); 0144 0145 private Q_SLOTS: 0146 void slotRowsAboutToBeInserted( const QModelIndex&, int, int ); 0147 void slotRowsInserted( const QModelIndex&, int, int ); 0148 void slotRowsAboutToBeRemoved( const QModelIndex&, int, int ); 0149 void slotRowsRemoved( const QModelIndex&, int, int ); 0150 0151 void slotColumnsAboutToBeInserted( const QModelIndex&, int, int ); 0152 void slotColumnsInserted( const QModelIndex&, int, int ); 0153 void slotColumnsAboutToBeRemoved( const QModelIndex&, int, int ); 0154 void slotColumnsRemoved( const QModelIndex&, int, int ); 0155 0156 void slotModelHeaderDataChanged( Qt::Orientation, int, int ); 0157 void slotModelDataChanged( const QModelIndex&, const QModelIndex& ); 0158 void slotModelLayoutChanged(); 0159 0160 // geometry has changed 0161 void rebuildCache(); 0162 // reset all cached values, without changing the cache geometry 0163 void clearCache(); 0164 0165 private: 0166 // private version of setResolution() that does *not* call rebuildCache() 0167 bool setResolutionInternal( int x, int y ); 0168 // forget cached data at the position 0169 void invalidate( const CachePosition& ); 0170 // check if position is inside the dataset's index range 0171 bool mapsToModelIndex( const CachePosition& ) const; 0172 0173 CachePosition mapToCache( const QModelIndex& ) const; 0174 CachePosition mapToCache( int row, int column ) const; 0175 // Note: returns only valid model indices 0176 QModelIndexList mapToModel( const CachePosition& ) const; 0177 qreal indexesPerPixel() const; 0178 0179 // common logic for slot{Rows,Columns}[AboutToBe]{Inserted,Removed} 0180 bool prepareDataChange( const QModelIndex& parent, 0181 bool isRows, /* columns otherwise */ 0182 int* start, int* end); 0183 0184 // retrieve data from the model, put it into the cache 0185 void retrieveModelData( const CachePosition& ) const; 0186 // check if a data point is in the cache: 0187 bool isCached( const CachePosition& ) const; 0188 // set sample step width according to settings: 0189 void calculateSampleStepWidth(); 0190 0191 0192 QPointer<QAbstractItemModel> m_model; 0193 QModelIndex m_rootIndex; 0194 0195 ApproximationMode m_mode; 0196 int m_xResolution; 0197 int m_yResolution; 0198 unsigned int m_sampleStep; 0199 0200 mutable QVector<DataPointVector> m_data; // one per dataset 0201 ModelDataCache< qreal, Qt::DisplayRole > m_modelCache; 0202 mutable DataValueAttributesCache m_dataValueAttributesCache; 0203 int m_datasetDimension; 0204 }; 0205 } 0206 0207 #endif