KD Chart 2  [rev.2.5]
KDChartPlotterDiagramCompressor.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 "KDChartPlotterDiagramCompressor.h"
00024 
00025 #include "KDChartPlotterDiagramCompressor_p.h"
00026 #include <QtCore/QPointF>
00027 
00028 #include <limits>
00029 #include <KDABLibFakes>
00030 
00031 using namespace KDChart;
00032 
00033 qreal calculateSlope( const PlotterDiagramCompressor::DataPoint &lhs, const PlotterDiagramCompressor::DataPoint & rhs )
00034 {
00035     return ( rhs.value - lhs.value ) /  ( rhs.key - lhs.key );
00036 }
00037 
00038 PlotterDiagramCompressor::Iterator::Iterator( int dataSet, PlotterDiagramCompressor *parent )
00039     : m_parent( parent )
00040     , m_index( 0 )
00041     , m_dataset( dataSet )
00042     , m_bufferIndex( 0 )
00043     , m_rebuffer( true )
00044 {
00045     if ( m_parent )
00046     {
00047         if ( parent->rowCount() > m_dataset && parent->rowCount() > 0 )
00048         {
00049             m_buffer.append( parent->data( CachePosition( m_index, m_dataset ) ) );
00050         }
00051     }
00052     else
00053     {
00054         m_dataset = - 1;
00055         m_index = - 1;
00056     }
00057 }
00058 
00059 PlotterDiagramCompressor::Iterator::Iterator( int dataSet, PlotterDiagramCompressor *parent, QVector< DataPoint > buffer )
00060     : m_parent( parent )
00061     , m_buffer( buffer )
00062     , m_index( 0 )
00063     , m_dataset( dataSet )
00064     , m_bufferIndex( 0 )
00065     , m_rebuffer( false )
00066     , m_timeOfCreation( QDateTime::currentDateTime() )
00067 {
00068     if ( !m_parent )
00069     {
00070         m_dataset = -1 ;
00071         m_index = - 1;
00072     }
00073     else
00074     {
00075         // buffer needs to be filled
00076         if ( parent->datasetCount() > m_dataset && parent->rowCount() > 0 && m_buffer.isEmpty() )
00077         {
00078             m_buffer.append( parent->data( CachePosition( m_index, m_dataset ) ) );
00079             m_rebuffer = true;
00080         }
00081     }
00082 }
00083 
00084 PlotterDiagramCompressor::Iterator::~Iterator()
00085 {
00086     if ( m_parent )
00087     {
00088         if ( m_parent.data()->d->m_timeOfLastInvalidation < m_timeOfCreation )
00089             m_parent.data()->d->m_bufferlist[ m_dataset ] = m_buffer;
00090     }
00091 }
00092 
00093 bool PlotterDiagramCompressor::Iterator::isValid() const
00094 {
00095     if ( m_parent == 0 )
00096         return false;
00097     return m_dataset >= 0 && m_index >= 0 && m_parent.data()->rowCount() > m_index;
00098 }
00099 
00100 //PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator++()
00101 //{
00102 //    ++m_index;
00103 
00104 //    ++m_bufferIndex;
00105 //    // the version that checks dataBoundaries is seperated here, this is to avoid the runtime cost
00106 //    // of checking everytime the boundaries if thats not necessary
00107 //    if ( m_parent.data()->d->forcedBoundaries( Qt::Vertical ) || m_parent.data()->d->forcedBoundaries( Qt::Vertical ) )
00108 //    {
00109 //        if ( m_bufferIndex >= m_buffer.count()  && m_rebuffer )
00110 //        {
00111 //            if ( m_index < m_parent.data()->rowCount() )
00112 //            {
00113 //                PlotterDiagramCompressor::DataPoint dp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00114 //                if ( m_parent.data()->d->inBoundaries( Qt::Vertical, dp ) && m_parent.data()->d->inBoundaries( Qt::Horizontal, dp ) )
00115 //                {
00116 //                    m_buffer.append( dp );
00117 //                }
00118 //                else
00119 //                {
00120 //                    if ( m_index + 1 < m_parent.data()->rowCount() )
00121 //                    {
00122 //                        PlotterDiagramCompressor::DataPoint dp1 = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00123 //                        if ( m_parent.data()->d->inBoundaries( Qt::Vertical, dp1 ) && m_parent.data()->d->inBoundaries( Qt::Horizontal, dp1 ) )
00124 //                        {
00125 //                            m_buffer.append( dp );
00126 //                        }
00127 //                    }
00128 //                }
00129 //            }
00130 //        }
00131 //        else
00132 //        {
00133 //            if ( m_bufferIndex == m_buffer.count() )
00134 //                m_index = - 1;
00135 //            return *this;
00136 //        }
00137 //        PlotterDiagramCompressor::DataPoint dp;
00138 //        if ( isValid() )
00139 //            dp = m_parent.data()->data( CachePosition( m_index - 1, m_dataset ) );
00140 //        if ( m_parent )
00141 //        {
00142 //            if ( m_index >= m_parent.data()->rowCount() )
00143 //                m_index = -1;
00144 //            else
00145 //            {
00146 //                const qreal mergeRadius = m_parent.data()->d->m_mergeRadius;
00147 //                PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00148 //                while ( dp.distance( newdp ) <= mergeRadius
00149 //                        || !( m_parent.data()->d->inBoundaries( Qt::Vertical, dp ) || m_parent.data()->d->inBoundaries( Qt::Horizontal, dp ) ) )
00150 //                {
00151 //                    ++m_index;
00152 //                    if ( m_index >= m_parent.data()->rowCount() )
00153 //                    {
00154 //                        m_index = - 1;
00155 //                        break;
00156 //                    }
00157 //                    newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00158 //                }
00159 //            }
00160 //        }
00161 //    }
00162 //    else
00163 //    {
00164 //        // we have a new point in the buffer
00165 //        if ( m_bufferIndex >= m_buffer.count()  && m_rebuffer )
00166 //        {
00167 //            if ( m_index < m_parent.data()->rowCount() )
00168 //                m_buffer.append( m_parent.data()->data( CachePosition( m_index, m_dataset ) ) );
00169 //        }
00170 //        else
00171 //        {
00172 //            if ( m_bufferIndex == m_buffer.count() )
00173 //                m_index = - 1;
00174 //            return *this;
00175 //        }
00176 //        PlotterDiagramCompressor::DataPoint dp;
00177 //        if ( isValid() )
00178 //            dp = m_parent.data()->data( CachePosition( m_index - 1, m_dataset ) );
00179 //        // make sure we switch to the next point which would be in the buffer
00180 //        if ( m_parent )
00181 //        {
00182 //            PlotterDiagramCompressor *parent = m_parent.data();
00183 //            if ( m_index >= parent->rowCount() )
00184 //                m_index = -1;
00185 //            else
00186 //            {
00187 //                switch( parent->d->m_mode )
00188 //                {
00189 //                case( PlotterDiagramCompressor::DISTANCE ):
00190 //                    {
00191 //                        const qreal mergeRadius = m_parent.data()->d->m_mergeRadius;
00192 //                        PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00193 //                        while ( dp.distance( newdp ) <= mergeRadius )
00194 //                        {
00195 //                            ++m_index;
00196 //                            if ( m_index >= m_parent.data()->rowCount() )
00197 //                            {
00198 //                                m_index = - 1;
00199 //                                break;
00200 //                            }
00201 //                            newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00202 //                        }
00203 //                    }
00204 //                    break;
00205 //                case( PlotterDiagramCompressor::BOTH ):
00206 //                    {
00207 //                        const qreal mergeRadius = m_parent.data()->d->m_mergeRadius;
00208 //                        PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00209 //                        while ( dp.distance( newdp ) <= mergeRadius )
00210 //                        {
00211 //                            ++m_index;
00212 //                            if ( m_index >= m_parent.data()->rowCount() )
00213 //                            {
00214 //                                m_index = - 1;
00215 //                                break;
00216 //                            }
00217 //                            newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00218 //                        }
00219 //                    }
00220 //                    break;
00221 //                case ( PlotterDiagramCompressor::SLOPE ):
00222 //                    {
00223 //                        const qreal mergedist = parent->d->m_maxSlopeRadius;
00224 //                        qreal oldSlope = 0;
00225 //                        qreal newSlope = 0;
00226 
00227 //                        PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00228 //                        PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
00229 //                        if ( m_bufferIndex > 1 )
00230 //                        {
00231 //                            oldSlope = calculateSlope( m_buffer[ m_bufferIndex - 2 ], m_buffer[ m_bufferIndex - 1 ] );
00232 //                            newSlope = calculateSlope( m_buffer[ m_bufferIndex - 1 ], newdp );
00233 //                        }
00234 //                        bool first = true;
00235 //                        while ( qAbs( newSlope - oldSlope ) < mergedist )
00236 //                        {
00237 //                            ++m_index;
00238 //                            if ( m_index >= m_parent.data()->rowCount() )
00239 //                            {
00240 //                                m_index = - 1;
00241 //                                break;
00242 //                            }
00243 //                            if ( first )
00244 //                            {
00245 //                                oldSlope = newSlope;
00246 //                                first = false;
00247 //                            }
00248 //                            olddp = newdp;
00249 //                            newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
00250 //                            newSlope = calculateSlope( olddp, newdp );
00251 //                        }
00252 //                    }
00253 //                    break;
00254 //                default:
00255 //                    Q_ASSERT( false );
00256 //                }
00257 //            }
00258 //        }
00259 //    }
00260 //    return *this;
00261 //}
00262 
00263 void PlotterDiagramCompressor::Iterator::handleSlopeForward( const DataPoint &dp )
00264 {
00265     PlotterDiagramCompressor* parent = m_parent.data();
00266     const qreal mergedist = parent->d->m_maxSlopeRadius;
00267     qreal oldSlope = 0;
00268     qreal newSlope = 0;
00269 
00270     PlotterDiagramCompressor::DataPoint newdp = dp;
00271     PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
00272     if ( m_bufferIndex > 1 )
00273     {
00274         //oldSlope = calculateSlope( m_buffer[ m_bufferIndex - 2 ], m_buffer[ m_bufferIndex - 1 ] );
00275         //newSlope = calculateSlope( m_buffer[ m_bufferIndex - 1 ], newdp );
00276         oldSlope = calculateSlope( parent->data( CachePosition( m_index - 2, m_dataset ) ) , parent->data( CachePosition( m_index - 1, m_dataset ) ) );
00277         newSlope = calculateSlope( parent->data( CachePosition( m_index - 1, m_dataset ) ), newdp );
00278         qreal accumulatedDist = qAbs( newSlope - oldSlope );
00279         qreal olddist = accumulatedDist;
00280         qreal newdist;
00281         int counter = 0;
00282         while ( accumulatedDist < mergedist )
00283         {
00284             ++m_index;
00285             if ( m_index >= m_parent.data()->rowCount() )
00286             {
00287                 m_index = - 1;
00288                 if ( m_buffer.last() != parent->data( CachePosition( parent->rowCount() -1, m_dataset ) ) )
00289                     m_index = parent->rowCount();
00290                 break;
00291             }
00292             oldSlope = newSlope;
00293             olddp = newdp;
00294             newdp = parent->data( CachePosition( m_index, m_dataset ) );
00295             newSlope = calculateSlope( olddp, newdp );
00296             newdist = qAbs( newSlope - oldSlope );
00297             if ( olddist == newdist )
00298             {
00299                 ++counter;
00300             }
00301             else
00302             {
00303                 if ( counter > 10 )
00304                     break;
00305             }
00306             accumulatedDist += newdist;
00307             olddist = newdist;
00308         }
00309         m_buffer.append( newdp );
00310     }
00311     else
00312         m_buffer.append( dp );
00313 }
00314 
00315 PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator++()
00316 {
00317     PlotterDiagramCompressor* parent = m_parent.data();
00318     Q_ASSERT( parent );
00319     const int count = parent->rowCount();
00320     //increment the indexes
00321     ++m_index;
00322     ++m_bufferIndex;
00323     //if the index reached the end of the datamodel make this iterator an enditerator
00324     //and make sure the buffer was not already build, if thats the case its not necessary
00325     //to rebuild it and it would be hard to extend it as we had to know where m_index was
00326     if ( m_index >= count || ( !m_rebuffer && m_bufferIndex == m_buffer.count() ) )
00327     {
00328         if ( m_bufferIndex == m_buffer.count() )
00329         {
00330             if ( m_buffer.last() != parent->data( CachePosition( parent->rowCount() -1, m_dataset ) ) )
00331                 m_index = parent->rowCount();
00332             else
00333                 m_index = - 1;
00334             ++m_bufferIndex;
00335         }
00336         else
00337             m_index = -1;
00338     }
00339     //if we reached the end of the buffer continue filling the buffer
00340     if ( m_bufferIndex == m_buffer.count() && m_index >= 0 && m_rebuffer )
00341     {
00342         PlotterDiagramCompressor::DataPoint dp = parent->data( CachePosition( m_index, m_dataset ) );
00343         if ( parent->d->inBoundaries( Qt::Vertical, dp ) && parent->d->inBoundaries( Qt::Horizontal, dp ) )
00344         {
00345             if ( parent->d->m_mode == PlotterDiagramCompressor::SLOPE )
00346                 handleSlopeForward( dp );
00347         }
00348         else
00349         {
00350             m_index = -1;
00351         }
00352     }
00353     return *this;
00354 }
00355 
00356 PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::Iterator::operator++( int )
00357 {
00358     Iterator result = *this;
00359     ++result;
00360     return result;
00361 }
00362 
00363 PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator += ( int value )
00364 {    
00365     for ( int index = m_index; index + value != m_index; ++( *this ) ){};
00366     return *this;
00367 }
00368 
00369 PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator--()
00370 {    
00371     --m_index;
00372     --m_bufferIndex;
00373     return *this;
00374 }
00375 
00376 PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::Iterator::operator--( int )
00377 {
00378     Iterator result = *this;
00379     --result;
00380     return result;
00381 }
00382 
00383 PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator-=( int value )
00384 {
00385     m_index -= value;
00386     return *this;
00387 }
00388 
00389 PlotterDiagramCompressor::DataPoint PlotterDiagramCompressor::Iterator::operator*()
00390 {
00391     if ( !m_parent )
00392         return PlotterDiagramCompressor::DataPoint();
00393     Q_ASSERT( m_parent );
00394     if ( m_index == m_parent.data()->rowCount() )
00395         return m_parent.data()->data( CachePosition( m_parent.data()->rowCount() - 1 , m_dataset ) );
00396     return m_buffer[ m_bufferIndex ];
00397 }
00398 
00399 bool PlotterDiagramCompressor::Iterator::operator==( const PlotterDiagramCompressor::Iterator &other ) const
00400 {
00401     return m_parent.data() == other.m_parent.data() && m_index == other.m_index && m_dataset == other.m_dataset;
00402 }
00403 
00404 bool PlotterDiagramCompressor::Iterator::operator!=( const PlotterDiagramCompressor::Iterator &other ) const
00405 {
00406     return ! ( *this == other );
00407 }
00408 
00409 void PlotterDiagramCompressor::Iterator::invalidate()
00410 {
00411     m_dataset = - 1;
00412 }
00413 
00414 PlotterDiagramCompressor::Private::Private( PlotterDiagramCompressor *parent )
00415     : m_parent( parent )
00416     , m_model( 0 )
00417     , m_mergeRadius( 0.1 )
00418     , m_maxSlopeRadius( 0.1 )
00419     , m_boundary( qMakePair( QPointF( std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN() )
00420                                       , QPointF( std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN() ) ) )
00421     , m_forcedXBoundaries( qMakePair( std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN() ) )
00422     , m_forcedYBoundaries( qMakePair( std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN() ) )
00423     , m_mode( PlotterDiagramCompressor::SLOPE )
00424 {
00425 
00426 }
00427 
00428 void PlotterDiagramCompressor::Private::setModelToZero()
00429 {
00430     m_model = 0;
00431 }
00432 
00433 inline bool inBoundary( const QPair< qreal, qreal > &bounds, qreal value )
00434 {
00435     return bounds.first <= value && value <= bounds.second;
00436 }
00437 
00438 bool PlotterDiagramCompressor::Private::inBoundaries( Qt::Orientation orient, const PlotterDiagramCompressor::DataPoint &dp ) const
00439 {
00440     if ( orient == Qt::Vertical && forcedBoundaries( Qt::Vertical ) )
00441     {
00442         return inBoundary( m_forcedYBoundaries, dp.value );
00443     }
00444     else if ( forcedBoundaries( Qt::Horizontal ) )
00445     {
00446         return inBoundary( m_forcedXBoundaries, dp.key );
00447     }
00448     return true;
00449 }
00450 
00453 //void PlotterDiagramCompressor::Private::rowsInserted( const QModelIndex& /*parent*/, int start, int end )
00454 //{
00455 
00456 //    if( m_bufferlist.count() > 0 && !m_bufferlist[ 0 ].isEmpty() && start < m_bufferlist[ 0 ].count() )
00457 //    {
00458 //        calculateDataBoundaries();
00459 //        clearBuffer();
00460 //        return;
00461 //    }
00462 //    // we are handling appends only here, a prepend might be added, insert is expensive if not needed
00463 //    qreal minX = std::numeric_limits< qreal >::max();
00464 //    qreal minY = std::numeric_limits< qreal >::max();
00465 //    qreal maxX = std::numeric_limits< qreal >::min();
00466 //    qreal maxY = std::numeric_limits< qreal >::min();
00467 //    for ( int dataset = 0; dataset < m_bufferlist.size(); ++dataset )
00468 //    {
00469 //        PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last();
00470 
00471 //        qreal oldSlope = 0;
00472 //        qreal newSlope = 0;
00473 //        PlotterDiagramCompressor::DataPoint newdp = m_parent->data( CachePosition( start, dataset ) );
00474 //        PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
00475 //        const int datacount = m_bufferlist[ dataset ].count();
00476 //        if ( m_mode != PlotterDiagramCompressor::DISTANCE && m_bufferlist[ dataset ].count() > 1 )
00477 //        {
00478 //            oldSlope = calculateSlope( m_bufferlist[ dataset ][ datacount - 2 ], m_bufferlist[ dataset ][ datacount - 1 ] );
00479 //            newSlope = calculateSlope( m_bufferlist[ dataset ][ datacount - 1 ], newdp );
00480 //        }
00481 //        bool first = true;
00482 //        for ( int row = start; row <= end; ++row )
00483 //        {
00484 //            PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) );
00485 //            const bool checkcur = inBoundaries( Qt::Vertical, curdp ) && inBoundaries( Qt::Horizontal, curdp );
00486 //            const bool checkpred = inBoundaries( Qt::Vertical, predecessor ) && inBoundaries( Qt::Horizontal, predecessor );
00487 //            const bool check = checkcur || checkpred;
00488 //            switch( m_mode )
00489 //            {
00490 //            case( PlotterDiagramCompressor::BOTH ):
00491 //                {
00492 //                    if ( predecessor.distance( curdp ) > m_mergeRadius && check )
00493 //                    {
00494 //                        if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
00495 //                        {
00496 //                            m_bufferlist[ dataset ].append( curdp );
00497 //                        }
00498 //                        else if( !m_bufferlist[ dataset ].isEmpty() )
00499 //                        {
00500 //                            m_bufferlist[ dataset ].insert( row, curdp );
00501 //                        }
00502 //                        predecessor = curdp;
00503 //                        minX = qMin( curdp.key, m_boundary.first.x() );
00504 //                        minY = qMin( curdp.value, m_boundary.first.y() );
00505 //                        maxX = qMax( curdp.key, m_boundary.second.x() );
00506 //                        maxY = qMax( curdp.value, m_boundary.second.y() );
00507 //                    }
00508 //                }
00509 //                break;
00510 //            case ( PlotterDiagramCompressor::DISTANCE ):
00511 //                {
00512 //                    if ( predecessor.distance( curdp ) > m_mergeRadius && check )
00513 //                    {
00514 //                        if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
00515 //                        {
00516 //                            m_bufferlist[ dataset ].append( curdp );
00517 //                        }
00518 //                        else if( !m_bufferlist[ dataset ].isEmpty() )
00519 //                        {
00520 //                            m_bufferlist[ dataset ].insert( row, curdp );
00521 //                        }
00522 //                        predecessor = curdp;
00523 //                        minX = qMin( curdp.key, m_boundary.first.x() );
00524 //                        minY = qMin( curdp.value, m_boundary.first.y() );
00525 //                        maxX = qMax( curdp.key, m_boundary.second.x() );
00526 //                        maxY = qMax( curdp.value, m_boundary.second.y() );
00527 //                    }
00528 //                }
00529 //                break;
00530 //            case( PlotterDiagramCompressor::SLOPE ):
00531 //                {
00532 //                    if ( check && qAbs( newSlope - oldSlope ) >= m_maxSlopeRadius )
00533 //                    {
00534 //                        if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
00535 //                        {
00536 //                            m_bufferlist[ dataset ].append( curdp );
00537 //                            oldSlope = newSlope;
00538 //                        }
00539 //                        else if( !m_bufferlist[ dataset ].isEmpty() )
00540 //                        {
00541 //                            m_bufferlist[ dataset ].insert( row, curdp );
00542 //                            oldSlope = newSlope;
00543 //                        }
00544 
00545 //                        predecessor = curdp;
00546 //                        minX = qMin( curdp.key, m_boundary.first.x() );
00547 //                        minY = qMin( curdp.value, m_boundary.first.y() );
00548 //                        maxX = qMax( curdp.key, m_boundary.second.x() );
00549 //                        maxY = qMax( curdp.value, m_boundary.second.y() );
00550 
00551 //                        if ( first )
00552 //                        {
00553 //                            oldSlope = newSlope;
00554 //                            first = false;
00555 //                        }
00556 //                        olddp = newdp;
00557 //                        newdp = m_parent->data( CachePosition( row, dataset ) );
00558 //                        newSlope = calculateSlope( olddp, newdp );
00559 //                    }
00560 //                }
00561 //                break;
00562 //            }
00563 //        }
00564 //    }
00565 //    setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) );
00566 //    emit m_parent->rowCountChanged();
00567 //}
00568 #include <QDebug>
00569 // TODO this is not threadsafe do never try to invoke the painting in a different thread than this
00570 // method
00571 void PlotterDiagramCompressor::Private::rowsInserted( const QModelIndex& /*parent*/, int start, int end )
00572 {
00573 
00574     //Q_ASSERT( std::numeric_limits<qreal>::quiet_NaN() < 5 || std::numeric_limits<qreal>::quiet_NaN() > 5 );
00575     //Q_ASSERT( 5 == qMin( std::numeric_limits<qreal>::quiet_NaN(),  5.0 ) );
00576     //Q_ASSERT( 5 == qMax( 5.0, std::numeric_limits<qreal>::quiet_NaN() ) );
00577     if( m_bufferlist.count() > 0 && !m_bufferlist[ 0 ].isEmpty() && start < m_bufferlist[ 0 ].count() )
00578     {
00579         calculateDataBoundaries();
00580         clearBuffer();
00581         return;
00582     }
00583 
00584     // we are handling appends only here, a prepend might be added, insert is expensive if not needed
00585     qreal minX = m_boundary.first.x();
00586     qreal minY = m_boundary.first.y();
00587     qreal maxX = m_boundary.second.x();
00588     qreal maxY = m_boundary.second.y();
00589     for ( int dataset = 0; dataset < m_bufferlist.size(); ++dataset )
00590     {
00591         if ( m_mode == PlotterDiagramCompressor::SLOPE )
00592         {
00593             PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last();
00594             qreal oldSlope = 0;
00595             qreal newSlope = 0;
00596             int counter = 0;            
00597 
00598             PlotterDiagramCompressor::DataPoint newdp = m_parent->data( CachePosition( start, dataset ) );
00599             PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
00600             if ( start  > 1 )
00601             {
00602                 oldSlope = calculateSlope( m_parent->data( CachePosition( start - 2, dataset ) ), m_parent->data( CachePosition( start - 1, dataset ) ) );
00603                 olddp = m_parent->data( CachePosition( start - 1, dataset ) );
00604             }
00605             else
00606             {
00607                 m_bufferlist[ dataset ].append( newdp );
00608                 minX = qMin( minX, newdp.key );
00609                 minY = qMin( minY, newdp.value );
00610                 maxX = qMax( newdp.key, maxX );
00611                 maxY = qMax( newdp.value, maxY );
00612                 continue;
00613             }
00614 
00615             qreal olddist = 0;
00616             qreal newdist = 0;            
00617             for ( int row = start; row <= end; ++row )
00618             {
00619                 PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) );
00620                 newdp = curdp;
00621                 newSlope = calculateSlope( olddp, newdp );
00622                 olddist = newdist;
00623                 newdist = qAbs( newSlope - oldSlope );
00624                 m_accumulatedDistances[ dataset ] += newdist;
00625                 const bool checkcur = inBoundaries( Qt::Vertical, curdp ) && inBoundaries( Qt::Horizontal, curdp );
00626                 const bool checkpred = inBoundaries( Qt::Vertical, predecessor ) && inBoundaries( Qt::Horizontal, predecessor );
00627                 const bool check = checkcur || checkpred;
00628 
00629                 if ( m_accumulatedDistances[ dataset ] >= m_maxSlopeRadius && check )
00630                 {
00631                     if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
00632                     {
00633                         m_bufferlist[ dataset ].append( curdp );
00634                     }
00635                     else if( !m_bufferlist[ dataset ].isEmpty() )
00636                     {
00637                         m_bufferlist[ dataset ].insert( row, curdp );
00638                     }
00639                     predecessor = curdp;
00640                     m_accumulatedDistances[ dataset ] = 0;
00641                 }
00642                 minX = qMin( minX, curdp.key );
00643                 minY = qMin( minY, curdp.value );
00644                 maxX = qMax( curdp.key, maxX );
00645                 maxY = qMax( curdp.value, maxY );
00646 
00647                 oldSlope = newSlope;
00648                 olddp = newdp;
00649                 if ( olddist == newdist )
00650                 {
00651                     ++counter;
00652                 }
00653                 else
00654                 {
00655                     if ( counter > 10 )
00656                     {
00657                         m_bufferlist[ dataset ].append( curdp );
00658                         predecessor = curdp;
00659                         m_accumulatedDistances[ dataset ] = 0;
00660                     }
00661                 }
00662             }
00663             setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) );
00664         }
00665         else
00666         {
00667         PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last();
00668 
00669         for ( int row = start; row <= end; ++row )
00670         {
00671             PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) );
00672             const bool checkcur = inBoundaries( Qt::Vertical, curdp ) && inBoundaries( Qt::Horizontal, curdp );
00673             const bool checkpred = inBoundaries( Qt::Vertical, predecessor ) && inBoundaries( Qt::Horizontal, predecessor );
00674             const bool check = checkcur || checkpred;
00675             if ( predecessor.distance( curdp ) > m_mergeRadius && check )
00676             {
00677                 if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
00678                 {
00679                     m_bufferlist[ dataset ].append( curdp );
00680                 }
00681                 else if( !m_bufferlist[ dataset ].isEmpty() )
00682                 {
00683                     m_bufferlist[ dataset ].insert( row, curdp );
00684                 }
00685                 predecessor = curdp;
00686                 qreal minX = qMin( curdp.key, m_boundary.first.x() );
00687                 qreal minY = qMin( curdp.value, m_boundary.first.y() );
00688                 qreal maxX = qMax( curdp.key, m_boundary.second.x() );
00689                 qreal maxY = qMax( curdp.value, m_boundary.second.y() );
00690                 setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) );
00691             }
00692         }
00693         }
00694     }
00695     emit m_parent->rowCountChanged();
00696 }
00697 
00698 
00699 void PlotterDiagramCompressor::setCompressionModel( CompressionMode value )
00700 {
00701     Q_ASSERT( d );
00702     if ( d->m_mode != value )
00703     {
00704         d->m_mode = value;
00705         d->clearBuffer();
00706         emit rowCountChanged();
00707     }
00708 }
00709 
00710 void PlotterDiagramCompressor::Private::setBoundaries( const Boundaries & bound )
00711 {
00712     if ( bound != m_boundary )
00713     {
00714         m_boundary = bound;
00715         emit m_parent->boundariesChanged();
00716     }
00717 }
00718 
00719 void PlotterDiagramCompressor::Private::calculateDataBoundaries()
00720 {
00721     if ( !forcedBoundaries( Qt::Vertical ) || !forcedBoundaries( Qt::Horizontal ) )
00722     {
00723         qreal minX = std::numeric_limits<qreal>::quiet_NaN();
00724         qreal minY = std::numeric_limits<qreal>::quiet_NaN();
00725         qreal maxX = std::numeric_limits<qreal>::quiet_NaN();
00726         qreal maxY = std::numeric_limits<qreal>::quiet_NaN();
00727         for ( int dataset = 0; dataset < m_parent->datasetCount(); ++dataset )
00728         {
00729             for ( int row = 0; row < m_parent->rowCount(); ++ row )
00730             {
00731                 PlotterDiagramCompressor::DataPoint dp = m_parent->data( CachePosition( row, dataset ) );
00732                 minX = qMin( minX, dp.key );
00733                 minY = qMin( minY, dp.value );
00734                 maxX = qMax( dp.key, maxX );
00735                 maxY = qMax( dp.value, maxY );
00736                 Q_ASSERT( !ISNAN( minX ) );
00737                 Q_ASSERT( !ISNAN( minY ) );
00738                 Q_ASSERT( !ISNAN( maxX ) );
00739                 Q_ASSERT( !ISNAN( maxY ) );
00740             }
00741         }
00742         if ( forcedBoundaries( Qt::Vertical ) )
00743         {
00744             minY = m_forcedYBoundaries.first;
00745             maxY = m_forcedYBoundaries.second;
00746         }
00747         if ( forcedBoundaries( Qt::Horizontal ) )
00748         {
00749             minX = m_forcedXBoundaries.first;
00750             maxX = m_forcedXBoundaries.second;
00751         }
00752         setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) );
00753     }
00754 }
00755 
00756 QModelIndexList PlotterDiagramCompressor::Private::mapToModel( const CachePosition &pos )
00757 {
00758     QModelIndexList indexes;
00759     QModelIndex index;
00760     index = m_model->index( pos.first, pos.second * 2, QModelIndex() );
00761     Q_ASSERT( index.isValid() );
00762     indexes << index;
00763     index = m_model->index( pos.first, pos.second * 2 + 1, QModelIndex() );
00764     Q_ASSERT( index.isValid() );
00765     indexes << index;
00766     return indexes;
00767 }
00768 
00769 bool PlotterDiagramCompressor::Private::forcedBoundaries( Qt::Orientation orient ) const
00770 {
00771     if ( orient == Qt::Vertical )
00772         return !ISNAN( m_forcedYBoundaries.first ) && !ISNAN( m_forcedYBoundaries.second );
00773     else
00774         return !ISNAN( m_forcedXBoundaries.first ) && !ISNAN( m_forcedXBoundaries.second );
00775 }
00776 
00777 void PlotterDiagramCompressor::Private::clearBuffer()
00778 {
00779     //TODO all iterator have to be invalid after this operation
00780     //TODO make sure there are no regressions, the timeOfLastInvalidation should stop iterators from
00781     // corrupting the cache
00782     m_bufferlist.clear();
00783     m_bufferlist.resize( m_parent->datasetCount() );
00784     m_accumulatedDistances.clear();
00785     m_accumulatedDistances.resize( m_parent->datasetCount() );
00786     m_timeOfLastInvalidation = QDateTime::currentDateTime();
00787 }
00788 
00789 PlotterDiagramCompressor::PlotterDiagramCompressor(QObject *parent)
00790     : QObject(parent)
00791     , d( new Private( this ) )
00792 {
00793 }
00794 
00795 void PlotterDiagramCompressor::setForcedDataBoundaries( const QPair< qreal, qreal > &bounds, Qt::Orientation direction )
00796 {
00797     if ( direction == Qt::Vertical )
00798     {
00799         d->m_forcedYBoundaries = bounds;
00800     }
00801     else
00802     {
00803         d->m_forcedXBoundaries = bounds;
00804     }
00805     d->clearBuffer();
00806     emit boundariesChanged();
00807 }
00808 
00809 QAbstractItemModel* PlotterDiagramCompressor::model() const
00810 {
00811     Q_ASSERT( d );
00812     return d->m_model;
00813 }
00814 
00815 void PlotterDiagramCompressor::setModel( QAbstractItemModel *model )
00816 {
00817     Q_ASSERT( d );
00818     if ( d->m_model )
00819     {
00820         d->m_model->disconnect( this );
00821         d->m_model->disconnect( d );
00822     }
00823     d->m_model = model;
00824     if ( d->m_model)
00825     {
00826         d->m_bufferlist.resize( datasetCount() );
00827         d->m_accumulatedDistances.resize( datasetCount() );
00828         d->calculateDataBoundaries();
00829         connect( d->m_model, SIGNAL( rowsInserted ( QModelIndex, int, int ) ), d, SLOT( rowsInserted( QModelIndex, int, int ) ) );
00830         connect( d->m_model, SIGNAL( modelReset() ), d, SLOT( clearBuffer() ) );
00831         connect( d->m_model, SIGNAL( destroyed( QObject* ) ), d, SLOT( setModelToZero() ) );
00832     }
00833 }
00834 
00835 PlotterDiagramCompressor::DataPoint PlotterDiagramCompressor::data( const CachePosition& pos ) const
00836 {
00837     DataPoint point;
00838     QModelIndexList indexes = d->mapToModel( pos );
00839     Q_ASSERT( indexes.count() == 2 );
00840     QVariant yValue = d->m_model->data( indexes.last() );
00841     QVariant xValue = d->m_model->data( indexes.first() );
00842     Q_ASSERT( xValue.isValid() );
00843     Q_ASSERT( yValue.isValid() );
00844     bool ok = false;
00845     point.key = xValue.toReal( &ok );
00846     Q_ASSERT( ok );
00847     ok = false;
00848     point.value = yValue.toReal( &ok );
00849     Q_ASSERT( ok );
00850     point.index = indexes.first();
00851     return point;
00852 }
00853 
00854 void PlotterDiagramCompressor::setMergeRadius( qreal radius )
00855 {
00856     if ( d->m_mergeRadius != radius )
00857     {
00858         d->m_mergeRadius = radius;
00859         if ( d->m_mode != PlotterDiagramCompressor::SLOPE )
00860             emit rowCountChanged();
00861     }
00862 }
00863 
00864 void PlotterDiagramCompressor::setMaxSlopeChange( qreal value )
00865 {
00866     if ( d->m_maxSlopeRadius != value )
00867     {
00868         d->m_maxSlopeRadius = value;
00869         emit boundariesChanged();
00870     }
00871 }
00872 
00873 qreal PlotterDiagramCompressor::maxSlopeChange() const
00874 {
00875     return d->m_maxSlopeRadius;
00876 }
00877 
00878 void PlotterDiagramCompressor::setMergeRadiusPercentage( qreal radius )
00879 {
00880     Boundaries bounds = dataBoundaries();
00881     const qreal width = radius * ( bounds.second.x() - bounds.first.x() );
00882     const qreal height = radius * ( bounds.second.y() - bounds.first.y() );
00883     const qreal realRadius = std::sqrt( width * height );
00884     setMergeRadius( realRadius );
00885 }
00886 
00887 int PlotterDiagramCompressor::rowCount() const
00888 {
00889     return d->m_model ? d->m_model->rowCount() : 0;
00890 }
00891 
00892 void PlotterDiagramCompressor::cleanCache()
00893 {
00894     d->clearBuffer();
00895 }
00896 
00897 int PlotterDiagramCompressor::datasetCount() const
00898 {
00899     if ( d->m_model && d->m_model->columnCount() == 0 )
00900         return 0;
00901     return d->m_model ? ( d->m_model->columnCount() + 1 ) / 2  : 0;
00902 }
00903 
00904 QPair< QPointF, QPointF > PlotterDiagramCompressor::dataBoundaries() const
00905 {
00906     Boundaries bounds = d->m_boundary;
00907     if ( d->forcedBoundaries( Qt::Vertical ) )
00908     {
00909         bounds.first.setY( d->m_forcedYBoundaries.first );
00910         bounds.second.setY( d->m_forcedYBoundaries.second );
00911     }
00912     if ( d->forcedBoundaries( Qt::Horizontal ) )
00913     {
00914         bounds.first.setX( d->m_forcedXBoundaries.first );
00915         bounds.second.setX( d->m_forcedXBoundaries.second );
00916     }
00917     return bounds;
00918 }
00919 
00920 PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::begin( int dataSet )
00921 {
00922     Q_ASSERT( dataSet >= 0 && dataSet < d->m_bufferlist.count() );
00923     return Iterator( dataSet, this, d->m_bufferlist[ dataSet ] );
00924 }
00925 
00926 PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::end( int dataSet )
00927 {
00928     Iterator it( dataSet, this );
00929     it.m_index = -1;
00930     return it;
00931 }
 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/