~ubuntu-branches/ubuntu/precise/koffice/precise

« back to all changes in this revision

Viewing changes to kchart/kdchart/src/KDChartCartesianDiagramDataCompressor_p.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2010-10-27 17:52:57 UTC
  • mfrom: (0.12.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20101027175257-s04zqqk5bs8ckm9o
Tags: 1:2.2.83-0ubuntu1
* Merge with Debian git remaining changes:
 - Add build-deps on librcps-dev, opengtl-dev, libqtgtl-dev, freetds-dev,
   create-resources, libspnav-dev
 - Remove needless build-dep on libwv2-dev
 - koffice-libs recommends create-resources
 - krita recommends pstoedit
 - Keep our patches
* New upstream release 2.3 beta 3
  - Remove debian/patches fixed by upstream
  - Update install files

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C++ -*-
2
 
   KDChart - a multi-platform charting engine
3
 
   */
4
 
 
5
 
/****************************************************************************
6
 
 ** Copyright (C) 2005-2007 Klarälvdalens Datakonsult AB.  All rights reserved.
7
 
 **
8
 
 ** This file is part of the KD Chart library.
9
 
 **
10
 
 ** This file may be used under the terms of the GNU General Public
11
 
 ** License versions 2.0 or 3.0 as published by the Free Software
12
 
 ** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
13
 
 ** included in the packaging of this file.  Alternatively you may (at
14
 
 ** your option) use any later version of the GNU General Public
15
 
 ** License if such license has been publicly approved by
16
 
 ** Klarälvdalens Datakonsult AB (or its successors, if any).
17
 
 ** 
18
 
 ** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
19
 
 ** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
20
 
 ** A PARTICULAR PURPOSE. Klarälvdalens Datakonsult AB reserves all rights
21
 
 ** not expressly granted herein.
22
 
 ** 
23
 
 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
24
 
 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25
 
 **
26
 
 **********************************************************************/
27
 
 
28
 
#include "KDChartCartesianDiagramDataCompressor_p.h"
29
 
 
30
 
#include <QtDebug>
31
 
#include <QAbstractItemModel>
32
 
 
33
 
#include "KDChartAbstractCartesianDiagram.h"
34
 
 
35
 
#include <KDABLibFakes>
36
 
 
37
 
using namespace KDChart;
38
 
using namespace std;
39
 
 
40
 
CartesianDiagramDataCompressor::CartesianDiagramDataCompressor( QObject* parent )
41
 
    : QObject( parent )
42
 
    , m_mode( Precise )
43
 
    , m_xResolution( 0 )
44
 
    , m_yResolution( 0 )
45
 
    , m_sampleStep( 0 )
46
 
    , m_datasetDimension( 1 )
47
 
{
48
 
    calculateSampleStepWidth();
49
 
}
50
 
 
51
 
QModelIndexList CartesianDiagramDataCompressor::indexesAt( const CachePosition& position ) const
52
 
{
53
 
    if ( isValidCachePosition( position ) ) {
54
 
        CachePosition posPrev( position );
55
 
        if( m_datasetDimension == 2 ){
56
 
            if(posPrev.second)
57
 
                --posPrev.second;
58
 
        }else{
59
 
            if(posPrev.first)
60
 
                --posPrev.first;
61
 
        }
62
 
        const QModelIndexList indPrev = mapToModel( posPrev );
63
 
        const QModelIndexList indCur  = mapToModel( position );
64
 
 
65
 
        QModelIndexList indexes;
66
 
        if( m_datasetDimension == 2 )
67
 
        {
68
 
            const int iStart = (indPrev.empty() || indPrev==indCur) ? indCur.first().column()
69
 
                             : indPrev.first().column() + 1;
70
 
            const int iEnd   = indCur.last().column();
71
 
            for( int i=iStart; i<=iEnd; ++i){
72
 
                indexes << m_model->index( position.first, i, m_rootIndex );
73
 
            }
74
 
        }
75
 
        else
76
 
        {
77
 
            const int iStart = (indPrev.empty() || indPrev==indCur)  ? indCur.first().row()
78
 
                             : indPrev.first().row() + 1;
79
 
            const int iEnd   = (indCur.isEmpty()) ? iStart : indCur.first().row();
80
 
            //qDebug()<<iStart<<iEnd << iEnd-iStart;
81
 
            for( int i=iStart; i<=iEnd; ++i){
82
 
                indexes << m_model->index( i, position.second, m_rootIndex );
83
 
            }
84
 
        }
85
 
        return indexes;
86
 
    } else {
87
 
        return QModelIndexList();
88
 
    }
89
 
}
90
 
 
91
 
 
92
 
CartesianDiagramDataCompressor::DataValueAttributesList CartesianDiagramDataCompressor::aggregatedAttrs(
93
 
        AbstractDiagram * diagram,
94
 
        const QModelIndex & index,
95
 
        const CachePosition& position ) const
96
 
{
97
 
    // return cached attrs, if any
98
 
    DataValueAttributesCache::const_iterator i = m_dataValueAttributesCache.constFind(position);
99
 
    if( i != m_dataValueAttributesCache.constEnd() )
100
 
        return i.value();
101
 
    // retrieve attrs from all cells between the prev. cell and the current one
102
 
    CartesianDiagramDataCompressor::DataValueAttributesList allAttrs;
103
 
    const QModelIndexList indexes( indexesAt( position ) );
104
 
    KDAB_FOREACH( QModelIndex idx, indexes ) {
105
 
        DataValueAttributes attrs( diagram->dataValueAttributes( idx ) );
106
 
        if( attrs.isVisible() ){
107
 
            // make sure no duplicate attrs are stored
108
 
            bool isDuplicate = false;
109
 
            CartesianDiagramDataCompressor::DataValueAttributesList::const_iterator i = allAttrs.constBegin();
110
 
            while (i != allAttrs.constEnd()) {
111
 
                if( i.value() == attrs ){
112
 
                    isDuplicate = true;
113
 
                    continue;
114
 
                }
115
 
                ++i;
116
 
            }
117
 
            if( !isDuplicate ){
118
 
                //qDebug()<<idx.row();
119
 
                allAttrs[idx] = attrs;
120
 
            }
121
 
        }
122
 
    }
123
 
    // if none of the attrs had the visible flag set
124
 
    // we just take the one set for the index to not return an empty list
125
 
    if( allAttrs.empty() ){
126
 
        allAttrs[index] = diagram->dataValueAttributes( index );
127
 
    }
128
 
    // cache the attrs
129
 
    m_dataValueAttributesCache[position] = allAttrs;
130
 
    return allAttrs;
131
 
}
132
 
 
133
 
 
134
 
void CartesianDiagramDataCompressor::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end )
135
 
