diff --git a/QLop.pro b/QLop.pro --- a/QLop.pro +++ b/QLop.pro @@ -13,8 +13,12 @@ TEMPLATE = app INCLUDEPATH += src/QCustomPlot src -QMAKE_CXXFLAGS += -O5 -fopenmp -march=corei7-avx -mtune=corei7-avx -mavx -QMAKE_LFLAGS += -O5 -fopenmp -march=corei7-avx -mtune=corei7-avx -mavx +QMAKE_CXXFLAGS_RELEASE += -O5 -fopenmp -march=corei7-avx -mtune=corei7-avx -mavx +QMAKE_LFLAGS_RELEASE += -O5 -fopenmp -march=corei7-avx -mtune=corei7-avx -mavx + +QMAKE_CXXFLAGS_DEBUG += -O0 -fopenmp -march=corei7-avx -mtune=corei7-avx -mavx +QMAKE_LFLAGS_DEBUG += -O0 -fopenmp -march=corei7-avx -mtune=corei7-avx -mavx + SOURCES += src/main.cpp\ src/mainwindow.cpp \ diff --git a/src/QCustomPlot/qcustomplot.cpp b/src/QCustomPlot/qcustomplot.cpp --- a/src/QCustomPlot/qcustomplot.cpp +++ b/src/QCustomPlot/qcustomplot.cpp @@ -5256,7 +5256,7 @@ void QCPAxis::rescale(bool onlyVisiblePl if (p.at(i)->keyAxis() == this) plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain); else - plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain); + plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain); if (currentFoundRange) { if (!haveRange) @@ -9369,6 +9369,7 @@ void QCustomPlot::setMultiSelectModifier mMultiSelectModifier = modifier; } + /*! Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect. @@ -14502,7 +14503,7 @@ QCPGraph::QCPGraph(QCPAxis *keyAxis, QCP QCPAbstractPlottable(keyAxis, valueAxis) { mData = new QCPDataMap; - + mDataVector = new QVector(); setPen(QPen(Qt::blue, 0)); setErrorPen(QPen(Qt::black)); setBrush(Qt::NoBrush); @@ -14515,6 +14516,7 @@ QCPGraph::QCPGraph(QCPAxis *keyAxis, QCP setErrorBarSkipSymbol(true); setChannelFillGraph(0); setAdaptiveSampling(true); + setUseFastVectors(false); } QCPGraph::~QCPGraph() @@ -14569,6 +14571,15 @@ void QCPGraph::setData(const QVector *data) +{ + if(data!=mDataVector) + { + delete mDataVector; + this->mDataVector = data; + } +} + /*! Replaces the current data with the provided points in \a key and \a value pairs. Additionally the symmetrical value error of the data points are set to the values in \a valueError. @@ -14869,6 +14880,11 @@ void QCPGraph::setAdaptiveSampling(bool mAdaptiveSampling = enabled; } +void QCPGraph::setUseFastVectors(bool useFastVectors) +{ + mUseFastVectors=useFastVectors; +} + /*! Adds the provided data points in \a dataMap to the current data. @@ -15095,7 +15111,7 @@ void QCPGraph::rescaleValueAxis(bool onl void QCPGraph::draw(QCPPainter *painter) { if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } - if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return; + if (mKeyAxis.data()->range().size() <= 0 || (mData->isEmpty() && mDataVector->isEmpty())) return; if (mLineStyle == lsNone && mScatterStyle.isNone()) return; // allocate line and (if necessary) point vectors: @@ -15239,7 +15255,10 @@ void QCPGraph::getLinePlotData(QVector lineData; - getPreparedData(&lineData, scatterData); + if(mUseFastVectors) + getPreparedDataVector(&lineData, scatterData); + else + getPreparedData(&lineData, scatterData); linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill linePixelData->resize(lineData.size()); @@ -15850,6 +15869,190 @@ void QCPGraph::getPreparedData(QVector *lineData, QVector *scatterData) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + // get visible data range: + QVector::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point + getVisibleDataBoundsVector(lower, upper); + if (lower == mDataVector->constEnd() || upper == mDataVector->constEnd()) + return; + + // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling: + int maxCount = std::numeric_limits::max(); + if (mAdaptiveSampling) + { + int keyPixelSpan = qAbs(keyAxis->coordToPixel((*lower).key)-keyAxis->coordToPixel((*upper).key)); + maxCount = 2*keyPixelSpan+2; + } + int dataCount = countDataInBoundsVector(lower, upper, maxCount); + + if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average + { + if (lineData) + { + QVector::const_iterator it = lower; + QVector::const_iterator upperEnd = upper+1; + double minValue = (*it).value; + double maxValue = (*it).value; + QVector::const_iterator currentIntervalFirstPoint = it; + int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction + int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey + double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*lower).key)+reversedRound)); + double lastIntervalEndKey = currentIntervalStartKey; + double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates + bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) + int intervalDataCount = 1; + ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect + while (it != upperEnd) + { + if ((*it).key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary + { + if ((*it).value < minValue) + minValue = (*it).value; + else if ((*it).value > maxValue) + maxValue = (*it).value; + ++intervalDataCount; + } else // new pixel interval started + { + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster + { + if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, (*currentIntervalFirstPoint).value)); + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); + if ((*it).key > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (*(it-1)).value)); + } else + lineData->append(QCPData((*currentIntervalFirstPoint).key, (*currentIntervalFirstPoint).value)); + lastIntervalEndKey = (*(it-1)).key; + minValue = (*it).value; + maxValue = (*it).value; + currentIntervalFirstPoint = it; + currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*it).key)+reversedRound)); + if (keyEpsilonVariable) + keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); + intervalDataCount = 1; + } + ++it; + } + // handle last interval: + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster + { + if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, (*currentIntervalFirstPoint).value)); + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); + lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); + } else + lineData->append(QCPData((*currentIntervalFirstPoint).key, (*currentIntervalFirstPoint).value)); + } + + if (scatterData) + { + double valueMaxRange = valueAxis->range().upper; + double valueMinRange = valueAxis->range().lower; + QVector::const_iterator it = lower; + QVector::const_iterator upperEnd = upper+1; + double minValue = (*it).value; + double maxValue = (*it).value; + QVector::const_iterator minValueIt = it; + QVector::const_iterator maxValueIt = it; + QVector::const_iterator currentIntervalStart = it; + int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction + int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey + double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*lower).key)+reversedRound)); + double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates + bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) + int intervalDataCount = 1; + ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect + while (it != upperEnd) + { + if ((*it).key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary + { + if ((*it).value < minValue && (*it).value > valueMinRange && (*it).value < valueMaxRange) + { + minValue = (*it).value; + minValueIt = it; + } else if ((*it).value > maxValue && (*it).value > valueMinRange && (*it).value < valueMaxRange) + { + maxValue = (*it).value; + maxValueIt = it; + } + ++intervalDataCount; + } else // new pixel started + { + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them + { + // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): + double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); + int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average + QVector::const_iterator intervalIt = currentIntervalStart; + int c = 0; + while (intervalIt != it) + { + if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && (*intervalIt).value > valueMinRange && (*intervalIt).value < valueMaxRange) + scatterData->append((*intervalIt)); + ++c; + ++intervalIt; + } + } else if ((*currentIntervalStart).value > valueMinRange && (*currentIntervalStart).value < valueMaxRange) + scatterData->append((*currentIntervalStart)); + minValue = (*it).value; + maxValue = (*it).value; + currentIntervalStart = it; + currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel((*it).key)+reversedRound)); + if (keyEpsilonVariable) + keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); + intervalDataCount = 1; + } + ++it; + } + // handle last interval: + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them + { + // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): + double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); + int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average + QVector::const_iterator intervalIt = currentIntervalStart; + int c = 0; + while (intervalIt != it) + { + if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && (*intervalIt).value > valueMinRange && (*intervalIt).value < valueMaxRange) + scatterData->append((*intervalIt)); + ++c; + ++intervalIt; + } + } else if ((*currentIntervalStart).value > valueMinRange && (*currentIntervalStart).value < valueMaxRange) + scatterData->append(*currentIntervalStart); + } + } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters + { + QVector *dataVector = 0; + if (lineData) + { + dataVector = lineData; + } + else if (scatterData) + dataVector = scatterData; + if (dataVector) + { + QVector::const_iterator it = lower; + QVector::const_iterator upperEnd = upper+1; + dataVector->reserve(dataCount+2); // +2 for possible fill end points + while (it != upperEnd) + { + dataVector->append(*it); + ++it; + } + } + if (lineData && scatterData) + *scatterData = *dataVector; + } +} + /*! \internal called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data @@ -15988,6 +16191,187 @@ void QCPGraph::getVisibleDataBounds(QCPD upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn } + + + +QVector::const_iterator __lowerBoundDico(QVector* vector,double key) +{ + int DX=vector->size()/2; + int pos=DX; + if(key>=((*vector)[vector->length()-1].key)) + return vector->constEnd(); + if(key<=((*vector)[0].key)) + return vector->constBegin(); + while (DX>1) + { + DX=DX/2; + if((*vector)[pos].key > key) + { + pos-=DX; + } + else + { + pos+=DX; + } + } + return vector->constBegin()+pos+1; +} + + +QVector::const_iterator __upperBoundDico(QVector* vector,double key) +{ + int DX=vector->size()/2; + int pos=DX; + if(key>=((*vector)[vector->length()-1].key)) + return vector->constEnd(); + if(key<=((*vector)[0].key)) + return vector->constBegin(); + while (DX>1) + { + DX=DX/2; + if((*vector)[pos].key > key) + { + pos-=DX; + } + else + { + pos+=DX; + } + } + if((*vector)[pos].key >= key) + return vector->constBegin()+pos; + return vector->constBegin()+pos+1; +} + +QVector::const_iterator __lowerBound(QVector* vector,double key) +{ + if(vector->length()) + { + double min=(*vector)[0].key,max=(*vector)[vector->length()-1].key; + int speculated=(int)((key/(max-min))*(vector->length()-1)); + double speculatedKey=(*vector)[speculated].key; + double prevKey; + double nextKey; + if(speculated>0) + prevKey= (*vector)[speculated-1].key; + else + prevKey=speculatedKey; + if(speculatedcount()-2) + nextKey = (*vector)[speculated+1].key; + else + nextKey=speculated; + + if(key>=max) + return vector->constEnd(); + if(key<=min) + return vector->constBegin(); + + while ((speculatedKey!=key) && !( (speculatedKey>key) && (prevKeykey) && (prevKeykey))) + { + speculated++; + break; + } + if(speculatedKey > key) + max=speculatedKey; + else + min=speculatedKey; + if(speculated>0) + prevKey= (*vector)[speculated-1].key; + else + prevKey=speculatedKey; + if(speculatedcount()-2) + nextKey = (*vector)[speculated+1].key; + else + nextKey=speculated; + speculated=(int)((key/(max-min))*(vector->length()-1)); + speculatedKey=(*vector)[speculated].key; + } + if(speculatedKey==key) + return vector->constBegin()+speculated+1; + else + return vector->constBegin()+speculated; + } + return vector->constBegin(); +} + +QVector::const_iterator __upperBound(QVector* vector,double key) +{ + if(vector->length()) + { + double min=(*vector)[0].key,max=(*vector)[vector->length()-1].key; + int speculated=(int)((key/(max-min))*(vector->length()-1)); + double speculatedKey=(*vector)[speculated].key; + double prevKey; + double nextKey; + if(speculated>0) + prevKey= (*vector)[speculated-1].key; + else + prevKey=speculatedKey; + if(speculatedcount()-2) + nextKey = (*vector)[speculated+1].key; + else + nextKey=speculated; + + if(key>=max) + return vector->constEnd(); + if(key<=min) + return vector->constBegin(); + + while ((speculatedKey!=key) && !( (speculatedKey>key) && (prevKeykey) && (prevKeykey))) + { + speculated++; + break; + } + if(speculatedKey > key) + max=speculatedKey; + else + min=speculatedKey; + if(speculated>0) + prevKey= (*vector)[speculated-1].key; + else + prevKey=speculatedKey; + if(speculatedcount()-2) + nextKey = (*vector)[speculated+1].key; + else + nextKey=speculated; + speculated=(int)((key/(max-min))*(vector->length()-1)); + speculatedKey=(*vector)[speculated].key; + } + return vector->constBegin()+speculated; + } + return vector->constEnd(); +} + +void QCPGraph::getVisibleDataBoundsVector(QVector::const_iterator &lower, QVector::const_iterator &upper) const +{ + if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + if (mDataVector->isEmpty()) + { + lower = mDataVector->constEnd(); + upper = mDataVector->constEnd(); + return; + } + QVector::const_iterator lbound = __lowerBoundDico(mDataVector,mKeyAxis.data()->range().lower); + QVector::const_iterator ubound = __lowerBoundDico(mDataVector,mKeyAxis.data()->range().upper); + bool lowoutlier = lbound != mDataVector->constBegin(); // indicates whether there exist points below axis range + bool highoutlier = ubound != mDataVector->constEnd(); // indicates whether there exist points above axis range + + lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn + upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn +} + /*! \internal Counts the number of data points between \a lower and \a upper (including them), up to a maximum @@ -16012,6 +16396,20 @@ int QCPGraph::countDataInBounds(const QC return count; } +int QCPGraph::countDataInBoundsVector(const QVector::const_iterator &lower, const QVector::const_iterator &upper, int maxCount) const +{ + if (upper == mDataVector->constEnd() && lower == mDataVector->constEnd()) + return 0; + QVector::const_iterator it = lower; + int count = 1; + while (it != upper && count < maxCount) + { + ++it; + ++count; + } + return count; +} + /*! \internal The line data vector generated by e.g. getLinePlotData contains only the line that connects the @@ -16497,6 +16895,8 @@ QCPRange QCPGraph::getKeyRange(bool &fou { // just call the specialized version which takes an additional argument whether error bars // should also be taken into consideration for range calculation. We set this to true here. + if(mUseFastVectors) + return getKeyRangeVector(foundRange, inSignDomain, true); return getKeyRange(foundRange, inSignDomain, true); } @@ -16505,6 +16905,8 @@ QCPRange QCPGraph::getValueRange(bool &f { // just call the specialized version which takes an additional argument whether error bars // should also be taken into consideration for range calculation. We set this to true here. + if(mUseFastVectors) + return getValueRangeVector(foundRange, inSignDomain, true); return getValueRange(foundRange, inSignDomain, true); } @@ -16614,6 +17016,106 @@ QCPRange QCPGraph::getKeyRange(bool &fou return range; } +QCPRange QCPGraph::getKeyRangeVector(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + double current, currentErrorMinus, currentErrorPlus; + + if (inSignDomain == sdBoth) // range may be anywhere + { + QVector::const_iterator it = mDataVector->constBegin(); + while (it != mDataVector->constEnd()) + { + current = (*it).key; + currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); + if (current-currentErrorMinus < range.lower || !haveLower) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if (current+currentErrorPlus > range.upper || !haveUpper) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + ++it; + } + } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain + { + QVector::const_iterator it = mDataVector->constBegin(); + while (it != mDataVector->constEnd()) + { + current = (*it).key; + currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. + { + if ((current < range.lower || !haveLower) && current < 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current < 0) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain + { + QVector::const_iterator it = mDataVector->constBegin(); + while (it != mDataVector->constEnd()) + { + current = (*it).key; + currentErrorMinus = (includeErrors ? (*it).keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? (*it).keyErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. + { + if ((current < range.lower || !haveLower) && current > 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current > 0) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + } + + foundRange = haveLower && haveUpper; + return range; +} + /*! \overload Allows to specify whether the error bars should be included in the range calculation. @@ -16720,6 +17222,105 @@ QCPRange QCPGraph::getValueRange(bool &f return range; } +QCPRange QCPGraph::getValueRangeVector(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const +{ + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + + double current, currentErrorMinus, currentErrorPlus; + + if (inSignDomain == sdBoth) // range may be anywhere + { + QVector::const_iterator it = mDataVector->constBegin(); + while (it != mDataVector->constEnd()) + { + current = (*it).value; + currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); + if (current-currentErrorMinus < range.lower || !haveLower) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if (current+currentErrorPlus > range.upper || !haveUpper) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + ++it; + } + } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain + { + QVector::const_iterator it = mDataVector->constBegin(); + while (it != mDataVector->constEnd()) + { + current = (*it).value; + currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. + { + if ((current < range.lower || !haveLower) && current < 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current < 0) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain + { + QVector::const_iterator it = mDataVector->constBegin(); + while (it != mDataVector->constEnd()) + { + current = (*it).value; + currentErrorMinus = (includeErrors ? (*it).valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? (*it).valueErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. + { + if ((current < range.lower || !haveLower) && current > 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current > 0) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + } + + foundRange = haveLower && haveUpper; + return range; +} //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPCurveData diff --git a/src/QCustomPlot/qcustomplot.h b/src/QCustomPlot/qcustomplot.h --- a/src/QCustomPlot/qcustomplot.h +++ b/src/QCustomPlot/qcustomplot.h @@ -1862,6 +1862,7 @@ protected: QCPLayer *mCurrentLayer; QCP::PlottingHints mPlottingHints; Qt::KeyboardModifier mMultiSelectModifier; + bool mUseFastVectors; // non-property members: QPixmap mPaintBuffer; @@ -2538,10 +2539,12 @@ public: bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; } QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); } bool adaptiveSampling() const { return mAdaptiveSampling; } + bool useFastVectors() const { return mUseFastVectors; } // setters: void setData(QCPDataMap *data, bool copy=false); void setData(const QVector &key, const QVector &value); + void setData(QVector *data); void setDataKeyError(const QVector &key, const QVector &value, const QVector &keyError); void setDataKeyError(const QVector &key, const QVector &value, const QVector &keyErrorMinus, const QVector &keyErrorPlus); void setDataValueError(const QVector &key, const QVector &value, const QVector &valueError); @@ -2556,6 +2559,7 @@ public: void setErrorBarSkipSymbol(bool enabled); void setChannelFillGraph(QCPGraph *targetGraph); void setAdaptiveSampling(bool enabled); + void setUseFastVectors(bool useFastVectors); // non-property methods: void addData(const QCPDataMap &dataMap); @@ -2580,6 +2584,7 @@ public: protected: // property members: QCPDataMap *mData; + QVector* mDataVector; QPen mErrorPen; LineStyle mLineStyle; QCPScatterStyle mScatterStyle; @@ -2588,6 +2593,7 @@ protected: bool mErrorBarSkipSymbol; QPointer mChannelFillGraph; bool mAdaptiveSampling; + bool mUseFastVectors; // reimplemented virtual methods: virtual void draw(QCPPainter *painter); @@ -2595,8 +2601,9 @@ protected: virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const; virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface + virtual QCPRange getKeyRangeVector(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface - + virtual QCPRange getValueRangeVector(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // introduced virtual methods: virtual void drawFill(QCPPainter *painter, QVector *lineData) const; virtual void drawScatterPlot(QCPPainter *painter, QVector *scatterData) const; @@ -2605,6 +2612,7 @@ protected: // non-virtual methods: void getPreparedData(QVector *lineData, QVector *scatterData) const; + void getPreparedDataVector(QVector *lineData, QVector *scatterData) const; void getPlotData(QVector *lineData, QVector *scatterData) const; void getScatterPlotData(QVector *scatterData) const; void getLinePlotData(QVector *linePixelData, QVector *scatterData) const; @@ -2614,7 +2622,9 @@ protected: void getImpulsePlotData(QVector *linePixelData, QVector *scatterData) const; void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const; void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const; + void getVisibleDataBoundsVector(QVector::const_iterator &lower, QVector::const_iterator &upper) const; int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const; + int countDataInBoundsVector(const QVector::const_iterator &lower, const QVector::const_iterator &upper, int maxCount) const; void addFillBasePoints(QVector *lineData) const; void removeFillBasePoints(QVector *lineData) const; QPointF lowerFillBasePoint(double lowerKey) const; diff --git a/src/SocExplorerPlot.cpp b/src/SocExplorerPlot.cpp --- a/src/SocExplorerPlot.cpp +++ b/src/SocExplorerPlot.cpp @@ -43,7 +43,6 @@ SocExplorerPlot::SocExplorerPlot(QWidget this->m_plot->setNoAntialiasingOnDrag(true); this->show(); this->m_plot->legend->setVisible(true); - } SocExplorerPlot::~SocExplorerPlot() @@ -117,6 +116,11 @@ void SocExplorerPlot::setAdaptativeSampl this->m_plot->graph(graphIndex)->setAdaptiveSampling(enable); } +void SocExplorerPlot::setUseFastVector(int graphIndex, bool enable) +{ + this->m_plot->graph(graphIndex)->setUseFastVectors(enable); +} + int SocExplorerPlot::addGraph() { this->m_plot->addGraph(); @@ -192,6 +196,16 @@ void SocExplorerPlot::setGraphData(int g this->m_plot->replot(); } +void SocExplorerPlot::setGraphData(int graphIndex,QVector *data, bool replot) +{ + if((graphIndexm_plot->graphCount()))// && (x.at(0).type()==QVariant::Double)) + { + this->m_plot->graph(graphIndex)->setData(data); + } + if(replot) + this->m_plot->replot(); +} + void SocExplorerPlot::addGraphData(int graphIndex, QList x, QList y) { if((graphIndexm_plot->graphCount()) && (x.count()==y.count()))// && (x.at(0).type()==QVariant::Double)) diff --git a/src/SocExplorerPlot.h b/src/SocExplorerPlot.h --- a/src/SocExplorerPlot.h +++ b/src/SocExplorerPlot.h @@ -43,12 +43,14 @@ public: void setLegendFont(QFont font); void setLegendSelectedFont(QFont font); void setAdaptativeSampling(int graphIndex,bool enable); + void setUseFastVector(int graphIndex,bool enable); int addGraph(); bool removeGraph(int graphIndex); void removeAllGraphs(); void setGraphName(int graphIndex,QString name); void setGraphData(int graphIndex, QList x, QList y); void setGraphData(int graphIndex, QCPDataMap* data,bool copy = true,bool replot=true); + void setGraphData(int graphIndex, QVector *data, bool replot); void addGraphData(int graphIndex, QList x, QList y); void addGraphData(int graphIndex, QVariant x, QVariant y); void setGraphPen(int graphIndex,QPen pen); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -36,11 +36,13 @@ MainWindow::MainWindow(int OMP_THREADS, connect(this->ui->addViewerQpb,SIGNAL(clicked()),this,SLOT(addFolderView())); connect(&this->fileReader,SIGNAL(dataReady(QCPDataMap*,QCPDataMap*,QCPDataMap*)),this,SLOT(dataReady(QCPDataMap*,QCPDataMap*,QCPDataMap*))); + connect(&this->fileReader,SIGNAL(dataReady(QVector*,QVector*,QVector*)),this,SLOT(dataReady(QVector*,QVector*,QVector*))); connect(this->ui->calendar,SIGNAL(activated(QDate)),this,SLOT(downloadData(QDate))); for(int i=0;i<3;i++) { this->ui->Plot->addGraph(); this->ui->Plot->setAdaptativeSampling(i,true); + this->ui->Plot->setUseFastVector(i,true); } QPen pen = this->ui->Plot->getGraphPen(0); pen.setColor(Qt::blue); @@ -124,6 +126,23 @@ void MainWindow::dataReady(QCPDataMap *c this->ui->Plot->replot(); } +void MainWindow::dataReady(QVector *ch1, QVector *ch2, QVector *ch3) +{ + for(int i=0;iprogressWidget->hide(); + this->ui->Plot->setGraphName(0,"MAG_X"); + this->ui->Plot->setGraphName(1,"MAG_Y"); + this->ui->Plot->setGraphName(2,"MAG_Z"); + this->ui->Plot->setGraphData(0,ch1,false); + this->ui->Plot->setGraphData(1,ch2,false); + this->ui->Plot->setGraphData(2,ch3,false); + this->ui->Plot->rescaleAxis(); + this->ui->Plot->replot(); +} + void MainWindow::downloadData(const QDate & date ) { QDate tmpDate; diff --git a/src/mainwindow.h b/src/mainwindow.h --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -49,6 +49,7 @@ public slots: void itemDoubleClicked(QListWidgetItem *item); void plotFile(const QString& File); void dataReady(QCPDataMap *ch1,QCPDataMap *ch2,QCPDataMap *ch3); + void dataReady(QVector *ch1,QVector *ch2,QVector *ch3); void downloadData(const QDate &date); void updateProgress(int threadId,int percentProgress); void addFolderView(); diff --git a/src/themisdatafile.cpp b/src/themisdatafile.cpp --- a/src/themisdatafile.cpp +++ b/src/themisdatafile.cpp @@ -129,9 +129,12 @@ void ThemisDataFile::run() { FILE* dataFile; dataFile = fopen(fileName.toStdString().c_str(),"r"); - QCPDataMap *ch1=new QCPDataMap(); - QCPDataMap *ch2=new QCPDataMap(); - QCPDataMap *ch3=new QCPDataMap(); + // QCPDataMap *ch1=new QCPDataMap(); + // QCPDataMap *ch2=new QCPDataMap(); + // QCPDataMap *ch3=new QCPDataMap(); + QVector *ch1=new QVector(); + QVector *ch2=new QVector(); + QVector *ch3=new QVector(); QElapsedTimer timr; double _x=0.0; @@ -157,19 +160,24 @@ void ThemisDataFile::run() timr.start(); for(int i=0;i<(FileSize/(58));i++) { -// _x= i; - _x= __decodeTimeFromEpoch((i*58),(unsigned char*)line); + _x= i; + //_x= __decodeTimeFromEpoch((i*58),(unsigned char*)line); data1.key=_x; data2.key=_x; data3.key=_x; data1.value=__decodeVal(((i*58)+27),(unsigned char*)line); data2.value=__decodeVal(((i*58)+38),(unsigned char*)line); data3.value=__decodeVal(((i*58)+49),(unsigned char*)line); - ch1->insertMulti(_x,data1); - ch2->insertMulti(_x,data2); - ch3->insertMulti(_x,data3); + + //ch1->insertMulti(_x,data1); + //ch2->insertMulti(_x,data2); + //ch3->insertMulti(_x,data3); +// x->append(_x); + ch1->append(data1); + ch2->append(data2); + ch3->append(data3); curLine++; - if(lastLineUpdate++>8000) + if(lastLineUpdate++>20000) { lastLineUpdate=0; int test=(curLine *100/ (lineCnt)); diff --git a/src/themisdatafile.h b/src/themisdatafile.h --- a/src/themisdatafile.h +++ b/src/themisdatafile.h @@ -35,6 +35,7 @@ public: void run(); signals: void dataReady(QCPDataMap *ch1,QCPDataMap *ch2,QCPDataMap *ch3); + void dataReady(QVector *ch1,QVector *ch2,QVector *ch3); void updateProgress(int percentProgress); void updateProgress(int threadId,int percentProgress); public slots: