KDChartCartesianDiagramDataCompressor_p.cpp

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

Generated on Thu Mar 4 23:19:10 2010 for KD Chart 2 by  doxygen 1.5.4