{
136
 
    if ( parent != m_rootIndex )
137
 
        return;
138
 
    Q_ASSERT( start <= end );
139
 
 
140
 
    CachePosition startPos = mapToCache( start, 0 );
141
 
    CachePosition endPos = mapToCache( end, 0 );
142
 
 
143
 
    static const CachePosition NullPosition( -1, -1 );
144
 
    if( startPos == NullPosition )
145
 
    {
146
 
        rebuildCache();
147
 
        startPos = mapToCache( start, 0 );
148
 
        endPos = mapToCache( end, 0 );
149
 
        // The start position still isn't valid,
150
 
        // means that no resolution was set yet or we're about to add the first rows
151
 
        if( startPos == NullPosition ) {
152
 
            return;
153
 
        }
154
 
    }
155
 
 
156
 
    start = startPos.first;
157
 
    end = endPos.first;
158
 
 
159
 
    for( int i = 0; i < m_data.size(); ++i )
160
 
    {
161
 
        Q_ASSERT( start >= 0 && start <= m_data[ i ].size() );
162
 
        m_data[ i ].insert( start, end - start + 1, DataPoint() );
163
 
    }
164
 
}
165
 
 
166
 
void CartesianDiagramDataCompressor::slotRowsInserted( const QModelIndex& parent, int start, int end )
167
 
