23 #include "KDChartCartesianDiagramDataCompressor_p.h"
26 #include <QAbstractItemModel>
30 #include <KDABLibFakes>
32 using namespace KDChart;
35 CartesianDiagramDataCompressor::CartesianDiagramDataCompressor(
QObject* parent )
41 , m_datasetDimension( 1 )
43 calculateSampleStepWidth();
47 static bool contains(
const CartesianDiagramDataCompressor::AggregatedDataValueAttributes& aggregated,
50 CartesianDiagramDataCompressor::AggregatedDataValueAttributes::const_iterator it = aggregated.constBegin();
51 for ( ; it != aggregated.constEnd(); ++it ) {
52 if ( it.value() == attributes ) {
59 CartesianDiagramDataCompressor::AggregatedDataValueAttributes CartesianDiagramDataCompressor::aggregatedAttrs(
61 const QModelIndex & index,
62 const CachePosition& position )
const
65 DataValueAttributesCache::const_iterator i = m_dataValueAttributesCache.constFind( position );
66 if ( i != m_dataValueAttributesCache.constEnd() ) {
71 CartesianDiagramDataCompressor::AggregatedDataValueAttributes aggregated;
72 KDAB_FOREACH(
const QModelIndex& neighborIndex, mapToModel( position ) ) {
78 if ( !
contains( aggregated, attrs ) ) {
79 aggregated[ neighborIndex ] = attrs;
84 if ( aggregated.isEmpty() ) {
88 m_dataValueAttributesCache[position] = aggregated;
92 bool CartesianDiagramDataCompressor::prepareDataChange(
const QModelIndex& parent,
bool isRows,
93 int* start,
int* end )
95 if ( parent != m_rootIndex ) {
98 Q_ASSERT( *start <= *end );
100 CachePosition startPos = isRows ? mapToCache( *start, 0 ) : mapToCache( 0, *start );
101 CachePosition endPos = isRows ? mapToCache( *end, 0 ) : mapToCache( 0, *end );
103 static const CachePosition nullPosition;
104 if ( startPos == nullPosition ) {
106 startPos = isRows ? mapToCache( *start, 0 ) : mapToCache( 0, *start );
107 endPos = isRows ? mapToCache( *end, 0 ) : mapToCache( 0, *end );
110 if ( startPos == nullPosition ) {
115 *start = isRows ? startPos.row : startPos.column;
116 *end = isRows ? endPos.row : endPos.column;
120 void CartesianDiagramDataCompressor::slotRowsAboutToBeInserted(
const QModelIndex& parent,
int start,
int end )
122 if ( !prepareDataChange( parent,
true, &start, &end ) ) {
125 for (
int i = 0; i < m_data.size(); ++i )
127 Q_ASSERT( start >= 0 && start <= m_data[ i ].size() );
128 m_data[ i ].insert( start, end - start + 1, DataPoint() );
132 void CartesianDiagramDataCompressor::slotRowsInserted(
const QModelIndex& parent,
int start,
int end )
134 if ( !prepareDataChange( parent,
true, &start, &end ) ) {
137 for (
int i = 0; i < m_data.size(); ++i )
139 for (
int j = start; j < m_data[i].size(); ++j ) {
140 retrieveModelData( CachePosition( j, i ) );
145 void CartesianDiagramDataCompressor::slotColumnsAboutToBeInserted(
const QModelIndex& parent,
int start,
int end )
147 if ( !prepareDataChange( parent,
false, &start, &end ) ) {
150 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
151 Q_ASSERT( start >= 0 && start <= m_data.size() );
155 void CartesianDiagramDataCompressor::slotColumnsInserted(
const QModelIndex& parent,
int start,
int end )
157 if ( !prepareDataChange( parent,
false, &start, &end ) ) {
160 for (
int i = start; i < m_data.size(); ++i )
162 for (
int j = 0; j < m_data[i].size(); ++j ) {
163 retrieveModelData( CachePosition( j, i ) );
168 void CartesianDiagramDataCompressor::slotRowsAboutToBeRemoved(
const QModelIndex& parent,
int start,
int end )
170 if ( !prepareDataChange( parent,
true, &start, &end ) ) {
173 for (
int i = 0; i < m_data.size(); ++i ) {
174 m_data[ i ].remove( start, end - start + 1 );
178 void CartesianDiagramDataCompressor::slotRowsRemoved(
const QModelIndex& parent,
int start,
int end )
180 if ( parent != m_rootIndex )
182 Q_ASSERT( start <= end );
185 CachePosition startPos = mapToCache( start, 0 );
186 static const CachePosition nullPosition;
187 if ( startPos == nullPosition ) {
193 for (
int i = 0; i < m_data.size(); ++i ) {
194 for (
int j = startPos.row; j < m_data[i].size(); ++j ) {
195 retrieveModelData( CachePosition( j, i ) );
200 void CartesianDiagramDataCompressor::slotColumnsAboutToBeRemoved(
const QModelIndex& parent,
int start,
int end )
202 if ( !prepareDataChange( parent,
false, &start, &end ) ) {
205 m_data.remove( start, end - start + 1 );
208 void CartesianDiagramDataCompressor::slotColumnsRemoved(
const QModelIndex& parent,
int start,
int end )
210 if ( parent != m_rootIndex )
212 Q_ASSERT( start <= end );
215 const CachePosition startPos = mapToCache( 0, start );
217 static const CachePosition nullPosition;
218 if ( startPos == nullPosition ) {
224 for (
int i = startPos.column; i < m_data.size(); ++i ) {
225 for (
int j = 0; j < m_data[i].size(); ++j ) {
226 retrieveModelData( CachePosition( j, i ) );
231 void CartesianDiagramDataCompressor::slotModelHeaderDataChanged( Qt::Orientation orientation,
int first,
int last )
233 if ( orientation != Qt::Vertical )
236 if ( m_model->rowCount( m_rootIndex ) > 0 ) {
237 const QModelIndex firstRow = m_model->index( 0, first, m_rootIndex );
238 const QModelIndex lastRow = m_model->index( m_model->rowCount( m_rootIndex ) - 1, last, m_rootIndex );
240 slotModelDataChanged( firstRow, lastRow );
244 void CartesianDiagramDataCompressor::slotModelDataChanged(
245 const QModelIndex& topLeftIndex,
246 const QModelIndex& bottomRightIndex )
248 if ( topLeftIndex.parent() != m_rootIndex )
250 Q_ASSERT( topLeftIndex.parent() == bottomRightIndex.parent() );
251 Q_ASSERT( topLeftIndex.row() <= bottomRightIndex.row() );
252 Q_ASSERT( topLeftIndex.column() <= bottomRightIndex.column() );
253 CachePosition topleft = mapToCache( topLeftIndex );
254 CachePosition bottomright = mapToCache( bottomRightIndex );
255 for (
int row = topleft.row; row <= bottomright.row; ++row )
256 for (
int column = topleft.column; column <= bottomright.column; ++column )
257 invalidate( CachePosition( row, column ) );
260 void CartesianDiagramDataCompressor::slotModelLayoutChanged()
263 calculateSampleStepWidth();
266 void CartesianDiagramDataCompressor::slotDiagramLayoutChanged(
AbstractDiagram* diagramBase )
275 int CartesianDiagramDataCompressor::modelDataColumns()
const
277 Q_ASSERT( m_datasetDimension != 0 );
280 const int columns = m_model->columnCount( m_rootIndex ) / m_datasetDimension;
287 Q_ASSERT( columns == m_data.size() );
294 int CartesianDiagramDataCompressor::modelDataRows()
const
297 if ( m_model && m_model->columnCount( m_rootIndex ) > 0 && m_xResolution > 0 ) {
298 return m_data.isEmpty() ? 0 : m_data.first().size();
304 void CartesianDiagramDataCompressor::setModel( QAbstractItemModel* model )
306 if ( m_model != 0 && m_model != model ) {
307 disconnect( m_model, SIGNAL( headerDataChanged( Qt::Orientation,
int,
int ) ),
308 this, SLOT( slotModelHeaderDataChanged( Qt::Orientation,
int,
int ) ) );
309 disconnect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
310 this, SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
311 disconnect( m_model, SIGNAL( layoutChanged() ),
312 this, SLOT( slotModelLayoutChanged() ) );
313 disconnect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex,
int,
int ) ),
314 this, SLOT( slotRowsAboutToBeInserted( QModelIndex,
int,
int ) ) );
315 disconnect( m_model, SIGNAL( rowsInserted( QModelIndex,
int,
int ) ),
316 this, SLOT( slotRowsInserted( QModelIndex,
int,
int ) ) );
317 disconnect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex,
int,
int ) ),
318 this, SLOT( slotRowsAboutToBeRemoved( QModelIndex,
int,
int ) ) );
319 disconnect( m_model, SIGNAL( rowsRemoved( QModelIndex,
int,
int ) ),
320 this, SLOT( slotRowsRemoved( QModelIndex,
int,
int ) ) );
321 disconnect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex,
int,
int ) ),
322 this, SLOT( slotColumnsAboutToBeInserted( QModelIndex,
int,
int ) ) );
323 disconnect( m_model, SIGNAL( columnsInserted( QModelIndex,
int,
int ) ),
324 this, SLOT( slotColumnsInserted( QModelIndex,
int,
int ) ) );
325 disconnect( m_model, SIGNAL( columnsRemoved( QModelIndex,
int,
int ) ),
326 this, SLOT( slotColumnsRemoved( QModelIndex,
int,
int ) ) );
327 disconnect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex,
int,
int ) ),
328 this, SLOT( slotColumnsAboutToBeRemoved( QModelIndex,
int,
int ) ) );
329 disconnect( m_model, SIGNAL( modelReset() ),
330 this, SLOT( rebuildCache() ) );
334 m_modelCache.setModel( model );
338 connect( m_model, SIGNAL( headerDataChanged( Qt::Orientation,
int,
int ) ),
339 SLOT( slotModelHeaderDataChanged( Qt::Orientation,
int,
int ) ) );
340 connect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
341 SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
342 connect( m_model, SIGNAL( layoutChanged() ),
343 SLOT( slotModelLayoutChanged() ) );
344 connect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex,
int,
int ) ),
345 SLOT( slotRowsAboutToBeInserted( QModelIndex,
int,
int ) ) );
346 connect( m_model, SIGNAL( rowsInserted( QModelIndex,
int,
int ) ),
347 SLOT( slotRowsInserted( QModelIndex,
int,
int ) ) );
348 connect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex,
int,
int ) ),
349 SLOT( slotRowsAboutToBeRemoved( QModelIndex,
int,
int ) ) );
350 connect( m_model, SIGNAL( rowsRemoved( QModelIndex,
int,
int ) ),
351 SLOT( slotRowsRemoved( QModelIndex,
int,
int ) ) );
352 connect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex,
int,
int ) ),
353 SLOT( slotColumnsAboutToBeInserted( QModelIndex,
int,
int ) ) );
354 connect( m_model, SIGNAL( columnsInserted( QModelIndex,
int,
int ) ),
355 SLOT( slotColumnsInserted( QModelIndex,
int,
int ) ) );
356 connect( m_model, SIGNAL( columnsRemoved( QModelIndex,
int,
int ) ),
357 SLOT( slotColumnsRemoved( QModelIndex,
int,
int ) ) );
358 connect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex,
int,
int ) ),
359 SLOT( slotColumnsAboutToBeRemoved( QModelIndex,
int,
int ) ) );
360 connect( m_model, SIGNAL( modelReset() ),
361 this, SLOT( rebuildCache() ) );
364 calculateSampleStepWidth();
367 void CartesianDiagramDataCompressor::setRootIndex(
const QModelIndex& root )
369 if ( m_rootIndex != root ) {
370 Q_ASSERT( root.model() == m_model || !root.isValid() );
372 m_modelCache.setRootIndex( root );
374 calculateSampleStepWidth();
378 void CartesianDiagramDataCompressor::recalcResolution()
380 setResolution( m_xResolution, m_yResolution );
383 void CartesianDiagramDataCompressor::setResolution(
int x,
int y )
385 if ( setResolutionInternal( x, y ) ) {
387 calculateSampleStepWidth();
391 bool CartesianDiagramDataCompressor::setResolutionInternal(
int x,
int y )
393 const int oldXRes = m_xResolution;
394 const int oldYRes = m_yResolution;
396 if ( m_datasetDimension != 1 ) {
398 m_xResolution = m_model ? m_model->rowCount( m_rootIndex ) : 0;
400 m_xResolution = qMax( 0, x );
402 m_yResolution = qMax( 0, y );
404 return m_xResolution != oldXRes || m_yResolution != oldYRes;
407 void CartesianDiagramDataCompressor::clearCache()
409 for (
int column = 0; column < m_data.size(); ++column )
410 m_data[column].fill( DataPoint() );
413 void CartesianDiagramDataCompressor::rebuildCache()
415 Q_ASSERT( m_datasetDimension != 0 );
418 setResolutionInternal( m_xResolution, m_yResolution );
419 const int columnDivisor = m_datasetDimension != 2 ? 1 : m_datasetDimension;
420 const int columnCount = m_model ? m_model->columnCount( m_rootIndex ) / columnDivisor : 0;
421 const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
422 m_data.resize( columnCount );
423 for (
int i = 0; i < columnCount; ++i ) {
424 m_data[i].resize( rowCount );
427 m_dataValueAttributesCache.clear();
430 const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data(
const CachePosition& position )
const
432 static DataPoint nullDataPoint;
433 if ( ! mapsToModelIndex( position ) ) {
434 return nullDataPoint;
436 if ( ! isCached( position ) ) {
437 retrieveModelData( position );
439 return m_data[ position.column ][ position.row ];
444 const int colCount = modelDataColumns();
445 qreal xMin = std::numeric_limits< qreal >::quiet_NaN();
446 qreal xMax = std::numeric_limits< qreal >::quiet_NaN();
447 qreal yMin = std::numeric_limits< qreal >::quiet_NaN();
448 qreal yMax = std::numeric_limits< qreal >::quiet_NaN();
450 for (
int column = 0; column < colCount; ++column )
452 const DataPointVector& data = m_data[ column ];
454 for ( DataPointVector::const_iterator it = data.begin(); it != data.end(); ++it, ++row )
456 const DataPoint& p = *it;
457 if ( !p.index.isValid() )
458 retrieveModelData( CachePosition( row, column ) );
460 const qreal valueX = ISNAN( p.key ) ? 0.0 : p.key;
461 const qreal valueY = ISNAN( p.value ) ? 0.0 : p.value;
471 xMin = qMin( xMin, valueX );
472 xMax = qMax( xMax, valueX );
473 yMin = qMin( yMin, valueY );
474 yMax = qMax( yMax, valueY );
482 const QPointF bottomLeft( xMin, yMin );
483 const QPointF topRight( xMax, yMax );
484 return qMakePair( bottomLeft, topRight );
487 void CartesianDiagramDataCompressor::retrieveModelData(
const CachePosition& position )
const
489 Q_ASSERT( mapsToModelIndex( position ) );
491 result.hidden =
true;
496 const QModelIndexList indexes = mapToModel( position );
498 if ( m_datasetDimension == 2 ) {
499 Q_ASSERT( indexes.count() == 2 );
500 const QModelIndex& xIndex = indexes.at( 0 );
501 result.index = xIndex;
502 result.key = m_modelCache.data( xIndex );
503 result.value = m_modelCache.data( indexes.at( 1 ) );
505 if ( indexes.isEmpty() ) {
508 result.value = std::numeric_limits< qreal >::quiet_NaN();
510 Q_FOREACH(
const QModelIndex& index, indexes ) {
511 const qreal value = m_modelCache.data( index );
512 if ( !ISNAN( value ) ) {
513 result.value = ISNAN( result.value ) ? value : result.value + value;
515 result.key += index.row();
517 result.index = indexes.at( 0 );
518 result.key /= indexes.size();
519 result.value /= indexes.size();
522 Q_FOREACH(
const QModelIndex& index, indexes ) {
524 if ( m_model->data( index,
DataHiddenRole ).value<
bool>() ==
false ) {
525 result.hidden =
false;
534 m_data[ position.column ][ position.row ] = result;
535 Q_ASSERT( isCached( position ) );
538 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
539 const QModelIndex& index )
const
541 Q_ASSERT( m_datasetDimension != 0 );
543 static const CachePosition nullPosition;
544 if ( !index.isValid() ) {
547 return mapToCache( index.row(), index.column() );
550 CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
551 int row,
int column )
const
553 Q_ASSERT( m_datasetDimension != 0 );
555 if ( m_data.size() == 0 || m_data[ 0 ].size() == 0 ) {
556 return mapToCache( QModelIndex() );
559 if ( indexesPerPixel() == 0 ) {
560 return mapToCache( QModelIndex() );
562 return CachePosition(
int( row / indexesPerPixel() ), column / m_datasetDimension );
565 QModelIndexList CartesianDiagramDataCompressor::mapToModel(
const CachePosition& position )
const
567 QModelIndexList indexes;
568 if ( !mapsToModelIndex( position ) ) {
572 if ( m_datasetDimension == 2 ) {
573 indexes << m_model->index( position.row, position.column * 2, m_rootIndex );
574 indexes << m_model->index( position.row, position.column * 2 + 1, m_rootIndex );
578 Q_ASSERT( position.column < m_model->columnCount( m_rootIndex ) );
579 const qreal ipp = indexesPerPixel();
580 const int baseRow = floor( position.row * ipp );
582 const int endRow = floor( ( position.row + 1 ) * ipp );
583 for (
int row = baseRow; row < endRow; ++row ) {
584 Q_ASSERT( row < m_model->rowCount( m_rootIndex ) );
585 const QModelIndex index = m_model->index( row, position.column, m_rootIndex );
586 if ( index.isValid() ) {
594 qreal CartesianDiagramDataCompressor::indexesPerPixel()
const
596 if ( !m_model || m_data.size() == 0 || m_data[ 0 ].size() == 0 ) {
599 return qreal( m_model->rowCount( m_rootIndex ) ) / qreal( m_data[ 0 ].size() );
602 bool CartesianDiagramDataCompressor::mapsToModelIndex(
const CachePosition& position )
const
604 return m_model && m_data.size() > 0 && m_data[ 0 ].size() > 0 &&
605 position.column >= 0 && position.column < m_data.size() &&
606 position.row >=0 && position.row < m_data[ 0 ].size();
609 void CartesianDiagramDataCompressor::invalidate(
const CachePosition& position )
611 if ( mapsToModelIndex( position ) ) {
612 m_data[ position.column ][ position.row ] = DataPoint();
616 m_dataValueAttributesCache.remove( position );
620 bool CartesianDiagramDataCompressor::isCached(
const CachePosition& position )
const
622 Q_ASSERT( mapsToModelIndex( position ) );
623 const DataPoint& p = m_data[ position.column ][ position.row ];
624 return p.index.isValid();
627 void CartesianDiagramDataCompressor::calculateSampleStepWidth()
629 if ( m_mode == Precise ) {
634 static unsigned int SomePrimes[] = {
635 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
636 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
637 151, 211, 313, 401, 503, 607, 701, 811, 911, 1009,
638 10037, 12911, 16001, 20011, 50021,
639 100003, 137867, 199999, 500009, 707753, 1000003, 0
643 const qreal WantedSamples = 17;
644 if ( WantedSamples > indexesPerPixel() ) {
648 for ( i = 0; SomePrimes[i] != 0; ++i ) {
649 if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) {
653 m_sampleStep = SomePrimes[i];
654 if ( SomePrimes[i] == 0 ) {
655 m_sampleStep = SomePrimes[i-1];
657 m_sampleStep = SomePrimes[i];
662 void CartesianDiagramDataCompressor::setDatasetDimension(
int dimension )
664 if ( dimension != m_datasetDimension ) {
665 m_datasetDimension = dimension;
667 calculateSampleStepWidth();