00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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 }
00045
00046 QModelIndexList CartesianDiagramDataCompressor::indexesAt( const CachePosition& position ) const
00047 {
00048 if ( isValidCachePosition( position ) ) {
00049 CachePosition posPrev( position );
00050 if( m_datasetDimension == 2 ){
00051 if(posPrev.second)
00052 --posPrev.second;
00053 }else{
00054 if(posPrev.first)
00055 --posPrev.first;
00056 }
00057 const QModelIndexList indPrev = mapToModel( posPrev );
00058 const QModelIndexList indCur = mapToModel( position );
00059
00060 QModelIndexList indexes;
00061 if( m_datasetDimension == 2 )
00062 {
00063 const int iStart = (indPrev.empty() || indPrev==indCur) ? indCur.first().column()
00064 : indPrev.first().column() + 1;
00065 const int iEnd = indCur.last().column();
00066 for( int i=iStart; i<=iEnd; ++i){
00067 indexes << m_model->index( position.first, i, m_rootIndex );
00068 }
00069 }
00070 else
00071 {
00072 const int iStart = (indPrev.empty() || indPrev==indCur) ? indCur.first().row()
00073 : indPrev.first().row() + 1;
00074 const int iEnd = (indCur.isEmpty()) ? iStart : indCur.first().row();
00075
00076 for( int i=iStart; i<=iEnd; ++i){
00077 indexes << m_model->index( i, position.second, m_rootIndex );
00078 }
00079 }
00080 return indexes;
00081 } else {
00082 return QModelIndexList();
00083 }
00084 }
00085
00086
00087 CartesianDiagramDataCompressor::DataValueAttributesList CartesianDiagramDataCompressor::aggregatedAttrs(
00088 AbstractDiagram * diagram,
00089 const QModelIndex & index,
00090 const CachePosition& position ) const
00091 {
00092
00093 DataValueAttributesCache::const_iterator i = m_dataValueAttributesCache.constFind(position);
00094 if( i != m_dataValueAttributesCache.constEnd() )
00095 return i.value();
00096
00097 CartesianDiagramDataCompressor::DataValueAttributesList allAttrs;
00098 const QModelIndexList indexes( indexesAt( position ) );
00099 KDAB_FOREACH( QModelIndex idx, indexes ) {
00100 DataValueAttributes attrs( diagram->dataValueAttributes( idx ) );
00101 if( attrs.isVisible() ){
00102
00103 bool isDuplicate = false;
00104 CartesianDiagramDataCompressor::DataValueAttributesList::const_iterator i = allAttrs.constBegin();
00105 while (i != allAttrs.constEnd()) {
00106 if( i.value() == attrs ){
00107 isDuplicate = true;
00108 continue;
00109 }
00110 ++i;
00111 }
00112 if( !isDuplicate ){
00113
00114 allAttrs[idx] = attrs;
00115 }
00116 }
00117 }
00118
00119
00120 if( allAttrs.empty() ){
00121 allAttrs[index] = diagram->dataValueAttributes( index );
00122 }
00123
00124 m_dataValueAttributesCache[position] = allAttrs;
00125 return allAttrs;
00126 }
00127
00128
00129 void CartesianDiagramDataCompressor::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end )
00130 {
00131 if ( parent != m_rootIndex )
00132 return;
00133 Q_ASSERT( start <= end );
00134
00135 CachePosition startPos = mapToCache( start, 0 );
00136 CachePosition endPos = mapToCache( end, 0 );
00137
00138 static const CachePosition NullPosition( -1, -1 );
00139 if( startPos == NullPosition )
00140 {
00141 rebuildCache();
00142 startPos = mapToCache( start, 0 );
00143 endPos = mapToCache( end, 0 );
00144
00145
00146 if( startPos == NullPosition ) {
00147 return;
00148 }
00149 }
00150
00151 start = startPos.first;
00152 end = endPos.first;
00153
00154 for( int i = 0; i < m_data.size(); ++i )
00155 {
00156 Q_ASSERT( start >= 0 && start <= m_data[ i ].size() );
00157 m_data[ i ].insert( start, end - start + 1, DataPoint() );
00158 }
00159 }
00160
00161 void CartesianDiagramDataCompressor::slotRowsInserted( const QModelIndex& parent, int start, int end )
00162 {
00163 if ( parent != m_rootIndex )
00164 return;
00165 Q_ASSERT( start <= end );
00166
00167 CachePosition startPos = mapToCache( start, 0 );
00168 CachePosition endPos = mapToCache( end, 0 );
00169
00170 static const CachePosition NullPosition( -1, -1 );
00171 if( startPos == NullPosition )
00172 {
00173
00174 rebuildCache();
00175 startPos = mapToCache( start, 0 );
00176 endPos = mapToCache( end, 0 );
00177
00178
00179 if( startPos == NullPosition ) {
00180 return;
00181 }
00182 }
00183
00184 start = startPos.first;
00185 end = endPos.first;
00186
00187 for( int i = 0; i < m_data.size(); ++i )
00188 {
00189 for( int j = start; j < m_data[i].size(); ++j ) {
00190 retrieveModelData( CachePosition( j, i ) );
00191 }
00192 }
00193 }
00194
00195 void CartesianDiagramDataCompressor::slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end )
00196 {
00197 if ( parent != m_rootIndex )
00198 return;
00199 Q_ASSERT( start <= end );
00200
00201 CachePosition startPos = mapToCache( 0, start );
00202 CachePosition endPos = mapToCache( 0, end );
00203
00204 static const CachePosition NullPosition( -1, -1 );
00205 if( startPos == NullPosition )
00206 {
00207 rebuildCache();
00208 startPos = mapToCache( 0, start );
00209 endPos = mapToCache( 0, end );
00210
00211
00212 if( startPos == NullPosition ) {
00213 return;
00214 }
00215 }
00216
00217 start = startPos.second;
00218 end = endPos.second;
00219
00220 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
00221 Q_ASSERT( start >= 0 && start <= m_data.size() );
00222 m_data.insert( start, end - start + 1, QVector< DataPoint >( rowCount ) );
00223 }
00224
00225 void CartesianDiagramDataCompressor::slotColumnsInserted( const QModelIndex& parent, int start, int end )
00226 {
00227 if ( parent != m_rootIndex )
00228 return;
00229 Q_ASSERT( start <= end );
00230
00231 CachePosition startPos = mapToCache( 0, start );
00232 CachePosition endPos = mapToCache( 0, end );
00233
00234 static const CachePosition NullPosition( -1, -1 );
00235 if( startPos == NullPosition )
00236 {
00237
00238 rebuildCache();
00239 startPos = mapToCache( 0, start );
00240 endPos = mapToCache( 0, end );
00241
00242
00243 if( startPos == NullPosition ) {
00244 return;
00245 }
00246 }
00247
00248 start = startPos.second;
00249 end = endPos.second;
00250
00251 for( int i = start; i < m_data.size(); ++i )
00252 {
00253 for(int j = 0; j < m_data[i].size(); ++j ) {
00254 retrieveModelData( CachePosition( j, i ) );
00255 }
00256 }
00257 }
00258
00259 void CartesianDiagramDataCompressor::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
00260 {
00261 if ( parent != m_rootIndex )
00262 return;
00263 Q_ASSERT( start <= end );
00264
00265 CachePosition startPos = mapToCache( start, 0 );
00266 CachePosition endPos = mapToCache( end, 0 );
00267
00268 static const CachePosition NullPosition( -1, -1 );
00269 if( startPos == NullPosition )
00270 {
00271 rebuildCache();
00272 startPos = mapToCache( start, 0 );
00273 endPos = mapToCache( end, 0 );
00274
00275
00276 if( startPos == NullPosition ) {
00277 return;
00278 }
00279 }
00280
00281 start = startPos.first;
00282 end = endPos.first;
00283
00284 for( int i = 0; i < m_data.size(); ++i )
00285 {
00286 m_data[ i ].remove( start, end - start + 1 );
00287 }
00288 }
00289
00290 void CartesianDiagramDataCompressor::slotRowsRemoved( const QModelIndex& parent, int start, int end )
00291 {
00292 if ( parent != m_rootIndex )
00293 return;
00294 Q_ASSERT( start <= end );
00295
00296 CachePosition startPos = mapToCache( start, 0 );
00297 CachePosition endPos = mapToCache( end, 0 );
00298
00299 start = startPos.first;
00300 end = endPos.first;
00301
00302 static const CachePosition NullPosition( -1, -1 );
00303 if( startPos == NullPosition )
00304 {
00305
00306
00307 return;
00308 }
00309
00310 for( int i = 0; i < m_data.size(); ++i ) {
00311 for(int j = start; j < m_data[i].size(); ++j ) {
00312 retrieveModelData( CachePosition( j, i ) );
00313 }
00314 }
00315 }
00316
00317 void CartesianDiagramDataCompressor::slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
00318 {
00319 if ( parent != m_rootIndex )
00320 return;
00321 Q_ASSERT( start <= end );
00322
00323 CachePosition startPos = mapToCache( 0, start );
00324 CachePosition endPos = mapToCache( 0, end );
00325
00326 static const CachePosition NullPosition( -1, -1 );
00327 if( startPos == NullPosition )
00328 {
00329 rebuildCache();
00330 startPos = mapToCache( 0, start );
00331 endPos = mapToCache( 0, end );
00332
00333
00334 if( startPos == NullPosition ) {
00335 return;
00336 }
00337 }
00338
00339 start = startPos.second;
00340 end = endPos.second;
00341
00342 m_data.remove( start, end - start + 1 );
00343 }
00344
00345 void CartesianDiagramDataCompressor::slotColumnsRemoved( const QModelIndex& parent, int start, int end )
00346 {
00347 if ( parent != m_rootIndex )
00348 return;
00349 Q_ASSERT( start <= end );
00350
00351 const CachePosition startPos = mapToCache( 0, start );
00352 const CachePosition endPos = mapToCache( 0, end );
00353
00354 start = startPos.second;
00355 end = endPos.second;
00356
00357 static const CachePosition NullPosition( -1, -1 );
00358 if( startPos == NullPosition )
00359 {
00360
00361
00362 return;
00363 }
00364
00365 for( int i = start; i < m_data.size(); ++i ) {
00366 for( int j = 0; j < m_data[i].size(); ++j ) {
00367 retrieveModelData( CachePosition( j, i ) );
00368 }
00369 }
00370 }
00371
00372 void CartesianDiagramDataCompressor::slotModelHeaderDataChanged( Qt::Orientation orientation, int first, int last )
00373 {
00374 if( orientation != Qt::Vertical )
00375 return;
00376
00377 const QModelIndex firstRow = m_model->index( 0, first, m_rootIndex );
00378 const QModelIndex lastRow = m_model->index( m_model->rowCount( m_rootIndex ) - 1, last, m_rootIndex );
00379
00380 slotModelDataChanged( firstRow, lastRow );
00381 }
00382
00383 void CartesianDiagramDataCompressor::slotModelDataChanged(
00384 const QModelIndex& topLeftIndex,
00385 const QModelIndex& bottomRightIndex )
00386 {
00387 if ( topLeftIndex.parent() != m_rootIndex )
00388 return;
00389 Q_ASSERT( topLeftIndex.parent() == bottomRightIndex.parent() );
00390 Q_ASSERT( topLeftIndex.row() <= bottomRightIndex.row() );
00391 Q_ASSERT( topLeftIndex.column() <= bottomRightIndex.column() );
00392 CachePosition topleft = mapToCache( topLeftIndex );
00393 CachePosition bottomright = mapToCache( bottomRightIndex );
00394 for ( int row = topleft.first; row <= bottomright.first; ++row )
00395 for ( int column = topleft.second; column <= bottomright.second; ++column )
00396 invalidate( CachePosition( row, column ) );
00397 }
00398
00399 void CartesianDiagramDataCompressor::slotModelLayoutChanged()
00400 {
00401 rebuildCache();
00402 calculateSampleStepWidth();
00403 }
00404
00405 void CartesianDiagramDataCompressor::slotDiagramLayoutChanged( AbstractDiagram* diagramBase )
00406 {
00407 AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( diagramBase );
00408 Q_ASSERT( diagram );
00409 if ( diagram->datasetDimension() != m_datasetDimension ) {
00410 setDatasetDimension( diagram->datasetDimension() );
00411 }
00412 }
00413
00414 int CartesianDiagramDataCompressor::modelDataColumns() const
00415 {
00416 Q_ASSERT( m_datasetDimension != 0 );
00417
00418 if ( m_model ) {
00419 const int columns = m_model->columnCount( m_rootIndex ) / m_datasetDimension;
00420
00421 if( columns != m_data.size() )
00422 {
00423 rebuildCache();
00424 }
00425
00426 Q_ASSERT( columns == m_data.size() );
00427 return columns;
00428 } else {
00429 return 0;
00430 }
00431 }
00432
00433 int CartesianDiagramDataCompressor::modelDataRows() const
00434 {
00435
00436 if ( m_model && m_model->columnCount( m_rootIndex ) > 0 && m_xResolution > 0 ) {
00437 return m_data.isEmpty() ? 0 : m_data.first().size();
00438 } else {
00439 return 0;
00440 }
00441 }
00442
00443 void CartesianDiagramDataCompressor::setModel( QAbstractItemModel* model )
00444 {
00445 if ( m_model != 0 && m_model != model ) {
00446 disconnect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
00447 this, SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) );
00448 disconnect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
00449 this, SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
00450 disconnect( m_model, SIGNAL( layoutChanged() ),
00451 this, SLOT( slotModelLayoutChanged() ) );
00452 disconnect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ),
00453 this, SLOT( slotRowsAboutToBeInserted( QModelIndex, int, int ) ) );
00454 disconnect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ),
00455 this, SLOT( slotRowsInserted( QModelIndex, int, int ) ) );
00456 disconnect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex, int, int ) ),
00457 this, SLOT( slotRowsAboutToBeRemoved( QModelIndex, int, int ) ) );
00458 disconnect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ),
00459 this, SLOT( slotRowsRemoved( QModelIndex, int, int ) ) );
00460 disconnect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ),
00461 this, SLOT( slotColumnsAboutToBeInserted( QModelIndex, int, int ) ) );
00462 disconnect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ),
00463 this, SLOT( slotColumnsInserted( QModelIndex, int, int ) ) );
00464 disconnect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ),
00465 this, SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) );
00466 disconnect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ),
00467 this, SLOT( slotColumnsAboutToBeRemoved( QModelIndex, int, int ) ) );
00468 disconnect( m_model, SIGNAL( modelReset() ),
00469 this, SLOT( rebuildCache() ) );
00470 m_model = 0;
00471 }
00472
00473 m_modelCache.setModel( model );
00474
00475 if ( model != 0 ) {
00476 m_model = model;
00477 connect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
00478 SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) );
00479 connect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
00480 SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
00481 connect( m_model, SIGNAL( layoutChanged() ),
00482 SLOT( slotModelLayoutChanged() ) );
00483 connect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ),
00484 SLOT( slotRowsAboutToBeInserted( QModelIndex, int, int ) ) );
00485 connect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ),
00486 SLOT( slotRowsInserted( QModelIndex, int, int ) ) );
00487 connect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex, int, int ) ),
00488 SLOT( slotRowsAboutToBeRemoved( QModelIndex, int, int ) ) );
00489 connect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ),
00490 SLOT( slotRowsRemoved( QModelIndex, int, int ) ) );
00491 connect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ),
00492 SLOT( slotColumnsAboutToBeInserted( QModelIndex, int, int ) ) );
00493 connect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ),
00494 SLOT( slotColumnsInserted( QModelIndex, int, int ) ) );
00495 connect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ),
00496 SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) );
00497 connect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ),
00498 SLOT( slotColumnsAboutToBeRemoved( QModelIndex, int, int ) ) );
00499 connect( m_model, SIGNAL( modelReset() ),
00500 this, SLOT( rebuildCache() ) );
00501 }
00502 rebuildCache();
00503 calculateSampleStepWidth();
00504 }
00505
00506 void CartesianDiagramDataCompressor::setRootIndex( const QModelIndex& root )
00507 {
00508 if ( m_rootIndex != root ) {
00509 Q_ASSERT( root.model() == m_model || !root.isValid() );
00510 m_rootIndex = root;
00511 m_modelCache.setRootIndex( root );
00512 rebuildCache();
00513 calculateSampleStepWidth();
00514 }
00515 }
00516 void CartesianDiagramDataCompressor::setResolution( int x, int y )
00517 {
00518 const int oldX = m_xResolution;
00519 const int oldY = m_yResolution;
00520
00521 if( m_datasetDimension != 1 )
00522 {
00523
00524 m_xResolution = m_model == 0 ? 0 : m_model->rowCount( m_rootIndex );
00525 m_yResolution = qMax( 0, y );
00526 }
00527 else if ( x != m_xResolution || y != m_yResolution ) {
00528 m_xResolution = qMax( 0, x );
00529 m_yResolution = qMax( 0, y );
00530 rebuildCache();
00531 calculateSampleStepWidth();
00532 }
00533
00534 if( oldX != m_xResolution || ( oldY != m_yResolution && m_datasetDimension == 1 ) )
00535 {
00536 rebuildCache();
00537 calculateSampleStepWidth();
00538 }
00539 }
00540
00541 void CartesianDiagramDataCompressor::clearCache()
00542 {
00543 for ( int column = 0; column < m_data.size(); ++column )
00544 m_data[column].fill( DataPoint() );
00545 }
00546
00547 void CartesianDiagramDataCompressor::rebuildCache() const
00548 {
00549 Q_ASSERT( m_datasetDimension != 0 );
00550
00551 m_data.clear();
00552 const int columnDivisor = m_datasetDimension != 2 ? 1 : m_datasetDimension;
00553 const int columnCount = m_model ? m_model->columnCount( m_rootIndex ) / columnDivisor : 0;
00554 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
00555 m_data.resize( columnCount );
00556 for ( int i = 0; i < columnCount; ++i ) {
00557 m_data[i].resize( rowCount );
00558 }
00559
00560 m_dataValueAttributesCache.clear();
00561 }
00562
00563 const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data( const CachePosition& position ) const
00564 {
00565 static DataPoint NullDataPoint;
00566 if ( ! isValidCachePosition( position ) ) return NullDataPoint;
00567 if ( ! isCached( position ) ) retrieveModelData( position );
00568 return m_data[ position.second ][ position.first ];
00569 }
00570
00571 QPair< QPointF, QPointF > CartesianDiagramDataCompressor::dataBoundaries() const
00572 {
00573 const int colCount = modelDataColumns();
00574 double xMin = std::numeric_limits< double >::quiet_NaN();
00575 double xMax = std::numeric_limits< double >::quiet_NaN();
00576 double yMin = std::numeric_limits< double >::quiet_NaN();
00577 double yMax = std::numeric_limits< double >::quiet_NaN();
00578
00579 for( int column = 0; column < colCount; ++column )
00580 {
00581 const DataPointVector& data = m_data[ column ];
00582 int row = 0;
00583 for( DataPointVector::const_iterator it = data.begin(); it != data.end(); ++it, ++row )
00584 {
00585 const DataPoint& p = *it;
00586 if( !p.index.isValid() )
00587 retrieveModelData( CachePosition( row, column ) );
00588
00589 const double valueX = ISNAN( p.key ) ? 0.0 : p.key;
00590 const double valueY = ISNAN( p.value ) ? 0.0 : p.value;
00591 if( ISNAN( xMin ) )
00592 {
00593 xMin = valueX;
00594 xMax = valueX;
00595 yMin = valueY;
00596 yMax = valueY;
00597 }
00598 else
00599 {
00600 xMin = qMin( xMin, valueX );
00601 xMax = qMax( xMax, valueX );
00602 yMin = qMin( yMin, valueY );
00603 yMax = qMax( yMax, valueY );
00604 }
00605 }
00606 }
00607
00608
00609
00610
00611 const QPointF bottomLeft( xMin, yMin );
00612 const QPointF topRight( xMax, yMax );
00613 return QPair< QPointF, QPointF >( bottomLeft, topRight );
00614 }
00615
00616 void CartesianDiagramDataCompressor::retrieveModelData( const CachePosition& position ) const
00617 {
00618 Q_ASSERT( isValidCachePosition( position ) );
00619 DataPoint result;
00620
00621 switch(m_mode ) {
00622 case Precise:
00623 {
00624 bool forceHidden = false;
00625 result.hidden = true;
00626 const QModelIndexList indexes = mapToModel( position );
00627 if( m_datasetDimension == 2 )
00628 {
00629 Q_ASSERT( indexes.count() == 2 );
00630
00631
00632 const int xColumn = indexes.first().column();
00633 const int yColumn = indexes.last().column();
00634 const QVariantList xValues = m_model->headerData( xColumn, Qt::Horizontal, ColumnDataRole ).toList();
00635 const QVariantList yValues = m_model->headerData( yColumn, Qt::Horizontal, ColumnDataRole ).toList();
00636
00637 if( !xValues.isEmpty() && !yValues.isEmpty() )
00638 {
00639 Q_ASSERT( xValues.count() == yValues.count() );
00640 int row = 0;
00641 QVariantList::const_iterator itX = xValues.begin();
00642 QVariantList::const_iterator itY = yValues.begin();
00643 for( ; itX != xValues.end(); ++itX, ++itY, ++row )
00644 {
00645 DataPoint result;
00646 result.index = m_model->index( row, xColumn, m_rootIndex );
00647 if( !itX->isNull() )
00648 {
00649 result.key = itX->toDouble();
00650 result.value = itY->toDouble();
00651 }
00652 m_data[ position.second ][ row ] = result;
00653 }
00654 return;
00655 }
00656
00657 const QModelIndex& xIndex = indexes.first();
00658 const QModelIndex& yIndex = indexes.last();
00659 const double xData = m_modelCache.data( xIndex );
00660 const double yData = m_modelCache.data( yIndex );
00661 result.index = xIndex;
00662 result.key = xData;
00663 result.value = yData;
00664 }
00665 else
00666 {
00667 if ( ! indexes.isEmpty() ) {
00668 result.value = std::numeric_limits< double >::quiet_NaN();
00669 result.key = 0.0;
00670 Q_FOREACH( const QModelIndex& index, indexes ) {
00671 const double value = m_modelCache.data( index );
00672 if( !ISNAN( value ) )
00673 {
00674 result.value = ISNAN( result.value ) ? value : result.value + value;
00675 }
00676 result.key += index.row();
00677 }
00678 result.index = indexes.at( 0 );
00679 result.key /= indexes.size();
00680 result.value /= indexes.size();
00681 }
00682 }
00683 if( !forceHidden )
00684 {
00685 Q_FOREACH( const QModelIndex& index, indexes )
00686 {
00687
00688 if ( qVariantValue<bool>( m_model->data( index, DataHiddenRole ) ) == false ) {
00689 result.hidden = false;
00690 }
00691 }
00692 }
00693 }
00694 break;
00695 case SamplingSeven:
00696 default:
00697 {
00698 }
00699 break;
00700 };
00701
00702 m_data[position.second][position.first] = result;
00703 Q_ASSERT( isCached( position ) );
00704 }
00705
00706 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
00707 const QModelIndex& index ) const
00708 {
00709 Q_ASSERT( m_datasetDimension != 0 );
00710
00711 static const CachePosition NullPosition( -1, -1 );
00712 if ( ! index.isValid() ) return NullPosition;
00713 return mapToCache( index.row(), index.column() );
00714 }
00715
00716 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
00717 int row, int column ) const
00718 {
00719 Q_ASSERT( m_datasetDimension != 0 );
00720
00721 if ( m_data.size() == 0 || m_data[0].size() == 0 ) return mapToCache( QModelIndex() );
00722
00723 if ( indexesPerPixel() == 0 ) return mapToCache( QModelIndex() );
00724 return CachePosition( static_cast< int >( ( row ) / indexesPerPixel() ), column / m_datasetDimension );
00725 }
00726
00727 QModelIndexList CartesianDiagramDataCompressor::mapToModel( const CachePosition& position ) const
00728 {
00729 if ( isValidCachePosition( position ) ) {
00730 QModelIndexList indexes;
00731 if( m_datasetDimension == 2 )
00732 {
00733 indexes << m_model->index( position.first, position.second * 2, m_rootIndex );
00734 indexes << m_model->index( position.first, position.second * 2 + 1, m_rootIndex );
00735 }
00736 else
00737 {
00738
00739 const qreal ipp = indexesPerPixel();
00740 for ( int i = 0; i < ipp; ++i ) {
00741 const QModelIndex index = m_model->index( qRound( position.first * ipp ) + i, position.second, m_rootIndex );
00742 if( index.isValid() )
00743 indexes << index;
00744 }
00745 }
00746 return indexes;
00747 } else {
00748 return QModelIndexList();
00749 }
00750 }
00751
00752 qreal CartesianDiagramDataCompressor::indexesPerPixel() const
00753 {
00754 if ( m_data.size() == 0 ) return 0;
00755 if ( m_data[0].size() == 0 ) return 0;
00756 if ( ! m_model ) return 0;
00757 return static_cast< qreal >( m_model->rowCount( m_rootIndex ) ) / static_cast< qreal >( m_data[0].size() );
00758 }
00759
00760 bool CartesianDiagramDataCompressor::isValidCachePosition( const CachePosition& position ) const
00761 {
00762 if ( ! m_model ) return false;
00763 if ( m_data.size() == 0 || m_data[0].size() == 0 ) return false;
00764 if ( position.second < 0 || position.second >= m_data.size() ) return false;
00765 if ( position.first < 0 || position.first >= m_data[0].size() ) return false;
00766 return true;
00767 }
00768
00769 void CartesianDiagramDataCompressor::invalidate( const CachePosition& position )
00770 {
00771 if ( isValidCachePosition( position ) ) {
00772 m_data[position.second][position.first] = DataPoint();
00773
00774
00775
00776 m_dataValueAttributesCache.remove( position );
00777 }
00778 }
00779
00780 bool CartesianDiagramDataCompressor::isCached( const CachePosition& position ) const
00781 {
00782 Q_ASSERT( isValidCachePosition( position ) );
00783 const DataPoint& p = m_data[position.second][position.first];
00784 return p.index.isValid();
00785 }
00786
00787 void CartesianDiagramDataCompressor::calculateSampleStepWidth()
00788 {
00789 if ( m_mode == Precise ) {
00790 m_sampleStep = 1;
00791 return;
00792 }
00793
00794 static unsigned int SomePrimes[] = {
00795 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
00796 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
00797 151, 211, 313, 401, 503, 607, 701, 811, 911, 1009,
00798 10037, 12911, 16001, 20011, 50021,
00799 100003, 137867, 199999, 500009, 707753, 1000003, 0
00800 };
00801
00802
00803 const double WantedSamples = 17;
00804 if ( WantedSamples > indexesPerPixel() ) {
00805 m_sampleStep = 1;
00806 } else {
00807 int i;
00808 for ( i = 0; SomePrimes[i] != 0; ++i ) {
00809 if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) {
00810 break;
00811 }
00812 }
00813 m_sampleStep = SomePrimes[i];
00814 if ( SomePrimes[i] == 0 ) {
00815 m_sampleStep = SomePrimes[i-1];
00816 } else {
00817 m_sampleStep = SomePrimes[i];
00818 }
00819 }
00820 }
00821
00822 void CartesianDiagramDataCompressor::setDatasetDimension( int dimension )
00823 {
00824 if ( dimension != m_datasetDimension ) {
00825 m_datasetDimension = dimension;
00826 rebuildCache();
00827 calculateSampleStepWidth();
00828 }
00829 }