{
168
 
    if ( parent != m_rootIndex )
169
 
        return;
170
 
    Q_ASSERT( start <= end );
171
 
 
172
 
    CachePosition startPos = mapToCache( start, 0 );
173
 
    CachePosition endPos = mapToCache( end, 0 );
174
 
 
175
 
    static const CachePosition NullPosition( -1, -1 );
176
 
    if( startPos == NullPosition )
177
 
    {
178
 
        // Rebuild the cache at this point if we have added the first rows
179
 
        rebuildCache();
180
 
        startPos = mapToCache( start, 0 );
181
 
        endPos = mapToCache( end, 0 );
182
 
        // The start position still isn't valid,
183
 
        // means that no resolution was set yet
184
 
        if( startPos == NullPosition ) {
185
 
            return;
186
 
        }
187
 
    }
188
 
 
189
 
    start = startPos.first;
190
 
    end = endPos.first;
191
 
 
192
 
    for( int i = 0; i < m_data.size(); ++i )
193
 
    {
194
 
        for( int j = start; j < m_data[i].size(); ++j ) {
195
 
            retrieveModelData( CachePosition( j, i ) );
196
 
        }
197
 
    }
198
 
}
199
 
 
200
 
void CartesianDiagramDataCompressor::slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end )
201
 
{
202
 
    if ( parent != m_rootIndex )
203
 
        return;
204
 
    Q_ASSERT( start <= end );
205
 
 
206
 
    CachePosition startPos = mapToCache( 0, start );
207
 
    CachePosition endPos = mapToCache( 0, end );
208
 
 
209
 
    static const CachePosition NullPosition( -1, -1 );
210
 
    if( startPos == NullPosition )
211
 
    {
212
 
        rebuildCache();
213
 
        startPos = mapToCache( 0, start );
214
 
        endPos = mapToCache( 0, end );
215
 
        // The start position still isn't valid,
216
 
        // means that no resolution was set yet or we're about to add the first columns
217
 
        if( startPos == NullPosition ) {
218
 
            return;
219
 
        }
220
 
    }
221
 
 
222
 
    start = startPos.second;
223
 
    end = endPos.second;
224
 
 
225
 
    const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
226
 
    Q_ASSERT( start >= 0 && start <= m_data.size() );
227
 
    m_data.insert( start, end - start + 1, QVector< DataPoint >( rowCount ) );
228
 
}
229
 
 
230
 
void CartesianDiagramDataCompressor::slotColumnsInserted( const QModelIndex& parent, int start, int end )
231
 
{
232
 
    if ( parent != m_rootIndex )
233
 
        return;
234
 
    Q_ASSERT( start <= end );
235
 
 
236
 
    CachePosition startPos = mapToCache( 0, start );
237
 
    CachePosition endPos = mapToCache( 0, end );
238
 
 
239
 
    static const CachePosition NullPosition( -1, -1 );
240
 
    if( startPos == NullPosition )
241
 
    {
242
 
        // Rebuild the cache at this point if we have added the first columns
243
 
        rebuildCache();
244
 
        startPos = mapToCache( 0, start );
245
 
        endPos = mapToCache( 0, end );
246
 
        // The start position still isn't valid,
247
 
        // means that no resolution was set yet
248
 
        if( startPos == NullPosition ) {
249
 
            return;
250
 
        }
251
 
    }
252
 
 
253
 
    start = startPos.second;
254
 
    end = endPos.second;
255
 
 
256
 
    for( int i = start; i < m_data.size(); ++i )
257
 
    {
258
 
        for(int j = 0; j < m_data[i].size(); ++j ) {
259
 
            retrieveModelData( CachePosition( j, i ) );
260
 
        }
261
 
    }
262
 
}
263
 
 
264
 
void CartesianDiagramDataCompressor::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
265
 
{
266
 
    if ( parent != m_rootIndex )
267
 
        return;
268
 
    Q_ASSERT( start <= end );
269
 
 
270
 
    CachePosition startPos = mapToCache( start, 0 );
271
 
    CachePosition endPos = mapToCache( end, 0 );
272
 
 
273
 
    static const CachePosition NullPosition( -1, -1 );
274
 
    if( startPos == NullPosition )
275
 
    {
276
 
        rebuildCache();
277
 
        startPos = mapToCache( start, 0 );
278
 
        endPos = mapToCache( end, 0 );
279
 
        // The start position still isn't valid,
280
 
        // probably means that no resolution was set yet
281
 
        if( startPos == NullPosition ) {
282
 
            return;
283
 
        }
284
 
    }
285
 
 
286
 
    start = startPos.first;
287
 
    end = endPos.first;
288
 
 
289
 
    for( int i = 0; i < m_data.size(); ++i )
290
 
    {
291
 
        m_data[ i ].remove( start, end - start + 1 );
292
 
    }
293
 
}
294
 
 
295
 
