KD Chart 2
[rev.2.5]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 Klaralvdalens Datakonsult AB. All rights reserved. 00003 ** 00004 ** This file is part of the KD Chart library. 00005 ** 00006 ** Licensees holding valid commercial KD Chart licenses may use this file in 00007 ** accordance with the KD Chart Commercial License Agreement provided with 00008 ** the Software. 00009 ** 00010 ** 00011 ** This file may be distributed and/or modified under the terms of the 00012 ** GNU General Public License version 2 and version 3 as published by the 00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. 00014 ** 00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00017 ** 00018 ** Contact info@kdab.com if any conditions of this licensing are not 00019 ** clear to you. 00020 ** 00021 **********************************************************************/ 00022 00023 #include "KDChartCartesianDiagramDataCompressor_p.h" 00024 00025 #include <QtDebug> 00026 #include <QAbstractItemModel> 00027 00028 #include "KDChartAbstractCartesianDiagram.h" 00029 00030 #include <KDABLibFakes> 00031 00032 using namespace KDChart; 00033 using namespace std; 00034 00035 CartesianDiagramDataCompressor::CartesianDiagramDataCompressor( QObject* parent ) 00036 : QObject( parent ) 00037 , m_mode( Precise ) 00038 , m_xResolution( 0 ) 00039 , m_yResolution( 0 ) 00040 , m_sampleStep( 0 ) 00041 , m_datasetDimension( 1 ) 00042 { 00043 calculateSampleStepWidth(); 00044 m_data.resize( 0 ); 00045 } 00046 00047 static bool contains( const CartesianDiagramDataCompressor::AggregatedDataValueAttributes& aggregated, 00048 const DataValueAttributes& attributes ) 00049 { 00050 CartesianDiagramDataCompressor::AggregatedDataValueAttributes::const_iterator it = aggregated.constBegin(); 00051 for ( ; it != aggregated.constEnd(); ++it ) { 00052 if ( it.value() == attributes ) { 00053 return true; 00054 } 00055 } 00056 return false; 00057 } 00058 00059 CartesianDiagramDataCompressor::AggregatedDataValueAttributes CartesianDiagramDataCompressor::aggregatedAttrs( 00060 const AbstractDiagram* diagram, 00061 const QModelIndex & index, 00062 const CachePosition& position ) const 00063 { 00064 // return cached attrs, if any 00065 DataValueAttributesCache::const_iterator i = m_dataValueAttributesCache.constFind( position ); 00066 if ( i != m_dataValueAttributesCache.constEnd() ) { 00067 return i.value(); 00068 } 00069 00070 // aggregate attributes from all indices in the same CachePosition as index 00071 CartesianDiagramDataCompressor::AggregatedDataValueAttributes aggregated; 00072 KDAB_FOREACH( const QModelIndex& neighborIndex, mapToModel( position ) ) { 00073 DataValueAttributes attrs = diagram->dataValueAttributes( neighborIndex ); 00074 // only store visible and unique attributes 00075 if ( !attrs.isVisible() ) { 00076 continue; 00077 } 00078 if ( !contains( aggregated, attrs ) ) { 00079 aggregated[ neighborIndex ] = attrs; 00080 } 00081 } 00082 // if none of the attributes had the visible flag set, we just take the one set for the index 00083 // to avoid returning an empty list (### why not return an empty list?) 00084 if ( aggregated.isEmpty() ) { 00085 aggregated[index] = diagram->dataValueAttributes( index ); 00086 } 00087 00088 m_dataValueAttributesCache[position] = aggregated; 00089 return aggregated; 00090 } 00091 00092 bool CartesianDiagramDataCompressor::prepareDataChange( const QModelIndex& parent, bool isRows, 00093 int* start, int* end ) 00094 { 00095 if ( parent != m_rootIndex ) { 00096 return false; 00097 } 00098 Q_ASSERT( *start <= *end ); 00099 00100 CachePosition startPos = isRows ? mapToCache( *start, 0 ) : mapToCache( 0, *start ); 00101 CachePosition endPos = isRows ? mapToCache( *end, 0 ) : mapToCache( 0, *end ); 00102 00103 static const CachePosition nullPosition; 00104 if ( startPos == nullPosition ) { 00105 rebuildCache(); 00106 startPos = isRows ? mapToCache( *start, 0 ) : mapToCache( 0, *start ); 00107 endPos = isRows ? mapToCache( *end, 0 ) : mapToCache( 0, *end ); 00108 // The start position still isn't valid, 00109 // means that no resolution was set yet or we're about to add the first rows 00110 if ( startPos == nullPosition ) { 00111 return false; 00112 } 00113 } 00114 00115 *start = isRows ? startPos.row : startPos.column; 00116 *end = isRows ? endPos.row : endPos.column; 00117 return true; 00118 } 00119 00120 void CartesianDiagramDataCompressor::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end ) 00121 { 00122 if ( !prepareDataChange( parent, true, &start, &end ) ) { 00123 return; 00124 } 00125 for( int i = 0; i < m_data.size(); ++i ) 00126 { 00127 Q_ASSERT( start >= 0 && start <= m_data[ i ].size() ); 00128 m_data[ i ].insert( start, end - start + 1, DataPoint() ); 00129 } 00130 } 00131 00132 void CartesianDiagramDataCompressor::slotRowsInserted( const QModelIndex& parent, int start, int end ) 00133 { 00134 if ( !prepareDataChange( parent, true, &start, &end ) ) { 00135 return; 00136 } 00137 for( int i = 0; i < m_data.size(); ++i ) 00138 { 00139 for( int j = start; j < m_data[i].size(); ++j ) { 00140 retrieveModelData( CachePosition( j, i ) ); 00141 } 00142 } 00143 } 00144 00145 void CartesianDiagramDataCompressor::slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end ) 00146 { 00147 if ( !prepareDataChange( parent, false, &start, &end ) ) { 00148 return; 00149 } 00150 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution ); 00151 Q_ASSERT( start >= 0 && start <= m_data.size() ); 00152 m_data.insert( start, end - start + 1, QVector< DataPoint >( rowCount ) ); 00153 } 00154 00155 void CartesianDiagramDataCompressor::slotColumnsInserted( const QModelIndex& parent, int start, int end ) 00156 { 00157 if ( !prepareDataChange( parent, false, &start, &end ) ) { 00158 return; 00159 } 00160 for( int i = start; i < m_data.size(); ++i ) 00161 { 00162 for(int j = 0; j < m_data[i].size(); ++j ) { 00163 retrieveModelData( CachePosition( j, i ) ); 00164 } 00165 } 00166 } 00167 00168 void CartesianDiagramDataCompressor::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) 00169 { 00170 if ( !prepareDataChange( parent, true, &start, &end ) ) { 00171 return; 00172 } 00173 for ( int i = 0; i < m_data.size(); ++i ) { 00174 m_data[ i ].remove( start, end - start + 1 ); 00175 } 00176 } 00177 00178 void CartesianDiagramDataCompressor::slotRowsRemoved( const QModelIndex& parent, int start, int end ) 00179 { 00180 if ( parent != m_rootIndex ) 00181 return; 00182 Q_ASSERT( start <= end ); 00183 00184 CachePosition startPos = mapToCache( start, 0 ); 00185 static const CachePosition nullPosition; 00186 if ( startPos == nullPosition ) { 00187 // Since we should already have rebuilt the cache, it won't help to rebuild it again. 00188 // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 rows 00189 return; 00190 } 00191 00192 for ( int i = 0; i < m_data.size(); ++i ) { 00193 for (int j = startPos.row; j < m_data[i].size(); ++j ) { 00194 retrieveModelData( CachePosition( j, i ) ); 00195 } 00196 } 00197 } 00198 00199 void CartesianDiagramDataCompressor::slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) 00200 { 00201 if ( !prepareDataChange( parent, false, &start, &end ) ) { 00202 return; 00203 } 00204 m_data.remove( start, end - start + 1 ); 00205 } 00206 00207 void CartesianDiagramDataCompressor::slotColumnsRemoved( const QModelIndex& parent, int start, int end ) 00208 { 00209 if ( parent != m_rootIndex ) 00210 return; 00211 Q_ASSERT( start <= end ); 00212 00213 const CachePosition startPos = mapToCache( 0, start ); 00214 00215 static const CachePosition nullPosition; 00216 if ( startPos == nullPosition ) { 00217 // Since we should already have rebuilt the cache, it won't help to rebuild it again. 00218 // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 columns 00219 return; 00220 } 00221 00222 for ( int i = startPos.column; i < m_data.size(); ++i ) { 00223 for ( int j = 0; j < m_data[i].size(); ++j ) { 00224 retrieveModelData( CachePosition( j, i ) ); 00225 } 00226 } 00227 } 00228 00229 void CartesianDiagramDataCompressor::slotModelHeaderDataChanged( Qt::Orientation orientation, int first, int last ) 00230 { 00231 if( orientation != Qt::Vertical ) 00232 return; 00233 00234 if ( m_model->rowCount() > 0 ) { 00235 const QModelIndex firstRow = m_model->index( 0, first, m_rootIndex ); // checked 00236 const QModelIndex lastRow = m_model->index( m_model->rowCount( m_rootIndex ) - 1, last, m_rootIndex ); // checked 00237 00238 slotModelDataChanged( firstRow, lastRow ); 00239 } 00240 } 00241 00242 void CartesianDiagramDataCompressor::slotModelDataChanged( 00243 const QModelIndex& topLeftIndex, 00244 const QModelIndex& bottomRightIndex ) 00245 { 00246 if ( topLeftIndex.parent() != m_rootIndex ) 00247 return; 00248 Q_ASSERT( topLeftIndex.parent() == bottomRightIndex.parent() ); 00249 Q_ASSERT( topLeftIndex.row() <= bottomRightIndex.row() ); 00250 Q_ASSERT( topLeftIndex.column() <= bottomRightIndex.column() ); 00251 CachePosition topleft = mapToCache( topLeftIndex ); 00252 CachePosition bottomright = mapToCache( bottomRightIndex ); 00253 for ( int row = topleft.row; row <= bottomright.row; ++row ) 00254 for ( int column = topleft.column; column <= bottomright.column; ++column ) 00255 invalidate( CachePosition( row, column ) ); 00256 } 00257 00258 void CartesianDiagramDataCompressor::slotModelLayoutChanged() 00259 { 00260 rebuildCache(); 00261 calculateSampleStepWidth(); 00262 } 00263 00264 void CartesianDiagramDataCompressor::slotDiagramLayoutChanged( AbstractDiagram* diagramBase ) 00265 { 00266 AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( diagramBase ); 00267 Q_ASSERT( diagram ); 00268 if ( diagram->datasetDimension() != m_datasetDimension ) { 00269 setDatasetDimension( diagram->datasetDimension() ); 00270 } 00271 } 00272 00273 int CartesianDiagramDataCompressor::modelDataColumns() const 00274 { 00275 Q_ASSERT( m_datasetDimension != 0 ); 00276 // only operational if there is a model and a resolution 00277 if ( m_model ) { 00278 const int columns = m_model->columnCount( m_rootIndex ) / m_datasetDimension; 00279 00280 // if( columns != m_data.size() ) 00281 // { 00282 // rebuildCache(); 00283 // } 00284 00285 Q_ASSERT( columns == m_data.size() ); 00286 return columns; 00287 } else { 00288 return 0; 00289 } 00290 } 00291 00292 int CartesianDiagramDataCompressor::modelDataRows() const 00293 { 00294 // only operational if there is a model, columns, and a resolution 00295 if ( m_model && m_model->columnCount( m_rootIndex ) > 0 && m_xResolution > 0 ) { 00296 return m_data.isEmpty() ? 0 : m_data.first().size(); 00297 } else { 00298 return 0; 00299 } 00300 } 00301 00302 void CartesianDiagramDataCompressor::setModel( QAbstractItemModel* model ) 00303 { 00304 if ( m_model != 0 && m_model != model ) { 00305 disconnect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ), 00306 this, SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) ); 00307 disconnect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), 00308 this, SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) ); 00309 disconnect( m_model, SIGNAL( layoutChanged() ), 00310 this, SLOT( slotModelLayoutChanged() ) ); 00311 disconnect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ), 00312 this, SLOT( slotRowsAboutToBeInserted( QModelIndex, int, int ) ) ); 00313 disconnect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), 00314 this, SLOT( slotRowsInserted( QModelIndex, int, int ) ) ); 00315 disconnect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex, int, int ) ), 00316 this, SLOT( slotRowsAboutToBeRemoved( QModelIndex, int, int ) ) ); 00317 disconnect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), 00318 this, SLOT( slotRowsRemoved( QModelIndex, int, int ) ) ); 00319 disconnect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ), 00320 this, SLOT( slotColumnsAboutToBeInserted( QModelIndex, int, int ) ) ); 00321 disconnect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ), 00322 this, SLOT( slotColumnsInserted( QModelIndex, int, int ) ) ); 00323 disconnect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ), 00324 this, SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) ); 00325 disconnect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ), 00326 this, SLOT( slotColumnsAboutToBeRemoved( QModelIndex, int, int ) ) ); 00327 disconnect( m_model, SIGNAL( modelReset() ), 00328 this, SLOT( rebuildCache() ) ); 00329 m_model = 0; 00330 } 00331 00332 m_modelCache.setModel( model ); 00333 00334 if ( model != 0 ) { 00335 m_model = model; 00336 connect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ), 00337 SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) ); 00338 connect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), 00339 SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) ); 00340 connect( m_model, SIGNAL( layoutChanged() ), 00341 SLOT( slotModelLayoutChanged() ) ); 00342 connect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ), 00343 SLOT( slotRowsAboutToBeInserted( QModelIndex, int, int ) ) ); 00344 connect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), 00345 SLOT( slotRowsInserted( QModelIndex, int, int ) ) ); 00346 connect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex, int, int ) ), 00347 SLOT( slotRowsAboutToBeRemoved( QModelIndex, int, int ) ) ); 00348 connect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), 00349 SLOT( slotRowsRemoved( QModelIndex, int, int ) ) ); 00350 connect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ), 00351 SLOT( slotColumnsAboutToBeInserted( QModelIndex, int, int ) ) ); 00352 connect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ), 00353 SLOT( slotColumnsInserted( QModelIndex, int, int ) ) ); 00354 connect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ), 00355 SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) ); 00356 connect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ), 00357 SLOT( slotColumnsAboutToBeRemoved( QModelIndex, int, int ) ) ); 00358 connect( m_model, SIGNAL( modelReset() ), 00359 this, SLOT( rebuildCache() ) ); 00360 } 00361 rebuildCache(); 00362 calculateSampleStepWidth(); 00363 } 00364 00365 void CartesianDiagramDataCompressor::setRootIndex( const QModelIndex& root ) 00366 { 00367 if ( m_rootIndex != root ) { 00368 Q_ASSERT( root.model() == m_model || !root.isValid() ); 00369 m_rootIndex = root; 00370 m_modelCache.setRootIndex( root ); 00371 rebuildCache(); 00372 calculateSampleStepWidth(); 00373 } 00374 } 00375 00376 void CartesianDiagramDataCompressor::recalcResolution() 00377 { 00378 setResolution( m_xResolution, m_yResolution ); 00379 } 00380 00381 void CartesianDiagramDataCompressor::setResolution( int x, int y ) 00382 { 00383 const int oldX = m_xResolution; 00384 const int oldY = m_yResolution; 00385 00386 if( m_datasetDimension != 1 ) 00387 { 00388 // just ignore the resolution in that case 00389 m_xResolution = m_model == 0 ? 0 : m_model->rowCount( m_rootIndex ); 00390 m_yResolution = qMax( 0, y ); 00391 } 00392 else if ( x != m_xResolution || y != m_yResolution ) { 00393 m_xResolution = qMax( 0, x ); 00394 m_yResolution = qMax( 0, y ); 00395 rebuildCache(); 00396 calculateSampleStepWidth(); 00397 } 00398 00399 if( oldX != m_xResolution || oldY != m_yResolution || ( oldY != m_yResolution && m_datasetDimension == 1 ) ) 00400 { 00401 rebuildCache(); 00402 calculateSampleStepWidth(); 00403 } 00404 } 00405 00406 void CartesianDiagramDataCompressor::clearCache() 00407 { 00408 for ( int column = 0; column < m_data.size(); ++column ) 00409 m_data[column].fill( DataPoint() ); 00410 } 00411 00412 void CartesianDiagramDataCompressor::rebuildCache() 00413 { 00414 Q_ASSERT( m_datasetDimension != 0 ); 00415 00416 m_data.clear(); 00417 recalcResolution(); 00418 const int columnDivisor = m_datasetDimension != 2 ? 1 : m_datasetDimension; 00419 const int columnCount = m_model ? m_model->columnCount( m_rootIndex ) / columnDivisor : 0; 00420 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution ); 00421 m_data.resize( columnCount ); 00422 for ( int i = 0; i < columnCount; ++i ) { 00423 m_data[i].resize( rowCount ); 00424 } 00425 // also empty the attrs cache 00426 m_dataValueAttributesCache.clear(); 00427 } 00428 00429 const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data( const CachePosition& position ) const 00430 { 00431 static DataPoint nullDataPoint; 00432 if ( ! mapsToModelIndex( position ) ) { 00433 return nullDataPoint; 00434 } 00435 if ( ! isCached( position ) ) { 00436 retrieveModelData( position ); 00437 } 00438 return m_data[ position.column ][ position.row ]; 00439 } 00440 00441 QPair< QPointF, QPointF > CartesianDiagramDataCompressor::dataBoundaries() const 00442 { 00443 const int colCount = modelDataColumns(); 00444 qreal xMin = std::numeric_limits< qreal >::quiet_NaN(); 00445 qreal xMax = std::numeric_limits< qreal >::quiet_NaN(); 00446 qreal yMin = std::numeric_limits< qreal >::quiet_NaN(); 00447 qreal yMax = std::numeric_limits< qreal >::quiet_NaN(); 00448 00449 for( int column = 0; column < colCount; ++column ) 00450 { 00451 const DataPointVector& data = m_data[ column ]; 00452 int row = 0; 00453 for( DataPointVector::const_iterator it = data.begin(); it != data.end(); ++it, ++row ) 00454 { 00455 const DataPoint& p = *it; 00456 if( !p.index.isValid() ) 00457 retrieveModelData( CachePosition( row, column ) ); 00458 00459 const qreal valueX = ISNAN( p.key ) ? 0.0 : p.key; 00460 const qreal valueY = ISNAN( p.value ) ? 0.0 : p.value; 00461 if( ISNAN( xMin ) ) 00462 { 00463 xMin = valueX; 00464 xMax = valueX; 00465 yMin = valueY; 00466 yMax = valueY; 00467 } 00468 else 00469 { 00470 xMin = qMin( xMin, valueX ); 00471 xMax = qMax( xMax, valueX ); 00472 yMin = qMin( yMin, valueY ); 00473 yMax = qMax( yMax, valueY ); 00474 } 00475 } 00476 } 00477 00478 // NOTE: calculateDataBoundaries must return the *real* data boundaries! 00479 // i.e. we may NOT fake yMin to be qMin( 0.0, yMin ) 00480 // (khz, 2008-01-24) 00481 const QPointF bottomLeft( xMin, yMin ); 00482 const QPointF topRight( xMax, yMax ); 00483 return qMakePair( bottomLeft, topRight ); 00484 } 00485 00486 void CartesianDiagramDataCompressor::retrieveModelData( const CachePosition& position ) const 00487 { 00488 Q_ASSERT( mapsToModelIndex( position ) ); 00489 DataPoint result; 00490 00491 switch ( m_mode ) { 00492 case Precise: 00493 { 00494 const QModelIndexList indexes = mapToModel( position ); 00495 00496 if ( m_datasetDimension == 2 ) { 00497 Q_ASSERT( indexes.count() == 2 ); 00498 const QModelIndex& xIndex = indexes.at( 0 ); 00499 result.index = xIndex; 00500 result.key = m_modelCache.data( xIndex ); 00501 result.value = m_modelCache.data( indexes.at( 1 ) ); 00502 } else { 00503 if ( indexes.isEmpty() ) { 00504 break; 00505 } 00506 result.value = std::numeric_limits< qreal >::quiet_NaN(); 00507 result.key = 0.0; 00508 Q_FOREACH( const QModelIndex& index, indexes ) { 00509 const qreal value = m_modelCache.data( index ); 00510 if ( !ISNAN( value ) ) { 00511 result.value = ISNAN( result.value ) ? value : result.value + value; 00512 } 00513 result.key += index.row(); 00514 } 00515 result.index = indexes.at( 0 ); 00516 result.key /= indexes.size(); 00517 result.value /= indexes.size(); 00518 } 00519 break; 00520 } 00521 case SamplingSeven: 00522 break; 00523 } 00524 00525 m_data[ position.column ][ position.row ] = result; 00526 Q_ASSERT( isCached( position ) ); 00527 } 00528 00529 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache( 00530 const QModelIndex& index ) const 00531 { 00532 Q_ASSERT( m_datasetDimension != 0 ); 00533 00534 static const CachePosition nullPosition; 00535 if ( !index.isValid() ) { 00536 return nullPosition; 00537 } 00538 return mapToCache( index.row(), index.column() ); 00539 } 00540 00541 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache( 00542 int row, int column ) const 00543 { 00544 Q_ASSERT( m_datasetDimension != 0 ); 00545 00546 if ( m_data.size() == 0 || m_data[ 0 ].size() == 0 ) { 00547 return mapToCache( QModelIndex() ); 00548 } 00549 // assumption: indexes per column == 1 00550 if ( indexesPerPixel() == 0 ) { 00551 return mapToCache( QModelIndex() ); 00552 } 00553 return CachePosition( int( row / indexesPerPixel() ), column / m_datasetDimension ); 00554 } 00555 00556 QModelIndexList CartesianDiagramDataCompressor::mapToModel( const CachePosition& position ) const 00557 { 00558 QModelIndexList indexes; 00559 if ( !mapsToModelIndex( position ) ) { 00560 return indexes; 00561 } 00562 00563 if ( m_datasetDimension == 2 ) { 00564 // check consistency with data dimension of 2 00565 Q_ASSERT( m_model->columnCount() > position.column * 2 + 1 ); 00566 indexes << m_model->index( position.row, position.column * 2, m_rootIndex ); // checked 00567 indexes << m_model->index( position.row, position.column * 2 + 1, m_rootIndex ); // checked 00568 } else { 00569 // assumption: indexes per column == 1 00570 const qreal ipp = indexesPerPixel(); 00571 for ( int i = 0; i < ipp; ++i ) { 00572 int row = qRound( position.row * ipp ) + i; 00573 int col = position.column; 00574 Q_ASSERT( row < m_model->rowCount() && col < m_model->columnCount() ); 00575 const QModelIndex index = m_model->index( row, col, m_rootIndex ); // checked 00576 if ( index.isValid() ) { 00577 indexes << index; 00578 } 00579 } 00580 } 00581 return indexes; 00582 } 00583 00584 qreal CartesianDiagramDataCompressor::indexesPerPixel() const 00585 { 00586 if ( !m_model || m_data.size() == 0 || m_data[ 0 ].size() == 0 ) { 00587 return 0; 00588 } 00589 return qreal( m_model->rowCount( m_rootIndex ) ) / qreal( m_data[ 0 ].size() ); 00590 } 00591 00592 bool CartesianDiagramDataCompressor::mapsToModelIndex( const CachePosition& position ) const 00593 { 00594 return m_model && m_data.size() > 0 && m_data[ 0 ].size() > 0 && 00595 position.column >= 0 && position.column < m_data.size() && 00596 position.row >=0 && position.row < m_data[ 0 ].size(); 00597 } 00598 00599 void CartesianDiagramDataCompressor::invalidate( const CachePosition& position ) 00600 { 00601 if ( mapsToModelIndex( position ) ) { 00602 m_data[ position.column ][ position.row ] = DataPoint(); 00603 // Also invalidate the data value attributes at "position". 00604 // Otherwise the user overwrites the attributes without us noticing 00605 // it because we keep reading what's in the cache. 00606 m_dataValueAttributesCache.remove( position ); 00607 } 00608 } 00609 00610 bool CartesianDiagramDataCompressor::isCached( const CachePosition& position ) const 00611 { 00612 Q_ASSERT( mapsToModelIndex( position ) ); 00613 const DataPoint& p = m_data[ position.column ][ position.row ]; 00614 return p.index.isValid(); 00615 } 00616 00617 void CartesianDiagramDataCompressor::calculateSampleStepWidth() 00618 { 00619 if ( m_mode == Precise ) { 00620 m_sampleStep = 1; 00621 return; 00622 } 00623 00624 static unsigned int SomePrimes[] = { 00625 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 00626 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 00627 151, 211, 313, 401, 503, 607, 701, 811, 911, 1009, 00628 10037, 12911, 16001, 20011, 50021, 00629 100003, 137867, 199999, 500009, 707753, 1000003, 0 00630 }; // ... after that, having a model at all becomes impractical 00631 00632 // we want at least 17 samples per data point, using a prime step width 00633 const qreal WantedSamples = 17; 00634 if ( WantedSamples > indexesPerPixel() ) { 00635 m_sampleStep = 1; 00636 } else { 00637 int i; 00638 for ( i = 0; SomePrimes[i] != 0; ++i ) { 00639 if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) { 00640 break; 00641 } 00642 } 00643 m_sampleStep = SomePrimes[i]; 00644 if ( SomePrimes[i] == 0 ) { 00645 m_sampleStep = SomePrimes[i-1]; 00646 } else { 00647 m_sampleStep = SomePrimes[i]; 00648 } 00649 } 00650 } 00651 00652 void CartesianDiagramDataCompressor::setDatasetDimension( int dimension ) 00653 { 00654 if ( dimension != m_datasetDimension ) { 00655 m_datasetDimension = dimension; 00656 rebuildCache(); 00657 calculateSampleStepWidth(); 00658 } 00659 }