KD Chart 2
[rev.2.5]
|
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 }