void CartesianDiagramDataCompressor::slotRowsRemoved( const QModelIndex& parent, int start, int end )
296
 
{
297
 
    if ( parent != m_rootIndex )
298
 
        return;
299
 
    Q_ASSERT( start <= end );
300
 
 
301
 
    CachePosition startPos = mapToCache( start, 0 );
302
 
    CachePosition endPos = mapToCache( end, 0 );
303
 
 
304
 
    start = startPos.first;
305
 
    end = endPos.first;
306
 
 
307
 
    static const CachePosition NullPosition( -1, -1 );
308
 
    if( startPos == NullPosition )
309
 
    {
310
 
        // Since we should already have rebuilt the cache, it won't help to rebuild it again.
311
 
        // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 rows
312
 
        return;
313
 
    }
314
 
 
315
 
    for( int i = 0; i < m_data.size(); ++i ) {
316
 
        for(int j = start; j < m_data[i].size(); ++j ) {
317
 
            retrieveModelData( CachePosition( j, i ) );
318
 
        }
319
 
    }
320
 
}
321
 
 
322
 
void CartesianDiagramDataCompressor::slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
323
 
{
324
 
    if ( parent != m_rootIndex )
325
 
        return;
326
 
    Q_ASSERT( start <= end );
327
 
 
328
 
    CachePosition startPos = mapToCache( 0, start );
329
 
    CachePosition endPos = mapToCache( 0, end );
330
 
 
331
 
    static const CachePosition NullPosition( -1, -1 );
332
 
    if( startPos == NullPosition )
333
 
    {
334
 
        rebuildCache();
335
 
        startPos = mapToCache( 0, start );
336
 
        endPos = mapToCache( 0, end );
337
 
        // The start position still isn't valid,
338
 
        // probably means that no resolution was set yet
339
 
        if( startPos == NullPosition ) {
340
 
            return;
341
 
        }
342
 
    }
343
 
 
344
 
    start = startPos.second;
345
 
    end = endPos.second;
346
 
 
347
 
    m_data.remove( start, end - start + 1 );
348
 
}
349
 
 
350
 
void CartesianDiagramDataCompressor::slotColumnsRemoved( const QModelIndex& parent, int start, int end )
351
 
{
352
 
    if ( parent != m_rootIndex )
353
 
        return;
354
 
    Q_ASSERT( start <= end );
355
 
 
356
 
    const CachePosition startPos = mapToCache( 0, start );
357
 
    const CachePosition endPos = mapToCache( 0, end );
358
 
 
359
 
    start = startPos.second;
360
 
    end = endPos.second;
361
 
 
362
 
    static const CachePosition NullPosition( -1, -1 );
363
 
    if( startPos == NullPosition )
364
 
    {
365
 
        // Since we should already have rebuilt the cache, it won't help to rebuild it again.
366
 
        // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 columns
367
 
        return;
368
 
    }
369
 
 
370
 
    for( int i = start; i < m_data.size(); ++i ) {
371
 
        for( int j = 0; j < m_data[i].size(); ++j ) {
372
 
            retrieveModelData( CachePosition( j, i ) );
373
 
        }
374
 
    }
375
 
}
376
 
 
377
 
void CartesianDiagramDataCompressor::slotModelHeaderDataChanged( Qt::Orientation orientation, int first, int last )
378
 
{
379
 
    if( orientation != Qt::Vertical )
380
 
        return;
381
 
 
382
 
    const QModelIndex firstRow = m_model->index( 0, first, m_rootIndex );
383
 
    const QModelIndex lastRow = m_model->index( m_model->rowCount( m_rootIndex ) - 1, last, m_rootIndex );
384
 
 
385
 
    slotModelDataChanged( firstRow, lastRow );
386
 
}
387
 
 
388
 
