KD Chart API Documentation 3.1
Loading...
Searching...
No Matches
KDChartPlotterDiagramCompressor.cpp
Go to the documentation of this file.
1/****************************************************************************
2**
3** This file is part of the KD Chart library.
4**
5** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6**
7** SPDX-License-Identifier: MIT
8**
9****************************************************************************/
10
12
13#include "KDChartPlotterDiagramCompressor_p.h"
14#include <QtCore/QPointF>
15
16#include <KDABLibFakes>
17#include <limits>
18
19using namespace KDChart;
20
22{
23 return (rhs.value - lhs.value) / (rhs.key - lhs.key);
24}
25
27 : m_parent(parent)
28 , m_index(0)
29 , m_dataset(dataSet)
30 , m_bufferIndex(0)
31 , m_rebuffer(true)
32{
33 if (m_parent) {
34 if (parent->rowCount() > m_dataset && parent->rowCount() > 0) {
35 m_buffer.append(parent->data(CachePosition(m_index, m_dataset)));
36 }
37 } else {
38 m_dataset = -1;
39 m_index = -1;
40 }
41}
42
44 : m_parent(parent)
45 , m_buffer(buffer)
46 , m_index(0)
47 , m_dataset(dataSet)
48 , m_bufferIndex(0)
49 , m_rebuffer(false)
50 , m_timeOfCreation(QDateTime::currentDateTime())
51{
52 if (!m_parent) {
53 m_dataset = -1;
54 m_index = -1;
55 } else {
56 // buffer needs to be filled
57 if (parent->datasetCount() > m_dataset && parent->rowCount() > 0 && m_buffer.isEmpty()) {
58 m_buffer.append(parent->data(CachePosition(m_index, m_dataset)));
59 m_rebuffer = true;
60 }
61 }
62}
63
65{
66 if (m_parent) {
67 if (m_parent.data()->d->m_timeOfLastInvalidation < m_timeOfCreation)
68 m_parent.data()->d->m_bufferlist[m_dataset] = m_buffer;
69 }
70}
71
73{
74 if (m_parent == nullptr)
75 return false;
76 return m_dataset >= 0 && m_index >= 0 && m_parent.data()->rowCount() > m_index;
77}
78
79// PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator++()
80//{
81// ++m_index;
82
83// ++m_bufferIndex;
84// // the version that checks dataBoundaries is separated here, this is to avoid the runtime cost
85// // of checking every time the boundaries if that's not necessary
86// if ( m_parent.data()->d->forcedBoundaries( Qt::Vertical ) || m_parent.data()->d->forcedBoundaries( Qt::Vertical ) )
87// {
88// if ( m_bufferIndex >= m_buffer.count() && m_rebuffer )
89// {
90// if ( m_index < m_parent.data()->rowCount() )
91// {
92// PlotterDiagramCompressor::DataPoint dp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
93// if ( m_parent.data()->d->inBoundaries( Qt::Vertical, dp ) && m_parent.data()->d->inBoundaries( Qt::Horizontal, dp ) )
94// {
95// m_buffer.append( dp );
96// }
97// else
98// {
99// if ( m_index + 1 < m_parent.data()->rowCount() )
100// {
101// PlotterDiagramCompressor::DataPoint dp1 = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
102// if ( m_parent.data()->d->inBoundaries( Qt::Vertical, dp1 ) && m_parent.data()->d->inBoundaries( Qt::Horizontal, dp1 ) )
103// {
104// m_buffer.append( dp );
105// }
106// }
107// }
108// }
109// }
110// else
111// {
112// if ( m_bufferIndex == m_buffer.count() )
113// m_index = - 1;
114// return *this;
115// }
116// PlotterDiagramCompressor::DataPoint dp;
117// if ( isValid() )
118// dp = m_parent.data()->data( CachePosition( m_index - 1, m_dataset ) );
119// if ( m_parent )
120// {
121// if ( m_index >= m_parent.data()->rowCount() )
122// m_index = -1;
123// else
124// {
125// const qreal mergeRadius = m_parent.data()->d->m_mergeRadius;
126// PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
127// while ( dp.distance( newdp ) <= mergeRadius
128// || !( m_parent.data()->d->inBoundaries( Qt::Vertical, dp ) || m_parent.data()->d->inBoundaries( Qt::Horizontal, dp ) ) )
129// {
130// ++m_index;
131// if ( m_index >= m_parent.data()->rowCount() )
132// {
133// m_index = - 1;
134// break;
135// }
136// newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
137// }
138// }
139// }
140// }
141// else
142// {
143// // we have a new point in the buffer
144// if ( m_bufferIndex >= m_buffer.count() && m_rebuffer )
145// {
146// if ( m_index < m_parent.data()->rowCount() )
147// m_buffer.append( m_parent.data()->data( CachePosition( m_index, m_dataset ) ) );
148// }
149// else
150// {
151// if ( m_bufferIndex == m_buffer.count() )
152// m_index = - 1;
153// return *this;
154// }
155// PlotterDiagramCompressor::DataPoint dp;
156// if ( isValid() )
157// dp = m_parent.data()->data( CachePosition( m_index - 1, m_dataset ) );
158// // make sure we switch to the next point which would be in the buffer
159// if ( m_parent )
160// {
161// PlotterDiagramCompressor *parent = m_parent.data();
162// if ( m_index >= parent->rowCount() )
163// m_index = -1;
164// else
165// {
166// switch ( parent->d->m_mode )
167// {
168// case( PlotterDiagramCompressor::DISTANCE ):
169// {
170// const qreal mergeRadius = m_parent.data()->d->m_mergeRadius;
171// PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
172// while ( dp.distance( newdp ) <= mergeRadius )
173// {
174// ++m_index;
175// if ( m_index >= m_parent.data()->rowCount() )
176// {
177// m_index = - 1;
178// break;
179// }
180// newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
181// }
182// }
183// break;
184// case( PlotterDiagramCompressor::BOTH ):
185// {
186// const qreal mergeRadius = m_parent.data()->d->m_mergeRadius;
187// PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
188// while ( dp.distance( newdp ) <= mergeRadius )
189// {
190// ++m_index;
191// if ( m_index >= m_parent.data()->rowCount() )
192// {
193// m_index = - 1;
194// break;
195// }
196// newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
197// }
198// }
199// break;
200// case ( PlotterDiagramCompressor::SLOPE ):
201// {
202// const qreal mergedist = parent->d->m_maxSlopeRadius;
203// qreal oldSlope = 0;
204// qreal newSlope = 0;
205
206// PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
207// PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
208// if ( m_bufferIndex > 1 )
209// {
210// oldSlope = calculateSlope( m_buffer[ m_bufferIndex - 2 ], m_buffer[ m_bufferIndex - 1 ] );
211// newSlope = calculateSlope( m_buffer[ m_bufferIndex - 1 ], newdp );
212// }
213// bool first = true;
214// while ( qAbs( newSlope - oldSlope ) < mergedist )
215// {
216// ++m_index;
217// if ( m_index >= m_parent.data()->rowCount() )
218// {
219// m_index = - 1;
220// break;
221// }
222// if ( first )
223// {
224// oldSlope = newSlope;
225// first = false;
226// }
227// olddp = newdp;
228// newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) );
229// newSlope = calculateSlope( olddp, newdp );
230// }
231// }
232// break;
233// default:
234// Q_ASSERT( false );
235// }
236// }
237// }
238// }
239// return *this;
240//}
241
242void PlotterDiagramCompressor::Iterator::handleSlopeForward(const DataPoint &dp)
243{
244 PlotterDiagramCompressor *parent = m_parent.data();
245 const qreal mergedist = parent->d->m_maxSlopeRadius;
246 qreal oldSlope = 0;
247 qreal newSlope = 0;
248
251 if (m_bufferIndex > 1) {
252 // oldSlope = calculateSlope( m_buffer[ m_bufferIndex - 2 ], m_buffer[ m_bufferIndex - 1 ] );
253 // newSlope = calculateSlope( m_buffer[ m_bufferIndex - 1 ], newdp );
254 oldSlope = calculateSlope(parent->data(CachePosition(m_index - 2, m_dataset)), parent->data(CachePosition(m_index - 1, m_dataset)));
255 newSlope = calculateSlope(parent->data(CachePosition(m_index - 1, m_dataset)), newdp);
259 int counter = 0;
260 while (accumulatedDist < mergedist) {
261 ++m_index;
262 if (m_index >= m_parent.data()->rowCount()) {
263 m_index = -1;
264 if (m_buffer.last() != parent->data(CachePosition(parent->rowCount() - 1, m_dataset)))
265 m_index = parent->rowCount();
266 break;
267 }
269 olddp = newdp;
270 newdp = parent->data(CachePosition(m_index, m_dataset));
273 if (olddist == newdist) {
274 ++counter;
275 } else {
276 if (counter > 10)
277 break;
278 }
281 }
282 m_buffer.append(newdp);
283 } else
284 m_buffer.append(dp);
285}
286
288{
289 PlotterDiagramCompressor *parent = m_parent.data();
291 const int count = parent->rowCount();
292 // increment the indexes
293 ++m_index;
294 ++m_bufferIndex;
295 // if the index reached the end of the datamodel make this iterator an enditerator
296 // and make sure the buffer was not already build, if that's the case its not necessary
297 // to rebuild it and it would be hard to extend it as we had to know where m_index was
298 if (m_index >= count || (!m_rebuffer && m_bufferIndex == m_buffer.count())) {
299 if (m_bufferIndex == m_buffer.count()) {
300 if (m_buffer.last() != parent->data(CachePosition(parent->rowCount() - 1, m_dataset)))
301 m_index = parent->rowCount();
302 else
303 m_index = -1;
304 ++m_bufferIndex;
305 } else
306 m_index = -1;
307 }
308 // if we reached the end of the buffer continue filling the buffer
309 if (m_bufferIndex == m_buffer.count() && m_index >= 0 && m_rebuffer) {
310 PlotterDiagramCompressor::DataPoint dp = parent->data(CachePosition(m_index, m_dataset));
311 if (parent->d->inBoundaries(Qt::Vertical, dp) && parent->d->inBoundaries(Qt::Horizontal, dp)) {
312 if (parent->d->m_mode == PlotterDiagramCompressor::SLOPE)
313 handleSlopeForward(dp);
314 } else {
315 m_index = -1;
316 }
317 }
318 return *this;
319}
320
322{
323 Iterator result = *this;
324 ++result;
325 return result;
326}
327
329{
330 for (int index = m_index; index + value != m_index; ++(*this)) { };
331 return *this;
332}
333
335{
336 --m_index;
337 --m_bufferIndex;
338 return *this;
339}
340
342{
343 Iterator result = *this;
344 --result;
345 return result;
346}
347
349{
350 m_index -= value;
351 return *this;
352}
353
355{
356 if (!m_parent)
358 Q_ASSERT(m_parent);
359 if (m_index == m_parent.data()->rowCount())
360 return m_parent.data()->data(CachePosition(m_parent.data()->rowCount() - 1, m_dataset));
361 return m_buffer[m_bufferIndex];
362}
363
365{
366 return m_parent.data() == other.m_parent.data() && m_index == other.m_index && m_dataset == other.m_dataset;
367}
368
373
375{
376 m_dataset = -1;
377}
378
379PlotterDiagramCompressor::Private::Private(PlotterDiagramCompressor *parent)
380 : m_parent(parent)
381 , m_model(nullptr)
382 , m_mergeRadius(0.1)
383 , m_maxSlopeRadius(0.1)
388{
389}
390
391void PlotterDiagramCompressor::Private::setModelToZero()
392{
393 m_model = nullptr;
394}
395
396inline bool inBoundary(const QPair<qreal, qreal> &bounds, qreal value)
397{
398 return bounds.first <= value && value <= bounds.second;
399}
400
401bool PlotterDiagramCompressor::Private::inBoundaries(Qt::Orientation orient, const PlotterDiagramCompressor::DataPoint &dp) const
402{
404 return inBoundary(m_forcedYBoundaries, dp.value);
405 } else if (forcedBoundaries(Qt::Horizontal)) {
406 return inBoundary(m_forcedXBoundaries, dp.key);
407 }
408 return true;
409}
410
413// void PlotterDiagramCompressor::Private::rowsInserted( const QModelIndex& /*parent*/, int start, int end )
414//{
415
416// if ( m_bufferlist.count() > 0 && !m_bufferlist[ 0 ].isEmpty() && start < m_bufferlist[ 0 ].count() )
417// {
418// calculateDataBoundaries();
419// clearBuffer();
420// return;
421// }
422// // we are handling appends only here, a prepend might be added, insert is expensive if not needed
423// qreal minX = std::numeric_limits< qreal >::max();
424// qreal minY = std::numeric_limits< qreal >::max();
425// qreal maxX = std::numeric_limits< qreal >::min();
426// qreal maxY = std::numeric_limits< qreal >::min();
427// for ( int dataset = 0; dataset < m_bufferlist.size(); ++dataset )
428// {
429// PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last();
430
431// qreal oldSlope = 0;
432// qreal newSlope = 0;
433// PlotterDiagramCompressor::DataPoint newdp = m_parent->data( CachePosition( start, dataset ) );
434// PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint();
435// const int datacount = m_bufferlist[ dataset ].count();
436// if ( m_mode != PlotterDiagramCompressor::DISTANCE && m_bufferlist[ dataset ].count() > 1 )
437// {
438// oldSlope = calculateSlope( m_bufferlist[ dataset ][ datacount - 2 ], m_bufferlist[ dataset ][ datacount - 1 ] );
439// newSlope = calculateSlope( m_bufferlist[ dataset ][ datacount - 1 ], newdp );
440// }
441// bool first = true;
442// for ( int row = start; row <= end; ++row )
443// {
444// PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) );
445// const bool checkcur = inBoundaries( Qt::Vertical, curdp ) && inBoundaries( Qt::Horizontal, curdp );
446// const bool checkpred = inBoundaries( Qt::Vertical, predecessor ) && inBoundaries( Qt::Horizontal, predecessor );
447// const bool check = checkcur || checkpred;
448// switch ( m_mode )
449// {
450// case( PlotterDiagramCompressor::BOTH ):
451// {
452// if ( predecessor.distance( curdp ) > m_mergeRadius && check )
453// {
454// if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
455// {
456// m_bufferlist[ dataset ].append( curdp );
457// }
458// else if ( !m_bufferlist[ dataset ].isEmpty() )
459// {
460// m_bufferlist[ dataset ].insert( row, curdp );
461// }
462// predecessor = curdp;
463// minX = qMin( curdp.key, m_boundary.first.x() );
464// minY = qMin( curdp.value, m_boundary.first.y() );
465// maxX = qMax( curdp.key, m_boundary.second.x() );
466// maxY = qMax( curdp.value, m_boundary.second.y() );
467// }
468// }
469// break;
470// case ( PlotterDiagramCompressor::DISTANCE ):
471// {
472// if ( predecessor.distance( curdp ) > m_mergeRadius && check )
473// {
474// if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
475// {
476// m_bufferlist[ dataset ].append( curdp );
477// }
478// else if ( !m_bufferlist[ dataset ].isEmpty() )
479// {
480// m_bufferlist[ dataset ].insert( row, curdp );
481// }
482// predecessor = curdp;
483// minX = qMin( curdp.key, m_boundary.first.x() );
484// minY = qMin( curdp.value, m_boundary.first.y() );
485// maxX = qMax( curdp.key, m_boundary.second.x() );
486// maxY = qMax( curdp.value, m_boundary.second.y() );
487// }
488// }
489// break;
490// case( PlotterDiagramCompressor::SLOPE ):
491// {
492// if ( check && qAbs( newSlope - oldSlope ) >= m_maxSlopeRadius )
493// {
494// if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() )
495// {
496// m_bufferlist[ dataset ].append( curdp );
497// oldSlope = newSlope;
498// }
499// else if ( !m_bufferlist[ dataset ].isEmpty() )
500// {
501// m_bufferlist[ dataset ].insert( row, curdp );
502// oldSlope = newSlope;
503// }
504
505// predecessor = curdp;
506// minX = qMin( curdp.key, m_boundary.first.x() );
507// minY = qMin( curdp.value, m_boundary.first.y() );
508// maxX = qMax( curdp.key, m_boundary.second.x() );
509// maxY = qMax( curdp.value, m_boundary.second.y() );
510
511// if ( first )
512// {
513// oldSlope = newSlope;
514// first = false;
515// }
516// olddp = newdp;
517// newdp = m_parent->data( CachePosition( row, dataset ) );
518// newSlope = calculateSlope( olddp, newdp );
519// }
520// }
521// break;
522// }
523// }
524// }
525// setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) );
526// Q_EMIT m_parent->rowCountChanged();
527//}
528#include <QDebug>
529// TODO this is not threadsafe do never try to invoke the painting in a different thread than this
530// method
531void PlotterDiagramCompressor::Private::rowsInserted(const QModelIndex & /*parent*/, int start, int end)
532{
533 // Q_ASSERT( std::numeric_limits<qreal>::quiet_NaN() < 5 || std::numeric_limits<qreal>::quiet_NaN() > 5 );
534 // Q_ASSERT( 5 == qMin( std::numeric_limits<qreal>::quiet_NaN(), 5.0 ) );
535 // Q_ASSERT( 5 == qMax( 5.0, std::numeric_limits<qreal>::quiet_NaN() ) );
536 if (m_bufferlist.count() > 0 && !m_bufferlist[0].isEmpty() && start < m_bufferlist[0].count()) {
537 calculateDataBoundaries();
538 clearBuffer();
539 return;
540 }
541
542 // we are handling appends only here, a prepend might be added, insert is expensive if not needed
543 qreal minX = m_boundary.first.x();
544 qreal minY = m_boundary.first.y();
545 qreal maxX = m_boundary.second.x();
546 qreal maxY = m_boundary.second.y();
547 for (int dataset = 0; dataset < m_bufferlist.size(); ++dataset) {
550 qreal oldSlope = 0;
551 qreal newSlope = 0;
552 int counter = 0;
553
554 PlotterDiagramCompressor::DataPoint newdp = m_parent->data(CachePosition(start, dataset));
556 if (start > 1) {
557 oldSlope = calculateSlope(m_parent->data(CachePosition(start - 2, dataset)), m_parent->data(CachePosition(start - 1, dataset)));
558 olddp = m_parent->data(CachePosition(start - 1, dataset));
559 } else {
560 m_bufferlist[dataset].append(newdp);
561 minX = qMin(minX, newdp.key);
562 minY = qMin(minY, newdp.value);
563 maxX = qMax(newdp.key, maxX);
564 maxY = qMax(newdp.value, maxY);
565 continue;
566 }
567
568 qreal olddist = 0;
569 qreal newdist = 0;
570 for (int row = start; row <= end; ++row) {
571 PlotterDiagramCompressor::DataPoint curdp = m_parent->data(CachePosition(row, dataset));
572 newdp = curdp;
579 const bool check = checkcur || checkpred;
580
582 if (start > m_bufferlist[dataset].count() && !m_bufferlist[dataset].isEmpty()) {
583 m_bufferlist[dataset].append(curdp);
584 } else if (!m_bufferlist[dataset].isEmpty()) {
585 m_bufferlist[dataset].insert(row, curdp);
586 }
589 }
590 minX = qMin(minX, curdp.key);
591 minY = qMin(minY, curdp.value);
592 maxX = qMax(curdp.key, maxX);
593 maxY = qMax(curdp.value, maxY);
594
596 olddp = newdp;
597 if (olddist == newdist) {
598 ++counter;
599 } else {
600 if (counter > 10) {
601 m_bufferlist[dataset].append(curdp);
604 }
605 }
606 }
607 setBoundaries(qMakePair(QPointF(minX, minY), QPointF(maxX, maxY)));
608 } else {
610
611 for (int row = start; row <= end; ++row) {
612 PlotterDiagramCompressor::DataPoint curdp = m_parent->data(CachePosition(row, dataset));
615 const bool check = checkcur || checkpred;
616 if (predecessor.distance(curdp) > m_mergeRadius && check) {
617 if (start > m_bufferlist[dataset].count() && !m_bufferlist[dataset].isEmpty()) {
618 m_bufferlist[dataset].append(curdp);
619 } else if (!m_bufferlist[dataset].isEmpty()) {
620 m_bufferlist[dataset].insert(row, curdp);
621 }
623 qreal minX = qMin(curdp.key, m_boundary.first.x());
624 qreal minY = qMin(curdp.value, m_boundary.first.y());
625 qreal maxX = qMax(curdp.key, m_boundary.second.x());
626 qreal maxY = qMax(curdp.value, m_boundary.second.y());
627 setBoundaries(qMakePair(QPointF(minX, minY), QPointF(maxX, maxY)));
628 }
629 }
630 }
631 }
632 Q_EMIT m_parent->rowCountChanged();
633}
634
636{
637 Q_ASSERT(d);
638 if (d->m_mode != value) {
639 d->m_mode = value;
640 d->clearBuffer();
642 }
643}
644
645void PlotterDiagramCompressor::Private::setBoundaries(const Boundaries &bound)
646{
647 if (bound != m_boundary) {
649 Q_EMIT m_parent->boundariesChanged();
650 }
651}
652
653void PlotterDiagramCompressor::Private::calculateDataBoundaries()
654{
656 qreal minX = std::numeric_limits<qreal>::quiet_NaN();
657 qreal minY = std::numeric_limits<qreal>::quiet_NaN();
658 qreal maxX = std::numeric_limits<qreal>::quiet_NaN();
659 qreal maxY = std::numeric_limits<qreal>::quiet_NaN();
660 for (int dataset = 0; dataset < m_parent->datasetCount(); ++dataset) {
661 for (int row = 0; row < m_parent->rowCount(); ++row) {
662 PlotterDiagramCompressor::DataPoint dp = m_parent->data(CachePosition(row, dataset));
663 minX = qMin(minX, dp.key);
664 minY = qMin(minY, dp.value);
665 maxX = qMax(dp.key, maxX);
666 maxY = qMax(dp.value, maxY);
671 }
672 }
675 maxY = m_forcedYBoundaries.second;
676 }
679 maxX = m_forcedXBoundaries.second;
680 }
681 setBoundaries(qMakePair(QPointF(minX, minY), QPointF(maxX, maxY)));
682 }
683}
684
685QModelIndexList PlotterDiagramCompressor::Private::mapToModel(const CachePosition &pos)
686{
687 QModelIndexList indexes;
688 QModelIndex index;
689 index = m_model->index(pos.first, pos.second * 2, QModelIndex());
690 Q_ASSERT(index.isValid());
691 indexes << index;
692 index = m_model->index(pos.first, pos.second * 2 + 1, QModelIndex());
693 Q_ASSERT(index.isValid());
694 indexes << index;
695 return indexes;
696}
697
698bool PlotterDiagramCompressor::Private::forcedBoundaries(Qt::Orientation orient) const
699{
700 if (orient == Qt::Vertical)
701 return !ISNAN(m_forcedYBoundaries.first) && !ISNAN(m_forcedYBoundaries.second);
702 else
703 return !ISNAN(m_forcedXBoundaries.first) && !ISNAN(m_forcedXBoundaries.second);
704}
705
706void PlotterDiagramCompressor::Private::clearBuffer()
707{
708 // TODO all iterator have to be invalid after this operation
709 // TODO make sure there are no regressions, the timeOfLastInvalidation should stop iterators from
710 // corrupting the cache
711 m_bufferlist.clear();
712 m_bufferlist.resize(m_parent->datasetCount());
714 m_accumulatedDistances.resize(m_parent->datasetCount());
716}
717
723
725{
726 delete d;
727 d = nullptr;
728}
729
731{
732 if (direction == Qt::Vertical) {
733 d->m_forcedYBoundaries = bounds;
734 } else {
735 d->m_forcedXBoundaries = bounds;
736 }
737 d->clearBuffer();
739}
740
742{
743 Q_ASSERT(d);
744 return d->m_model;
745}
746
748{
749 Q_ASSERT(d);
750 if (d->m_model) {
751 d->m_model->disconnect(this);
752 d->m_model->disconnect(d);
753 }
754 d->m_model = model;
755 if (d->m_model) {
756 d->m_bufferlist.resize(datasetCount());
757 d->m_accumulatedDistances.resize(datasetCount());
758 d->calculateDataBoundaries();
759 connect(d->m_model, &QAbstractItemModel::rowsInserted, d, &Private::rowsInserted);
760 connect(d->m_model, &QAbstractItemModel::modelReset, d, &Private::clearBuffer);
761 connect(d->m_model, &QAbstractItemModel::destroyed, d, &Private::setModelToZero);
762 }
763}
764
766{
767 DataPoint point;
768 QModelIndexList indexes = d->mapToModel(pos);
769 Q_ASSERT(indexes.count() == 2);
770 QVariant yValue = d->m_model->data(indexes.last());
771 QVariant xValue = d->m_model->data(indexes.first());
772 Q_ASSERT(xValue.isValid());
773 Q_ASSERT(yValue.isValid());
774 bool ok = false;
775 point.key = xValue.toReal(&ok);
776 Q_ASSERT(ok);
777 ok = false;
778 point.value = yValue.toReal(&ok);
779 Q_ASSERT(ok);
780 point.index = indexes.first();
781 return point;
782}
783
785{
786 if (d->m_mergeRadius != radius) {
787 d->m_mergeRadius = radius;
788 if (d->m_mode != PlotterDiagramCompressor::SLOPE)
790 }
791}
792
794{
795 if (d->m_maxSlopeRadius != value) {
796 d->m_maxSlopeRadius = value;
798 }
799}
800
802{
803 return d->m_maxSlopeRadius;
804}
805
807{
809 const qreal width = radius * (bounds.second.x() - bounds.first.x());
810 const qreal height = radius * (bounds.second.y() - bounds.first.y());
811 const qreal realRadius = std::sqrt(width * height);
813}
814
816{
817 return d->m_model ? d->m_model->rowCount() : 0;
818}
819
821{
822 d->clearBuffer();
823}
824
826{
827 if (d->m_model && d->m_model->columnCount() == 0)
828 return 0;
829 return d->m_model ? (d->m_model->columnCount() + 1) / 2 : 0;
830}
831
833{
834 Boundaries bounds = d->m_boundary;
835 if (d->forcedBoundaries(Qt::Vertical)) {
836 bounds.first.setY(d->m_forcedYBoundaries.first);
837 bounds.second.setY(d->m_forcedYBoundaries.second);
838 }
839 if (d->forcedBoundaries(Qt::Horizontal)) {
840 bounds.first.setX(d->m_forcedXBoundaries.first);
841 bounds.second.setX(d->m_forcedXBoundaries.second);
842 }
843 return bounds;
844}
845
847{
849 return Iterator(dataSet, this, d->m_bufferlist[dataSet]);
850}
851
853{
854 Iterator it(dataSet, this);
855 it.m_index = -1;
856 return it;
857}
qreal calculateSlope(const PlotterDiagramCompressor::DataPoint &lhs, const PlotterDiagramCompressor::DataPoint &rhs)
bool inBoundary(const QPair< qreal, qreal > &bounds, qreal value)
Iterator(int dataSet, PlotterDiagramCompressor *parent)
QPair< QPointF, QPointF > dataBoundaries() const
DataPoint data(const CachePosition &pos) const
void setForcedDataBoundaries(const QPair< qreal, qreal > &bounds, Qt::Orientation direction)
void rowsInserted(const QModelIndex &parent, int first, int last)
QDateTime currentDateTime()
bool isValid() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void destroyed(QObject *obj)
QObject * parent() const const
T qobject_cast(QObject *object)
Vertical

© 2001 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-chart/
Generated on Wed May 1 2024 00:01:10 for KD Chart API Documentation by doxygen 1.9.8