00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <QtDebug>
00031 #include <QAbstractItemModel>
00032
00033 #include "KDChartAbstractCartesianDiagram.h"
00034 #include "KDChartCartesianDiagramDataCompressor_p.h"
00035
00036 using namespace KDChart;
00037
00038 CartesianDiagramDataCompressor::CartesianDiagramDataCompressor( QObject* parent )
00039 : QObject( parent )
00040 , m_mode( Precise )
00041 , m_xResolution( 0 )
00042 , m_yResolution( 0 )
00043 , m_sampleStep( 0 )
00044 , m_datasetDimension( 1 )
00045 {
00046 calculateSampleStepWidth();
00047 }
00048
00049 void CartesianDiagramDataCompressor::slotRowsInserted( const QModelIndex& parent, int start, int end )
00050 {
00051 Q_UNUSED( parent );
00052 Q_ASSERT( start <= end );
00053
00054 const CachePosition startPos = mapToCache( start, 0 );
00055 const CachePosition endPos = mapToCache( end, 0 );
00056
00057 start = startPos.first;
00058 end = endPos.first;
00059
00060 static const CachePosition NullPosition( -1, -1 );
00061 if( startPos == NullPosition )
00062 return rebuildCache();
00063
00064 for( int i = 0; i < m_data.size(); ++i )
00065 {
00066 Q_ASSERT( start >= 0 && start <= m_data[ i ].size() );
00067 m_data[ i ].insert( start, end - start + 1, DataPoint() );
00068 }
00069 }
00070
00071 void CartesianDiagramDataCompressor::slotColumnsInserted( const QModelIndex& parent, int start, int end )
00072 {
00073 Q_UNUSED( parent );
00074 Q_ASSERT( start <= end );
00075
00076
00077 const CachePosition startPos = mapToCache( 0, start );
00078 const CachePosition endPos = mapToCache( 0, end );
00079
00080 static const CachePosition NullPosition( -1, -1 );
00081 if( startPos == NullPosition )
00082 return rebuildCache();
00083
00084 start = startPos.second;
00085 end = endPos.second;
00086
00087 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
00088 Q_ASSERT( start >= 0 && start <= m_data.size() );
00089 m_data.insert( start, end - start + 1, QVector< DataPoint >( rowCount ) );
00090 }
00091
00092 void CartesianDiagramDataCompressor::slotRowsRemoved( const QModelIndex& parent, int start, int end )
00093 {
00094 Q_UNUSED( parent );
00095 Q_ASSERT( start <= end );
00096
00097 const CachePosition startPos = mapToCache( start, 0 );
00098 const CachePosition endPos = mapToCache( end, 0 );
00099
00100 start = startPos.first;
00101 end = endPos.first;
00102
00103 for( int i = 0; i < m_data.size(); ++i )
00104 {
00105 m_data[ i ].remove( start, end - start + 1 );
00106 }
00107 }
00108
00109 void CartesianDiagramDataCompressor::slotColumnsRemoved( const QModelIndex& parent, int start, int end )
00110 {
00111 Q_UNUSED( parent );
00112 Q_ASSERT( start <= end );
00113
00114 const CachePosition startPos = mapToCache( 0, start );
00115 const CachePosition endPos = mapToCache( 0, end );
00116
00117 start = startPos.second;
00118 end = endPos.second;
00119
00120 m_data.remove( start, end - start + 1 );
00121 }
00122
00123 void CartesianDiagramDataCompressor::slotModelHeaderDataChanged( Qt::Orientation orientation, int first, int last )
00124 {
00125 if( orientation != Qt::Vertical )
00126 return;
00127
00128 const QModelIndex firstRow = m_model->index( 0, first, m_rootIndex );
00129 const QModelIndex lastRow = m_model->index( m_model->rowCount( m_rootIndex ) - 1, last, m_rootIndex );
00130
00131 slotModelDataChanged( firstRow, lastRow );
00132 }
00133
00134 void CartesianDiagramDataCompressor::slotModelDataChanged(
00135 const QModelIndex& topLeftIndex,
00136 const QModelIndex& bottomRightIndex )
00137 {
00138 Q_ASSERT( topLeftIndex.row() <= bottomRightIndex.row() );
00139 Q_ASSERT( topLeftIndex.column() <= bottomRightIndex.column() );
00140 CachePosition topleft = mapToCache( topLeftIndex );
00141 CachePosition bottomright = mapToCache( bottomRightIndex );
00142 for ( int row = topleft.first; row <= bottomright.first; ++row )
00143 for ( int column = topleft.second; column <= bottomright.second; ++column )
00144 invalidate( CachePosition( row, column ) );
00145 }
00146
00147 void CartesianDiagramDataCompressor::slotModelLayoutChanged()
00148 {
00149 rebuildCache();
00150 calculateSampleStepWidth();
00151 }
00152
00153 void CartesianDiagramDataCompressor::slotDiagramLayoutChanged( AbstractDiagram* diagramBase )
00154 {
00155 AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( diagramBase );
00156 Q_ASSERT( diagram );
00157 if ( diagram->datasetDimension() != m_datasetDimension ) {
00158 setDatasetDimension( diagram->datasetDimension() );
00159 }
00160 }
00161
00162 int CartesianDiagramDataCompressor::modelDataColumns() const
00163 {
00164 Q_ASSERT( m_datasetDimension != 0 );
00165
00166 if ( m_model ) {
00167 const int columns = m_model->columnCount( m_rootIndex ) / m_datasetDimension;
00168
00169 if( columns != m_data.size() )
00170 {
00171 rebuildCache();
00172 }
00173
00174 Q_ASSERT( columns == m_data.size() );
00175 return columns;
00176 } else {
00177 return 0;
00178 }
00179 }
00180
00181 int CartesianDiagramDataCompressor::modelDataRows() const
00182 {
00183
00184 if ( m_model && m_model->columnCount( m_rootIndex ) > 0 && m_xResolution > 0 ) {
00185 return m_data[0].size();
00186 } else {
00187 return 0;
00188 }
00189 }
00190
00191 void CartesianDiagramDataCompressor::setModel( QAbstractItemModel* model )
00192 {
00193 if ( m_model != 0 && m_model != model ) {
00194 disconnect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
00195 this, SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) );
00196 disconnect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
00197 this, SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
00198 disconnect( m_model, SIGNAL( layoutChanged() ),
00199 this, SLOT( slotModelLayoutChanged() ) );
00200 disconnect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ),
00201 this, SLOT( slotRowsInserted( QModelIndex, int, int ) ) );
00202 disconnect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ),
00203 this, SLOT( slotRowsRemoved( QModelIndex, int, int ) ) );
00204 disconnect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ),
00205 this, SLOT( slotColumnsInserted( QModelIndex, int, int ) ) );
00206 disconnect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ),
00207 this, SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) );
00208 m_model = 0;
00209 }
00210
00211 if ( model != 0 ) {
00212 m_model = model;
00213 connect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
00214 SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) );
00215 connect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
00216 SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
00217 connect( m_model, SIGNAL( layoutChanged() ),
00218 SLOT( slotModelLayoutChanged() ) );
00219 connect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ),
00220 SLOT( slotRowsInserted( QModelIndex, int, int ) ) );
00221 connect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ),
00222 SLOT( slotRowsRemoved( QModelIndex, int, int ) ) );
00223 connect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ),
00224 SLOT( slotColumnsInserted( QModelIndex, int, int ) ) );
00225 connect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ),
00226 SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) );
00227 }
00228
00229 rebuildCache();
00230 calculateSampleStepWidth();
00231 }
00232
00233 void CartesianDiagramDataCompressor::setRootIndex( const QModelIndex& root )
00234 {
00235 if ( m_rootIndex != root ) {
00236 m_rootIndex = root;
00237 rebuildCache();
00238 calculateSampleStepWidth();
00239 }
00240 }
00241 void CartesianDiagramDataCompressor::setResolution( int x, int y )
00242 {
00243 const int oldX = m_xResolution;
00244 const int oldY = m_yResolution;
00245
00246 if( m_datasetDimension != 1 )
00247 {
00248
00249 m_xResolution = m_model == 0 ? 0 : m_model->rowCount( m_rootIndex );
00250 m_yResolution = qMax( 0, y );
00251 }
00252 else if ( x != m_xResolution || y != m_yResolution ) {
00253 m_xResolution = qMax( 0, x );
00254 m_yResolution = qMax( 0, y );
00255 rebuildCache();
00256 calculateSampleStepWidth();
00257 }
00258
00259 if( oldX != m_xResolution || oldY != m_yResolution )
00260 {
00261 rebuildCache();
00262 calculateSampleStepWidth();
00263 }
00264 }
00265
00266 void CartesianDiagramDataCompressor::clearCache()
00267 {
00268 for ( int column = 0; column < m_data.size(); ++column )
00269 m_data[column].fill( DataPoint() );
00270 }
00271
00272 void CartesianDiagramDataCompressor::rebuildCache() const
00273 {
00274 Q_ASSERT( m_datasetDimension != 0 );
00275
00276 m_data.clear();
00277 const int columnCount = m_model ? m_model->columnCount( m_rootIndex ) / m_datasetDimension : 0;
00278 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
00279 m_data.resize( columnCount );
00280 for ( int i = 0; i < columnCount; ++i ) {
00281 m_data[i].resize( rowCount );
00282 m_data[i].fill( DataPoint() );
00283 }
00284 }
00285
00286 const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data( const CachePosition& position ) const
00287 {
00288 static DataPoint NullDataPoint;
00289 if ( ! isValidCachePosition( position ) ) return NullDataPoint;
00290 if ( ! isCached( position ) ) retrieveModelData( position );
00291 return m_data[ position.second ][ position.first ];
00292 }
00293
00294 void CartesianDiagramDataCompressor::retrieveModelData( const CachePosition& position ) const
00295 {
00296 Q_ASSERT( isValidCachePosition( position ) );
00297 DataPoint result;
00298
00299 switch(m_mode ) {
00300 case Precise:
00301 {
00302 result.hidden = true;
00303 const QModelIndexList indexes = mapToModel( position );
00304 if( m_datasetDimension != 1 )
00305 {
00306 Q_ASSERT( indexes.count() == 2 );
00307 const QModelIndex xIndex = indexes.first();
00308 const QModelIndex yIndex = indexes.last();
00309 result.index = xIndex;
00310 result.key = m_model->data( xIndex ).toDouble();
00311 result.value = m_model->data( yIndex ).toDouble();
00312 }
00313 else
00314 {
00315 if ( ! indexes.isEmpty() ) {
00316 Q_FOREACH( const QModelIndex& index, indexes ) {
00317 bool ok;
00318 const QVariant valueVariant = m_model->data( index, Qt::DisplayRole );
00319 const double value = valueVariant.toDouble( &ok );
00320 if( ok )
00321 {
00322 result.value += value;
00323 result.key += index.row();
00324 }
00325 }
00326 result.index = indexes.at( 0 );
00327 result.key /= indexes.size();
00328 result.value /= indexes.size();
00329 }
00330 }
00331 Q_FOREACH( const QModelIndex& index, indexes )
00332 {
00333
00334 if ( qVariantValue<bool>( m_model->data( index, DataHiddenRole ) ) == false ) {
00335 result.hidden = false;
00336 }
00337 }
00338 }
00339 break;
00340 case SamplingSeven:
00341 default:
00342 {
00343 }
00344 break;
00345 };
00346
00347 m_data[position.second][position.first] = result;
00348 Q_ASSERT( isCached( position ) );
00349 }
00350
00351 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
00352 const QModelIndex& index ) const
00353 {
00354 Q_ASSERT( m_datasetDimension != 0 );
00355
00356 static const CachePosition NullPosition( -1, -1 );
00357 if ( ! index.isValid() ) return NullPosition;
00358 return mapToCache( index.row(), index.column() );
00359 }
00360
00361 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
00362 int row, int column ) const
00363 {
00364 Q_ASSERT( m_datasetDimension != 0 );
00365
00366 if ( m_data.size() == 0 || m_data[0].size() == 0 ) return mapToCache( QModelIndex() );
00367
00368 if ( indexesPerPixel() == 0 ) return mapToCache( QModelIndex() );
00369 return CachePosition( qRound( static_cast< qreal >( row ) / indexesPerPixel() ), column / m_datasetDimension );
00370 }
00371
00372 QModelIndexList CartesianDiagramDataCompressor::mapToModel( const CachePosition& position ) const
00373 {
00374 if ( isValidCachePosition( position ) ) {
00375 QModelIndexList indexes;
00376 if( m_datasetDimension == 2 )
00377 {
00378 indexes << m_model->index( position.first, position.second * 2, m_rootIndex );
00379 indexes << m_model->index( position.first, position.second * 2 + 1, m_rootIndex );
00380 }
00381 else
00382 {
00383
00384 const qreal ipp = indexesPerPixel();
00385 for ( int i = 0; i < ipp; ++i ) {
00386 indexes << m_model->index( qRound( position.first * ipp ), position.second, m_rootIndex );
00387 }
00388 }
00389 return indexes;
00390 } else {
00391 return QModelIndexList();
00392 }
00393 }
00394
00395 qreal CartesianDiagramDataCompressor::indexesPerPixel() const
00396 {
00397 if ( m_data.size() == 0 ) return 0;
00398 if ( m_data[0].size() == 0 ) return 0;
00399 if ( ! m_model ) return 0;
00400 return static_cast< qreal >( m_model->rowCount( m_rootIndex ) ) / static_cast< qreal >( m_data[0].size() );
00401 }
00402
00403 bool CartesianDiagramDataCompressor::isValidCachePosition( const CachePosition& position ) const
00404 {
00405 if ( ! m_model ) return false;
00406 if ( m_data.size() == 0 || m_data[0].size() == 0 ) return false;
00407 if ( position.second < 0 || position.second >= m_data.size() ) return false;
00408 if ( position.first < 0 || position.first >= m_data[0].size() ) return false;
00409 return true;
00410 }
00411
00412 void CartesianDiagramDataCompressor::invalidate( const CachePosition& position )
00413 {
00414 if ( isValidCachePosition( position ) )
00415 m_data[position.second][position.first] = DataPoint();
00416 }
00417
00418 bool CartesianDiagramDataCompressor::isCached( const CachePosition& position ) const
00419 {
00420 Q_ASSERT( isValidCachePosition( position ) );
00421 return m_data[position.second][position.first].index.isValid();
00422 }
00423
00424 void CartesianDiagramDataCompressor::calculateSampleStepWidth()
00425 {
00426 if ( m_mode == Precise ) {
00427 m_sampleStep = 1;
00428 return;
00429 }
00430
00431 static unsigned int SomePrimes[] = {
00432 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
00433 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
00434 151, 211, 313, 401, 503, 607, 701, 811, 911, 1009,
00435 10037, 12911, 16001, 20011, 50021,
00436 100003, 137867, 199999, 500009, 707753, 1000003, 0
00437 };
00438
00439
00440 const double WantedSamples = 17;
00441 if ( WantedSamples > indexesPerPixel() ) {
00442 m_sampleStep = 1;
00443 } else {
00444 int i;
00445 for ( i = 0; SomePrimes[i] != 0; ++i ) {
00446 if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) {
00447 break;
00448 }
00449 }
00450 m_sampleStep = SomePrimes[i];
00451 if ( SomePrimes[i] == 0 ) {
00452 m_sampleStep = SomePrimes[i-1];
00453 } else {
00454 m_sampleStep = SomePrimes[i];
00455 }
00456 }
00457 }
00458
00459 void CartesianDiagramDataCompressor::setDatasetDimension( int dimension )
00460 {
00461 if ( dimension != m_datasetDimension ) {
00462 m_datasetDimension = dimension;
00463 rebuildCache();
00464 calculateSampleStepWidth();
00465 }
00466 }