void CartesianDiagramDataCompressor::slotModelDataChanged(
389
 
    const QModelIndex& topLeftIndex,
390
 
    const QModelIndex& bottomRightIndex )
391
 
{
392
 
    if ( topLeftIndex.parent() != m_rootIndex )
393
 
        return;
394
 
    Q_ASSERT( topLeftIndex.parent() == bottomRightIndex.parent() );
395
 
    Q_ASSERT( topLeftIndex.row() <= bottomRightIndex.row() );
396
 
    Q_ASSERT( topLeftIndex.column() <= bottomRightIndex.column() );
397
 
    CachePosition topleft = mapToCache( topLeftIndex );
398
 
    CachePosition bottomright = mapToCache( bottomRightIndex );
399
 
    for ( int row = topleft.first; row <= bottomright.first; ++row )
400
 
        for ( int column = topleft.second; column <= bottomright.second; ++column )
401
 
            invalidate( CachePosition( row, column ) );
402
 
}
403
 
 
404
 
void CartesianDiagramDataCompressor::slotModelLayoutChanged()
405
 
{
406
 
    rebuildCache();
407
 
    calculateSampleStepWidth();
408
 
}
409
 
 
410
 
void CartesianDiagramDataCompressor::slotDiagramLayoutChanged( AbstractDiagram* diagramBase )
411
 
{
412
 
    AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( diagramBase );
413
 
    Q_ASSERT( diagram );
414
 
    if ( diagram->datasetDimension() != m_datasetDimension ) {
415
 
        setDatasetDimension( diagram->datasetDimension() );
416
 
    }
417
 
}
418
 
 
419
 
int CartesianDiagramDataCompressor::modelDataColumns() const
420
 
{
421
 
    Q_ASSERT( m_datasetDimension != 0 );
422
 
    // only operational if there is a model and a resolution
423
 
    if ( m_model ) {
424
 
        const int columns = m_model->columnCount( m_rootIndex ) / m_datasetDimension;
425
 
 
426
 
        if( columns != m_data.size() )
427
 
        {
428
 
            rebuildCache();
429
 
        }
430
 
 
431
 
        Q_ASSERT( columns == m_data.size() );
432
 
        return columns;
433
 
    } else {
434
 
        return 0;
435
 
    }
436
 
}
437
 
 
438
 
int CartesianDiagramDataCompressor::modelDataRows() const
439
 
{
440
 
    // only operational if there is a model, columns, and a resolution
441
 
    if ( m_model && m_model->columnCount( m_rootIndex ) > 0 && m_xResolution > 0 ) {
442
 
        return m_data.isEmpty() ? 0 : m_data.first().size();
443
 
    } else {
444
 
        return 0;
445
 
    }
446
 
}
447
 
 
448
 
void CartesianDiagramDataCompressor::setModel( QAbstractItemModel* model )
449
 
{
450
 
    if ( m_model != 0 && m_model != model ) {
451
 
        disconnect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
452
 
                 this, SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) );
453
 
        disconnect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
454
 
                 this, SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
455
 
        disconnect( m_model, SIGNAL( layoutChanged() ),
456
 
                 this, SLOT( slotModelLayoutChanged() ) );
457
 
        disconnect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ),
458
 
                 this, SLOT( slotRowsAboutToBeInserted( QModelIndex, int, int ) ) );
459
 
        disconnect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ),
460
 
                 this, SLOT( slotRowsInserted( QModelIndex, int, int ) ) );
461
 
        disconnect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex, int, int ) ),
462
 
                 this, SLOT( slotRowsAboutToBeRemoved( QModelIndex, int, int ) ) );
463
 
        disconnect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ),
464
 
                 this, SLOT( slotRowsRemoved( QModelIndex, int, int ) ) );
465
 
        disconnect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ),
466
 
                 this, SLOT( slotColumnsAboutToBeInserted( QModelIndex, int, int ) ) );
467
 
        disconnect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ),
468
 
                 this, SLOT( slotColumnsInserted( QModelIndex, int, int ) ) );
469
 
        disconnect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ),
470
 
                 this, SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) );
471
 
        disconnect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ),
472
 
                 this, SLOT( slotColumnsAboutToBeRemoved( QModelIndex, int, int ) ) );
