! \internal Returns via \a scatterData the data points that need to be visualized for this graph when plotting scatter points, taking into consideration the currently visible axis ranges and, if \ref setAdaptiveSampling is enabled, local point densities. The considered data can be restricted further by \a begin and \a end, e.g. to only plot a certain segment of the data (see \ref getDat
| 20996 | \see getOptimizedLineData |
| 20997 | */ |
| 20998 | void QCPGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const |
| 20999 | { |
| 21000 | if (!scatterData) return; |
| 21001 | QCPAxis *keyAxis = mKeyAxis.data(); |
| 21002 | QCPAxis *valueAxis = mValueAxis.data(); |
| 21003 | if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } |
| 21004 | |
| 21005 | const int scatterModulo = mScatterSkip+1; |
| 21006 | const bool doScatterSkip = mScatterSkip > 0; |
| 21007 | int beginIndex = begin-mDataContainer->constBegin(); |
| 21008 | int endIndex = end-mDataContainer->constBegin(); |
| 21009 | while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter |
| 21010 | { |
| 21011 | ++beginIndex; |
| 21012 | ++begin; |
| 21013 | } |
| 21014 | if (begin == end) return; |
| 21015 | int dataCount = end-begin; |
| 21016 | int maxCount = (std::numeric_limits<int>::max)(); |
| 21017 | if (mAdaptiveSampling) |
| 21018 | { |
| 21019 | int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)); |
| 21020 | maxCount = 2*keyPixelSpan+2; |
| 21021 | } |
| 21022 | |
| 21023 | if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average |
| 21024 | { |
| 21025 | double valueMaxRange = valueAxis->range().upper; |
| 21026 | double valueMinRange = valueAxis->range().lower; |
| 21027 | QCPGraphDataContainer::const_iterator it = begin; |
| 21028 | int itIndex = beginIndex; |
| 21029 | double minValue = it->value; |
| 21030 | double maxValue = it->value; |
| 21031 | QCPGraphDataContainer::const_iterator minValueIt = it; |
| 21032 | QCPGraphDataContainer::const_iterator maxValueIt = it; |
| 21033 | QCPGraphDataContainer::const_iterator currentIntervalStart = it; |
| 21034 | int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction |
| 21035 | int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey |
| 21036 | double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound)); |
| 21037 | double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates |
| 21038 | bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) |
| 21039 | int intervalDataCount = 1; |
| 21040 | // advance iterator to second (non-skipped) data point because adaptive sampling works in 1 point retrospect: |
| 21041 | if (!doScatterSkip) |
| 21042 | ++it; |
| 21043 | else |
| 21044 | { |
| 21045 | itIndex += scatterModulo; |
| 21046 | if (itIndex < endIndex) // make sure we didn't jump over end |
| 21047 | it += scatterModulo; |
| 21048 | else |
| 21049 | { |
| 21050 | it = end; |
| 21051 | itIndex = endIndex; |
| 21052 | } |
| 21053 | } |
| 21054 | // main loop over data points: |
| 21055 | while (it != end) |
nothing calls this directly
no test coverage detected