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 columnCount = m_model ? m_model->columnCount( m_rootIndex ) / m_datasetDimension : 0;
00553 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
00554 m_data.resize( columnCount );
00555 for ( int i = 0; i < columnCount; ++i ) {
00556 m_data[i].resize( rowCount );
00557 }
00558
00559 m_dataValueAttributesCache.clear();
00560 }
00561
00562 const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data( const CachePosition& position ) const
00563 {
00564 static DataPoint NullDataPoint;
00565 if ( ! isValidCachePosition( position ) ) return NullDataPoint;
00566 if ( ! isCached( position ) ) retrieveModelData( position );
00567 return m_data[ position.second ][ position.first ];
00568 }
00569
00570 QPair< QPointF, QPointF > CartesianDiagramDataCompressor::dataBoundaries() const
00571 {
00572 const int colCount = modelDataColumns();
00573 double xMin = std::numeric_limits< double >::quiet_NaN();
00574 double xMax = std::numeric_limits< double >::quiet_NaN();
00575 double yMin = std::numeric_limits< double >::quiet_NaN();
00576 double yMax = std::numeric_limits< double >::quiet_NaN();
00577
00578 for( int column = 0; column < colCount; ++column )
00579 {
00580 const DataPointVector& data = m_data[ column ];
00581 int row = 0;
00582 for( DataPointVector::const_iterator it = data.begin(); it != data.end(); ++it, ++row )
00583 {
00584 const DataPoint& p = *it;
00585 if( !p.index.isValid() )
00586 retrieveModelData( CachePosition( row, column ) );
00587
00588 const double valueX = ISNAN( p.key ) ? 0.0 : p.key;
00589 const double valueY = ISNAN( p.value ) ? 0.0 : p.value;
00590 if( ISNAN( xMin ) )
00591 {
00592 xMin = valueX;
00593 xMax = valueX;
00594 yMin = valueY;
00595 yMax = valueY;
00596 }
00597 else
00598 {
00599 xMin = qMin( xMin, valueX );
00600 xMax = qMax( xMax, valueX );
00601 yMin = qMin( yMin, valueY );
00602 yMax = qMax( yMax, valueY );
00603 }
00604 }
00605 }
00606
00607
00608
00609
00610 const QPointF bottomLeft( QPointF( xMin, yMin ) );
00611 const QPointF topRight( QPointF( xMax, yMax ) );
00612 return QPair< QPointF, QPointF >( bottomLeft, topRight );
00613 }
00614
00615 void CartesianDiagramDataCompressor::retrieveModelData( const CachePosition& position ) const
00616 {
00617 Q_ASSERT( isValidCachePosition( position ) );
00618 DataPoint result;
00619
00620 switch(m_mode ) {
00621 case Precise:
00622 {
00623 bool forceHidden = false;
00624 result.hidden = true;
00625 const QModelIndexList indexes = mapToModel( position );
00626 if( m_datasetDimension != 1 )
00627 {
00628 Q_ASSERT( indexes.count() == 2 );
00629
00630
00631 const int xColumn = indexes.first().column();
00632 const int yColumn = indexes.last().column();
00633 const QVariantList xValues = m_model->headerData( xColumn, Qt::Horizontal, ColumnDataRole ).toList();
00634 const QVariantList yValues = m_model->headerData( yColumn, Qt::Horizontal, ColumnDataRole ).toList();
00635
00636 if( !xValues.isEmpty() && !yValues.isEmpty() )
00637 {
00638 Q_ASSERT( xValues.count() == yValues.count() );
00639 int row = 0;
00640 QVariantList::const_iterator itX = xValues.begin();
00641 QVariantList::const_iterator itY = yValues.begin();
00642 for( ; itX != xValues.end(); ++itX, ++itY, ++row )
00643 {
00644 DataPoint result;
00645 result.index = m_model->index( row, xColumn, m_rootIndex );
00646 if( !itX->isNull() )
00647 {
00648 result.key = itX->toDouble();
00649 result.value = itY->toDouble();
00650 }
00651 m_data[ position.second ][ row ] = result;
00652 }
00653 return;
00654 }
00655
00656 const QModelIndex& xIndex = indexes.first();
00657 const QModelIndex& yIndex = indexes.last();
00658 const double xData = m_modelCache.data( xIndex );
00659 const double yData = m_modelCache.data( yIndex );
00660 result.index = xIndex;
00661 result.key = xData;
00662 result.value = yData;
00663 }
00664 else
00665 {
00666 if ( ! indexes.isEmpty() ) {
00667 result.value = std::numeric_limits< double >::quiet_NaN();
00668 result.key = 0.0;
00669 Q_FOREACH( const QModelIndex& index, indexes ) {
00670 const double value = m_modelCache.data( index );
00671 if( !ISNAN( value ) )
00672 {
00673 result.value = ISNAN( result.value ) ? value : result.value + value;
00674 }
00675 result.key += index.row();
00676 }
00677 result.index = indexes.at( 0 );
00678 result.key /= indexes.size();
00679 result.value /= indexes.size();
00680 }
00681 }
00682 if( !forceHidden )
00683 {
00684 Q_FOREACH( const QModelIndex& index, indexes )
00685 {
00686
00687 if ( qVariantValue<bool>( m_model->data( index, DataHiddenRole ) ) == false ) {
00688 result.hidden = false;
00689 }
00690 }
00691 }
00692 }
00693 break;
00694 case SamplingSeven:
00695 default:
00696 {
00697 }
00698 break;
00699 };
00700
00701 m_data[position.second][position.first] = result;
00702 Q_ASSERT( isCached( position ) );
00703 }
00704
00705 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
00706 const QModelIndex& index ) const
00707 {
00708 Q_ASSERT( m_datasetDimension != 0 );
00709
00710 static const CachePosition NullPosition( -1, -1 );
00711 if ( ! index.isValid() ) return NullPosition;
00712 return mapToCache( index.row(), index.column() );
00713 }
00714
00715 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
00716 int row, int column ) const
00717 {
00718 Q_ASSERT( m_datasetDimension != 0 );
00719
00720 if ( m_data.size() == 0 || m_data[0].size() == 0 ) return mapToCache( QModelIndex() );
00721
00722 if ( indexesPerPixel() == 0 ) return mapToCache( QModelIndex() );
00723 return CachePosition( static_cast< int >( ( row ) / indexesPerPixel() ), column / m_datasetDimension );
00724 }
00725
00726 QModelIndexList CartesianDiagramDataCompressor::mapToModel( const CachePosition& position ) const
00727 {
00728 if ( isValidCachePosition( position ) ) {
00729 QModelIndexList indexes;
00730 if( m_datasetDimension == 2 )
00731 {
00732 indexes << m_model->index( position.first, position.second * 2, m_rootIndex );
00733 indexes << m_model->index( position.first, position.second * 2 + 1, m_rootIndex );
00734 }
00735 else
00736 {
00737
00738 const qreal ipp = indexesPerPixel();
00739 for ( int i = 0; i < ipp; ++i ) {
00740 const QModelIndex index = m_model->index( qRound( position.first * ipp ) + i, position.second, m_rootIndex );
00741 if( index.isValid() )
00742 indexes << index;
00743 }
00744 }
00745 return indexes;
00746 } else {
00747 return QModelIndexList();
00748 }
00749 }
00750
00751 qreal CartesianDiagramDataCompressor::indexesPerPixel() const
00752 {
00753 if ( m_data.size() == 0 ) return 0;
00754 if ( m_data[0].size() == 0 ) return 0;
00755 if ( ! m_model ) return 0;
00756 return static_cast< qreal >( m_model->rowCount( m_rootIndex ) ) / static_cast< qreal >( m_data[0].size() );
00757 }
00758
00759 bool CartesianDiagramDataCompressor::isValidCachePosition( const CachePosition& position ) const
00760 {
00761 if ( ! m_model ) return false;
00762 if ( m_data.size() == 0 || m_data[0].size() == 0 ) return false;
00763 if ( position.second < 0 || position.second >= m_data.size() ) return false;
00764 if ( position.first < 0 || position.first >= m_data[0].size() ) return false;
00765 return true;
00766 }
00767
00768 void CartesianDiagramDataCompressor::invalidate( const CachePosition& position )
00769 {
00770 if ( isValidCachePosition( position ) ) {
00771 m_data[position.second][position.first] = DataPoint();
00772
00773
00774
00775 m_dataValueAttributesCache.remove( position );
00776 }
00777 }
00778
00779 bool CartesianDiagramDataCompressor::isCached( const CachePosition& position ) const
00780 {
00781 Q_ASSERT( isValidCachePosition( position ) );
00782 const DataPoint& p = m_data[position.second][position.first];
00783 return p.index.isValid();
00784 }
00785
00786 void CartesianDiagramDataCompressor::calculateSampleStepWidth()
00787 {
00788 if ( m_mode == Precise ) {
00789 m_sampleStep = 1;
00790 return;
00791 }
00792
00793 static unsigned int SomePrimes[] = {
00794 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
00795 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
00796 151, 211, 313, 401, 503, 607, 701, 811, 911, 1009,
00797 10037, 12911, 16001, 20011, 50021,
00798 100003, 137867, 199999, 500009, 707753, 1000003, 0
00799 };
00800
00801
00802 const double WantedSamples = 17;
00803 if ( WantedSamples > indexesPerPixel() ) {
00804 m_sampleStep = 1;
00805 } else {
00806 int i;
00807 for ( i = 0; SomePrimes[i] != 0; ++i ) {
00808 if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) {
00809 break;
00810 }
00811 }
00812 m_sampleStep = SomePrimes[i];
00813 if ( SomePrimes[i] == 0 ) {
00814 m_sampleStep = SomePrimes[i-1];
00815 } else {
00816 m_sampleStep = SomePrimes[i];
00817 }
00818 }
00819 }
00820
00821 void CartesianDiagramDataCompressor::setDatasetDimension( int dimension )
00822 {
00823 if ( dimension != m_datasetDimension ) {
00824 m_datasetDimension = dimension;
00825 rebuildCache();
00826 calculateSampleStepWidth();
00827 }
00828 }