473
 
        disconnect( m_model, SIGNAL( modelReset() ),
474
 
                    this, SLOT( rebuildCache() ) );
475
 
        m_model = 0;
476
 
    }
477
 
 
478
 
    m_modelCache.setModel( model );
479
 
 
480
 
    if ( model != 0 ) {
481
 
        m_model = model;
482
 
        connect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
483
 
                 SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) );
484
 
        connect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
485
 
                 SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) );
486
 
        connect( m_model, SIGNAL( layoutChanged() ),
487
 
                 SLOT( slotModelLayoutChanged() ) );
488
 
        connect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ),
489
 
                 SLOT( slotRowsAboutToBeInserted( QModelIndex, int, int ) ) );
490
 
        connect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ),
491
 
                 SLOT( slotRowsInserted( QModelIndex, int, int ) ) );
492
 
        connect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex, int, int ) ),
493
 
                 SLOT( slotRowsAboutToBeRemoved( QModelIndex, int, int ) ) );
494
 
        connect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ),
495
 
                 SLOT( slotRowsRemoved( QModelIndex, int, int ) ) );
496
 
        connect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ),
497
 
                 SLOT( slotColumnsAboutToBeInserted( QModelIndex, int, int ) ) );
498
 
        connect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ),
499
 
                 SLOT( slotColumnsInserted( QModelIndex, int, int ) ) );
500
 
        connect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ),
501
 
                 SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) );
502
 
        connect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ),
503
 
                 SLOT( slotColumnsAboutToBeRemoved( QModelIndex, int, int ) ) );
504
 
        connect( m_model, SIGNAL( modelReset() ),
505
 
                    this, SLOT( rebuildCache() ) );
506
 
    }
507
 
    rebuildCache();
508
 
    calculateSampleStepWidth();
509
 
}
510
 
 
511
 
void CartesianDiagramDataCompressor::setRootIndex( const QModelIndex& root )
512
 
{
513
 
    if ( m_rootIndex != root ) {
514
 
        Q_ASSERT( root.model() == m_model || !root.isValid() );
515
 
        m_rootIndex = root;
516
 
        m_modelCache.setRootIndex( root );
517
 
        rebuildCache();
518
 
        calculateSampleStepWidth();
519
 
    }
520
 
}
521
 
void CartesianDiagramDataCompressor::setResolution( int x, int y )
522
 
{
523
 
    const int oldX = m_xResolution;
524
 
    const int oldY = m_yResolution;
525
 
 
526
 
    if( m_datasetDimension != 1 )
527
 
    {
528
 
        // just ignore the resolution in that case
529
 
        m_xResolution = m_model == 0 ? 0 : m_model->rowCount( m_rootIndex );
530
 
        m_yResolution = qMax( 0, y );
531
 
    }
532
 
    else if ( x != m_xResolution || y != m_yResolution ) {
533
 
        m_xResolution = qMax( 0, x );
534
 
        m_yResolution = qMax( 0, y );
535
 
        rebuildCache();
536
 
        calculateSampleStepWidth();
537
 
    }
538
 
 
539
 
    if( oldX != m_xResolution || oldY != m_yResolution )
540
 
    {
541
 
        rebuildCache();
542
 
        calculateSampleStepWidth();
543
 
    }
544
 
}
545
 
 
546
 
void CartesianDiagramDataCompressor::clearCache()
547
 
{
548
 
    for ( int column = 0; column < m_data.size(); ++column )
549
 
        m_data[column].fill( DataPoint() );
550
 
}
551
 
 
552
 
void CartesianDiagramDataCompressor::rebuildCache() const
553
 
{
554
 
    Q_ASSERT( m_datasetDimension != 0 );
555
 
 
556
 
    m_data.clear();
557
 
    const int columnCount = m_model ? m_model->columnCount( m_rootIndex ) / m_datasetDimension : 0;
558
 
    const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution );
559
 
    m_data.resize( columnCount );
560
 
    for ( int i = 0; i < columnCount; ++i ) {
561
 
        m_data[i].resize( rowCount );
562
 
    }
563
 
    // also empty the attrs cache
564
 
    m_dataValueAttributesCache.clear();
565
 
}
566
 
 
567
 
