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 <QtDebug>
00031 #include <QAbstractItemModel>
00032 
00033 #include "KDChartAbstractCartesianDiagram.h"
00034 #include "KDChartCartesianDiagramDataCompressor_p.h"
00035 
00036 using namespace KDChart;
00037 
00038 CartesianDiagramDataCompressor::CartesianDiagramDataCompressor( QObject* parent )
00039     : QObject( parent )
00040     , m_mode( Precise )
00041     , m_xResolution( 0 )
00042     , m_yResolution( 0 )
00043     , m_sampleStep( 0 )
00044     , m_datasetDimension( 1 )
00045 {
00046     calculateSampleStepWidth();
00047 }
00048 
00049 void CartesianDiagramDataCompressor::slotRowsInserted( const QModelIndex& parent, int start, int end )
00050 {
00051     Q_UNUSED( parent );
00052     Q_ASSERT( start <= end );
00053 
00054     const CachePosition startPos = mapToCache( start, 0 );
00055     const CachePosition endPos = mapToCache( end, 0 );
00056 
00057     start = startPos.first;
00058     end = endPos.first;
00059     
00060     static const CachePosition NullPosition( -1, -1 );
00061     if( startPos == NullPosition )
00062         return rebuildCache();
00063 
00064     for( int i = 0; i < m_data.size(); ++i )
00065     {
00066         Q_ASSERT( start >= 0 && start <= m_data[ i ].size() );
00067         m_data[ i ].insert( start, end - start + 1, DataPoint() );
00068     }
00069 }
00070 
00071 void CartesianDiagramDataCompressor::slotColumnsInserted( const QModelIndex& parent, int start, int end )
00072 {
00073     Q_UNUSED( parent );
00074     Q_ASSERT( start <= end );
00075 
00076 
00077     const CachePosition startPos = mapToCache( 0, start );
00078     const CachePosition endPos = mapToCache( 0, end );
00079 
00080     static const CachePosition NullPosition( -1, -1 );
00081     if( startPos == NullPosition )
00082         return rebuildCache();
00083 
00084     start = startPos.second;
00085     end = endPos.second;
00086 
00087     const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
00088     Q_ASSERT( start >= 0 && start <= m_data.size() );
00089     m_data.insert( start, end - start + 1, QVector< DataPoint >( rowCount ) );
00090 }
00091 
00092 void CartesianDiagramDataCompressor::slotRowsRemoved( const QModelIndex& parent, int start, int end )
00093 {
00094     Q_UNUSED( parent );
00095     Q_ASSERT( start <= end );
00096 
00097     const CachePosition startPos = mapToCache( start, 0 );
00098     const CachePosition endPos = mapToCache( end, 0 );
00099 
00100     start = startPos.first;
00101     end = endPos.first;
00102 
00103     for( int i = 0; i < m_data.size(); ++i )
00104     {
00105         m_data[ i ].remove( start, end - start + 1 );
00106     }
00107 }
00108 
00109 void CartesianDiagramDataCompressor::slotColumnsRemoved( const QModelIndex& parent, int start, int end )
00110 {
00111     Q_UNUSED( parent );
00112     Q_ASSERT( start <= end );
00113 
00114     const CachePosition startPos = mapToCache( 0, start );
00115     const CachePosition endPos = mapToCache( 0, end );
00116 
00117     start = startPos.second;
00118     end = endPos.second;
00119 
00120     m_data.remove( start, end - start + 1 );
00121 }
00122 
00123 void CartesianDiagramDataCompressor::slotModelHeaderDataChanged( Qt::Orientation orientation, int first, int last )
00124 {
00125     if( orientation != Qt::Vertical )
00126         return;
00127 
00128     const QModelIndex firstRow = m_model->index( 0, first, m_rootIndex );
00129     const QModelIndex lastRow = m_model->index( m_model->rowCount( m_rootIndex ) - 1, last, m_rootIndex );
00130     
00131     slotModelDataChanged( firstRow, lastRow );
00132 }
00133 
00134 void CartesianDiagramDataCompressor::slotModelDataChanged(
00135     const QModelIndex& topLeftIndex,
00136     const QModelIndex& bottomRightIndex )
00137 {
00138     Q_ASSERT( topLeftIndex.row() <= bottomRightIndex.row() );
00139     Q_ASSERT( topLeftIndex.column() <= bottomRightIndex.column() );
00140     CachePosition topleft = mapToCache( topLeftIndex );
00141     CachePosition bottomright = mapToCache( bottomRightIndex );
00142     for ( int row = topleft.first; row <= bottomright.first; ++row )
00143         for ( int column = topleft.second; column <= bottomright.second; ++column )
00144             invalidate( CachePosition( row, column ) );
00145 }
00146 
00147 void CartesianDiagramDataCompressor::slotModelLayoutChanged()
00148 {
00149     rebuildCache();
00150     calculateSampleStepWidth();
00151 }
00152 
00153 void CartesianDiagramDataCompressor::slotDiagramLayoutChanged( AbstractDiagram* diagramBase )
00154 {
00155     AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( diagramBase );
00156     Q_ASSERT( diagram );
00157     if ( diagram->datasetDimension() != m_datasetDimension ) {
00158         setDatasetDimension( diagram->datasetDimension() );
00159     }
00160 }
00161 
00162 int CartesianDiagramDataCompressor::modelDataColumns() const
00163 {
00164     Q_ASSERT( m_datasetDimension != 0 );
00165     // only operational if there is a model and a resolution
00166     if ( m_model ) {
00167         const int columns = m_model->columnCount( m_rootIndex ) / m_datasetDimension;
00168 
00169         if( columns != m_data.size() )
00170         {
00171             rebuildCache();
00172         }
00173 
00174         Q_ASSERT( columns == m_data.size() );
00175         return columns;
00176     } else {
00177         return 0;
00178     }
00179 }
00180 
00181 int CartesianDiagramDataCompressor::modelDataRows() const
00182 {
00183     // only operational if there is a model, columns, and a resolution
00184     if ( m_model && m_model->columnCount( m_rootIndex ) > 0 && m_xResolution > 0 ) {
00185         return m_data[0].size();
00186     } else {
00187         return 0;
00188     }
00189 }
00190 
00191 void CartesianDiagramDataCompressor::setModel( QAbstractItemModel* model )
00192 {
00193     if ( m_model != 0 && m_model != model ) {
00194         disconnect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
00195                     this, SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) );
00196         disconnect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
00197                     this, SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
00198         disconnect( m_model, SIGNAL( layoutChanged() ),
00199                     this, SLOT( slotModelLayoutChanged() ) );
00200         disconnect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ),
00201                     this, SLOT( slotRowsInserted( QModelIndex, int, int ) ) );
00202         disconnect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ),
00203                     this, SLOT( slotRowsRemoved( QModelIndex, int, int ) ) );
00204         disconnect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ),
00205                     this, SLOT( slotColumnsInserted( QModelIndex, int, int ) ) );
00206         disconnect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ),
00207                     this, SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) );
00208         m_model = 0;
00209     }
00210 
00211     if ( model != 0 ) {
00212         m_model = model;
00213         connect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
00214                  SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) );
00215         connect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
00216                  SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
00217         connect( m_model, SIGNAL( layoutChanged() ),
00218                  SLOT( slotModelLayoutChanged() ) );
00219         connect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ),
00220                  SLOT( slotRowsInserted( QModelIndex, int, int ) ) );
00221         connect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ),
00222                  SLOT( slotRowsRemoved( QModelIndex, int, int ) ) );
00223         connect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ),
00224                  SLOT( slotColumnsInserted( QModelIndex, int, int ) ) );
00225         connect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ),
00226                  SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) );
00227     }
00228 
00229     rebuildCache();
00230     calculateSampleStepWidth();
00231 }
00232 
00233 void CartesianDiagramDataCompressor::setRootIndex( const QModelIndex& root )
00234 {
00235     if ( m_rootIndex != root ) {
00236         m_rootIndex = root;
00237         rebuildCache();
00238         calculateSampleStepWidth();
00239     }
00240 }
00241 void CartesianDiagramDataCompressor::setResolution( int x, int y )
00242 {
00243     const int oldX = m_xResolution;
00244     const int oldY = m_yResolution;
00245 
00246     if( m_datasetDimension != 1 )
00247     {
00248         // just ignore the resolution in that case
00249         m_xResolution = m_model == 0 ? 0 : m_model->rowCount( m_rootIndex );
00250         m_yResolution = qMax( 0, y );
00251     }
00252     else if ( x != m_xResolution || y != m_yResolution ) {
00253         m_xResolution = qMax( 0, x );
00254         m_yResolution = qMax( 0, y );
00255         rebuildCache();
00256         calculateSampleStepWidth();
00257     }
00258 
00259     if( oldX != m_xResolution || oldY != m_yResolution )
00260     {
00261         rebuildCache();
00262         calculateSampleStepWidth();
00263     }
00264 }
00265 
00266 void CartesianDiagramDataCompressor::clearCache()
00267 {
00268     for ( int column = 0; column < m_data.size(); ++column )
00269         m_data[column].fill( DataPoint() );
00270 }
00271 
00272 void CartesianDiagramDataCompressor::rebuildCache() const
00273 {
00274     Q_ASSERT( m_datasetDimension != 0 );
00275 
00276     m_data.clear();
00277     const int columnCount = m_model ? m_model->columnCount( m_rootIndex ) / m_datasetDimension : 0;
00278     const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
00279     m_data.resize( columnCount );
00280     for ( int i = 0; i < columnCount; ++i ) {
00281         m_data[i].resize( rowCount );
00282         m_data[i].fill( DataPoint() );
00283     }
00284 }
00285 
00286 const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data( const CachePosition& position ) const
00287 {
00288     static DataPoint NullDataPoint;
00289     if ( ! isValidCachePosition( position ) ) return NullDataPoint;
00290     if ( ! isCached( position ) ) retrieveModelData( position );
00291     return m_data[ position.second ][ position.first ];
00292 }
00293 
00294 void CartesianDiagramDataCompressor::retrieveModelData( const CachePosition& position ) const
00295 {
00296     Q_ASSERT( isValidCachePosition( position ) );
00297     DataPoint result;
00298 
00299     switch(m_mode ) {
00300     case Precise:
00301     {
00302         result.hidden = true;
00303         const QModelIndexList indexes = mapToModel( position );
00304         if( m_datasetDimension != 1 )
00305         {
00306             Q_ASSERT( indexes.count() == 2 );
00307             const QModelIndex xIndex = indexes.first();
00308             const QModelIndex yIndex = indexes.last();
00309             result.index = xIndex;
00310             result.key = m_model->data( xIndex ).toDouble();
00311             result.value = m_model->data( yIndex ).toDouble();
00312         }
00313         else
00314         {
00315             if ( ! indexes.isEmpty() ) {
00316                 Q_FOREACH( const QModelIndex& index, indexes ) {
00317                     bool ok;
00318                     const QVariant valueVariant = m_model->data( index, Qt::DisplayRole );
00319                     const double value = valueVariant.toDouble( &ok );
00320                     if( ok )
00321                     {
00322                         result.value += value;
00323                         result.key += index.row();
00324                     }
00325                 }
00326                 result.index = indexes.at( 0 );
00327                 result.key /= indexes.size();
00328                 result.value /= indexes.size();
00329             }
00330         }
00331         Q_FOREACH( const QModelIndex& index, indexes )
00332         {
00333             // the point is visible if any of the points at this pixel position is visible
00334             if ( qVariantValue<bool>( m_model->data( index, DataHiddenRole ) ) == false ) {
00335                 result.hidden = false;
00336             }
00337         }
00338     }
00339     break;
00340     case SamplingSeven:
00341     default:
00342     {
00343     }
00344     break;
00345     };
00346 
00347     m_data[position.second][position.first] = result;
00348     Q_ASSERT( isCached( position ) );
00349 }
00350 
00351 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
00352     const QModelIndex& index ) const
00353 {
00354     Q_ASSERT( m_datasetDimension != 0 );
00355 
00356     static const CachePosition NullPosition( -1, -1 );
00357     if ( ! index.isValid() ) return NullPosition;
00358     return mapToCache( index.row(), index.column() );
00359 }
00360 
00361 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
00362     int row, int column ) const
00363 {
00364     Q_ASSERT( m_datasetDimension != 0 );
00365 
00366     if ( m_data.size() == 0 || m_data[0].size() == 0 ) return mapToCache( QModelIndex() );
00367     // assumption: indexes per column == 1
00368     if ( indexesPerPixel() == 0 ) return mapToCache( QModelIndex() );
00369     return CachePosition( qRound( static_cast< qreal >( row ) / indexesPerPixel() ), column / m_datasetDimension );
00370 }
00371 
00372 QModelIndexList CartesianDiagramDataCompressor::mapToModel( const CachePosition& position ) const
00373 {
00374     if ( isValidCachePosition( position ) ) {
00375         QModelIndexList indexes;
00376         if( m_datasetDimension == 2 )
00377         {
00378             indexes << m_model->index( position.first, position.second * 2, m_rootIndex );
00379             indexes << m_model->index( position.first, position.second * 2 + 1, m_rootIndex );
00380         }
00381         else
00382         {
00383         // assumption: indexes per column == 1
00384             const qreal ipp = indexesPerPixel();
00385             for ( int i = 0; i < ipp; ++i ) {
00386                 indexes << m_model->index( qRound( position.first * ipp ), position.second, m_rootIndex );
00387             }
00388         }
00389         return indexes;
00390     } else {
00391         return QModelIndexList();
00392     }
00393 }
00394 
00395 qreal CartesianDiagramDataCompressor::indexesPerPixel() const
00396 {
00397     if ( m_data.size() == 0 ) return 0;
00398     if ( m_data[0].size() == 0 ) return 0;
00399     if ( ! m_model ) return 0;
00400     return static_cast< qreal >( m_model->rowCount( m_rootIndex ) ) / static_cast< qreal >( m_data[0].size() );
00401 }
00402 
00403 bool CartesianDiagramDataCompressor::isValidCachePosition( const CachePosition& position ) const
00404 {
00405     if ( ! m_model ) return false;
00406     if ( m_data.size() == 0 || m_data[0].size() == 0 ) return false;
00407     if ( position.second < 0 || position.second >= m_data.size() ) return false;
00408     if ( position.first < 0 || position.first >= m_data[0].size() ) return false;
00409     return true;
00410 }
00411 
00412 void CartesianDiagramDataCompressor::invalidate( const CachePosition& position )
00413 {
00414     if ( isValidCachePosition( position ) )
00415         m_data[position.second][position.first] = DataPoint();
00416 }
00417 
00418 bool CartesianDiagramDataCompressor::isCached( const CachePosition& position ) const
00419 {
00420     Q_ASSERT( isValidCachePosition( position ) );
00421     return m_data[position.second][position.first].index.isValid();
00422 }
00423 
00424 void CartesianDiagramDataCompressor::calculateSampleStepWidth()
00425 {
00426     if ( m_mode == Precise ) {
00427         m_sampleStep = 1;
00428         return;
00429     }
00430 
00431     static unsigned int SomePrimes[] = {
00432         2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
00433         53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
00434         151, 211, 313, 401, 503, 607, 701, 811, 911, 1009,
00435         10037, 12911, 16001, 20011, 50021,
00436         100003, 137867, 199999, 500009, 707753, 1000003, 0
00437     }; // ... after that, having a model at all becomes impractical
00438 
00439     // we want at least 17 samples per data point, using a prime step width
00440     const double WantedSamples = 17;
00441     if ( WantedSamples > indexesPerPixel() ) {
00442         m_sampleStep = 1;
00443     } else {
00444         int i;
00445         for ( i = 0; SomePrimes[i] != 0; ++i ) {
00446             if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) {
00447                 break;
00448             }
00449         }
00450         m_sampleStep = SomePrimes[i];
00451         if ( SomePrimes[i] == 0 ) {
00452             m_sampleStep = SomePrimes[i-1];
00453         } else {
00454             m_sampleStep = SomePrimes[i];
00455         }
00456     }
00457 }
00458 
00459 void CartesianDiagramDataCompressor::setDatasetDimension( int dimension )
00460 {
00461     if ( dimension != m_datasetDimension ) {
00462         m_datasetDimension = dimension;
00463         rebuildCache();
00464         calculateSampleStepWidth();
00465     }
00466 }

Generated on Mon Sep 17 16:16:50 2007 for KD Chart 2 by  doxygen 1.5.1