KD Chart 2  [rev.2.5]
KDChartCartesianDiagramDataCompressor_p.cpp
Go to the documentation of this file.
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 }
 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/