const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data( const CachePosition& position ) const
568
 
{
569
 
    static DataPoint NullDataPoint;
570
 
    if ( ! isValidCachePosition( position ) ) return NullDataPoint;
571
 
    if ( ! isCached( position ) ) retrieveModelData( position );
572
 
    return m_data[ position.second ][ position.first ];
573
 
}
574
 
 
575
 
void CartesianDiagramDataCompressor::retrieveModelData( const CachePosition& position ) const
576
 
{
577
 
    Q_ASSERT( isValidCachePosition( position ) );
578
 
    DataPoint result;
579
 
 
580
 
    switch(m_mode ) {
581
 
    case Precise:
582
 
    {
583
 
        bool forceHidden = false;
584
 
        result.hidden = true;
585
 
        const QModelIndexList indexes = mapToModel( position );
586
 
        if( m_datasetDimension != 1 )
587
 
        {
588
 
            Q_ASSERT( indexes.count() == 2 );
589
 
            const QModelIndex xIndex = indexes.first();
590
 
            const QModelIndex yIndex = indexes.last();
591
 
            const double xData = m_modelCache.data( xIndex );
592
 
            const double yData = m_modelCache.data( yIndex );
593
 
            result.index = xIndex;
594
 
            result.key   = xData;
595
 
            result.value = yData;
596
 
        }
597
 
        else
598
 
        {
599
 
            if ( ! indexes.isEmpty() ) {
600
 
                result.value = std::numeric_limits< double >::quiet_NaN();
601
 
                result.key = 0.0;
602
 
                Q_FOREACH( const QModelIndex& index, indexes ) {
603
 
                    const double value = m_modelCache.data( index );
604
 
                    if( !ISNAN( value ) )
605
 
                    {
606
 
                        result.value = ISNAN( result.value ) ? value : result.value + value;
607
 
                    }
608
 
                    result.key += index.row();
609
 
                }
610
 
                result.index = indexes.at( 0 );
611
 
                result.key /= indexes.size();
612
 
                result.value /= indexes.size();
613
 
            }
614
 
        }
615
 
        if( !forceHidden )
616
 
        {
617
 
        Q_FOREACH( const QModelIndex& index, indexes )
618
 
        {
619
 
            // the point is visible if any of the points at this pixel position is visible
620
 
            if ( qVariantValue<bool>( m_model->data( index, DataHiddenRole ) ) == false ) {
621
 
                result.hidden = false;
622
 
            }
623
 
        }
624
 
        }
625
 
    }
626
 
    break;
627
 
    case SamplingSeven:
628
 
    default:
629
 
    {
630
 
    }
631
 
    break;
632
 
    };
633
 
 
634
 
    m_data[position.second][position.first] = result;
635
 
    Q_ASSERT( isCached( position ) );
636
 
}
637
 
 
638
 
CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
639
 
        const QModelIndex& index ) const
640
 
{
641
 
    Q_ASSERT( m_datasetDimension != 0 );
642
 
 
643
 
    static const CachePosition NullPosition( -1, -1 );
644
 
    if ( ! index.isValid() ) return NullPosition;
645
 
    return mapToCache( index.row(), index.column() );
646
 
}
647
 
 
648
 
CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache(
649
 
        int row, int column ) const
650
 
{
651
 
    Q_ASSERT( m_datasetDimension != 0 );
652
 
 
653
 
    if ( m_data.size() == 0 || m_data[0].size() == 0 ) return mapToCache( QModelIndex() );
654
 
    // assumption: indexes per column == 1
655
 
    if ( indexesPerPixel() == 0 ) return mapToCache( QModelIndex() );
656
 
    return CachePosition( static_cast< int >( ( row ) / indexesPerPixel() ), column / m_datasetDimension );
657
 
}
658
 
 
659
 
QModelIndexList CartesianDiagramDataCompressor::mapToModel( const CachePosition& position ) const
660
 
