KDChartCartesianDiagramDataCompressor_p.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2011 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 }
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             //qDebug()<<iStart<<iEnd << iEnd-iStart;
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     // return cached attrs, if any
00093     DataValueAttributesCache::const_iterator i = m_dataValueAttributesCache.constFind(position);
00094     if( i != m_dataValueAttributesCache.constEnd() )
00095         return i.value();
00096     // retrieve attrs from all cells between the prev. cell and the current one
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             // make sure no duplicate attrs are stored
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                 //qDebug()<<idx.row();
00114                 allAttrs[idx] = attrs;
00115             }
00116         }
00117     }
00118     // if none of the attrs had the visible flag set
00119     // we just take the one set for the index to not return an empty list
00120     if( allAttrs.empty() ){
00121         allAttrs[index] = diagram->dataValueAttributes( index );
00122     }
00123     // cache the attrs
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         // The start position still isn't valid,
00145         // means that no resolution was set yet or we're about to add the first rows
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         // Rebuild the cache at this point if we have added the first rows
00174         rebuildCache();
00175         startPos = mapToCache( start, 0 );
00176         endPos = mapToCache( end, 0 );
00177         // The start position still isn't valid,
00178         // means that no resolution was set yet
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         // The start position still isn't valid,
00211         // means that no resolution was set yet or we're about to add the first columns
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         // Rebuild the cache at this point if we have added the first columns
00238         rebuildCache();
00239         startPos = mapToCache( 0, start );
00240         endPos = mapToCache( 0, end );
00241         // The start position still isn't valid,
00242         // means that no resolution was set yet
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         // The start position still isn't valid,
00275         // probably means that no resolution was set yet
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         // Since we should already have rebuilt the cache, it won't help to rebuild it again.
00306         // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 rows
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         // The start position still isn't valid,
00333         // probably means that no resolution was set yet
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         // Since we should already have rebuilt the cache, it won't help to rebuild it again.
00361         // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 columns
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     // only operational if there is a model and a resolution
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     // only operational if there is a model, columns, and a resolution
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         // just ignore the resolution in that case
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     // also empty the attrs cache
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     // NOTE: calculateDataBoundaries must return the *real* data boundaries!
00609     //       i.e. we may NOT fake yMin to be qMin( 0.0, yMin )
00610     //       (khz, 2008-01-24)
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             // try the ColumnDataRole approach first
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             // the point is visible if any of the points at this pixel position is visible
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     // assumption: indexes per column == 1
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         // assumption: indexes per column == 1
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         // Also invalidate the data value attributes at "position".
00774         // Otherwise the user overwrites the attributes without us noticing
00775         // it because we keep reading what's in the cache.
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     }; // ... after that, having a model at all becomes impractical
00801 
00802     // we want at least 17 samples per data point, using a prime step width
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/