{
661
 
    if ( isValidCachePosition( position ) ) {
662
 
        QModelIndexList indexes;
663
 
        if( m_datasetDimension == 2 )
664
 
        {
665
 
            indexes << m_model->index( position.first, position.second * 2, m_rootIndex );
666
 
            indexes << m_model->index( position.first, position.second * 2 + 1, m_rootIndex );
667
 
        }
668
 
        else
669
 
        {
670
 
        // assumption: indexes per column == 1
671
 
            const qreal ipp = indexesPerPixel();
672
 
            for ( int i = 0; i < ipp; ++i ) {
673
 
                const QModelIndex index = m_model->index( qRound( position.first * ipp ) + i, position.second, m_rootIndex );
674
 
                if( index.isValid() )
675
 
                    indexes << index;
676
 
            }
677
 
        }
678
 
        return indexes;
679
 
    } else {
680
 
        return QModelIndexList();
681
 
    }
682
 
}
683
 
 
684
 
qreal CartesianDiagramDataCompressor::indexesPerPixel() const
685
 
{
686
 
    if ( m_data.size() == 0 ) return 0;
687
 
    if ( m_data[0].size() == 0 ) return 0;
688
 
    if ( ! m_model ) return 0;
689
 
    return static_cast< qreal >( m_model->rowCount( m_rootIndex ) ) / static_cast< qreal >( m_data[0].size() );
690
 
}
691
 
 
692
 
bool CartesianDiagramDataCompressor::isValidCachePosition( const CachePosition& position ) const
693
 
{
694
 
    if ( ! m_model ) return false;
695
 
    if ( m_data.size() == 0 || m_data[0].size() == 0 ) return false;
696
 
    if ( position.second < 0 || position.second >= m_data.size() ) return false;
697
 
    if ( position.first < 0 || position.first >= m_data[0].size() ) return false;
698
 
    return true;
699
 
}
700
 
 
701
 
void CartesianDiagramDataCompressor::invalidate( const CachePosition& position )
702
 
{
703
 
    if ( isValidCachePosition( position ) ) {
704
 
        m_data[position.second][position.first] = DataPoint();
705
 
        // Also invalidate the data value attributes at "position".
706
 
        // Otherwise the user overwrites the attributes without us noticing
707
 
        // it because we keep reading what's in the cache.
708
 
        m_dataValueAttributesCache.remove( position );
709
 
    }
710
 
}
711
 
 
712
 
bool CartesianDiagramDataCompressor::isCached( const CachePosition& position ) const
713
 
{
714
 
    Q_ASSERT( isValidCachePosition( position ) );
715
 
    const DataPoint& p = m_data[position.second][position.first];
716
 
    return p.index.isValid();
717
 
}
718
 
 
719
 
void CartesianDiagramDataCompressor::calculateSampleStepWidth()
720
 
{
721
 
    if ( m_mode == Precise ) {
722
 
        m_sampleStep = 1;
723
 
        return;
724
 
    }
725
 
 
726
 
    static unsigned int SomePrimes[] = {
727
 
        2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
728
 
        53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
729
 
        151, 211, 313, 401, 503, 607, 701, 811, 911, 1009,
730
 
        10037, 12911, 16001, 20011, 50021,
731
 
        100003, 137867, 199999, 500009, 707753, 1000003, 0
732
 
    }; // ... after that, having a model at all becomes impractical
733
 
 
734
 
    // we want at least 17 samples per data point, using a prime step width
735
 
    const double WantedSamples = 17;
736
 
    if ( WantedSamples > indexesPerPixel() ) {
737
 
        m_sampleStep = 1;
738
 
    } else {
739
 
        int i;
740
 
        for ( i = 0; SomePrimes[i] != 0; ++i ) {
741
 
            if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) {
742
 
                break;
743
 
            }
744
 
        }
745
 
        m_sampleStep = SomePrimes[i];
746
 
        if ( SomePrimes[i] == 0 ) {
747
 
            m_sampleStep = SomePrimes[i-1];
748
 
        } else {
749
 
            m_sampleStep = SomePrimes[i];
750
 
        }
751
 
    }
752
 
}
753
 
 
754
 
void CartesianDiagramDataCompressor::setDatasetDimension( int dimension )
755
 
{
756
 
    if ( dimension != m_datasetDimension ) {
757
 
        m_datasetDimension = dimension;
758
 
        rebuildCache();
759
 
        calculateSampleStepWidth();
760
 
    }
761
 
}