Index: src/backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h =================================================================== --- src/backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h +++ src/backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h @@ -76,6 +76,7 @@ QVector mapLogicalToScene(const QVector&, MappingFlags flags = DefaultMapping) const override; void mapLogicalToScene(const QVector& logicalPoints, QVector& scenePoints, std::vector& visiblePoints, MappingFlags flags = DefaultMapping) const; + void mapLogicalToScene(int startIndex, int endIndex, const QVector &logicalPoints, QVector& scenePoints, std::vector &visiblePoints, QVector >& scenePointsUsed, double minLogicalDiffX, double minLogicalDiffY, MappingFlags flags = DefaultMapping) const; QPointF mapLogicalToScene(QPointF, MappingFlags flags = DefaultMapping) const override; QVector mapLogicalToScene(const QVector&, MappingFlags flags = DefaultMapping) const override; Index: src/backend/worksheet/plots/cartesian/CartesianCoordinateSystem.cpp =================================================================== --- src/backend/worksheet/plots/cartesian/CartesianCoordinateSystem.cpp +++ src/backend/worksheet/plots/cartesian/CartesianCoordinateSystem.cpp @@ -251,11 +251,11 @@ } /*! - Maps the points in logical coordinates from \c points and fills the \c visiblePoints with the points in logical coordinates restricted to the current intervals. - \param logicalPoints List of points in logical coordinates - \param scenePoints List for the points in scene coordinates - \param visiblePoints List for the logical coordinates restricted to the current region of the coordinate system - \param flags + Maps the points in logical coordinates from @p points and fills the @p visiblePoints with the points in logical coordinates restricted to the current intervals. + @param logicalPoints List of points in logical coordinates + @param scenePoints List for the points in scene coordinates + @param visiblePoints List for the logical coordinates restricted to the current region of the coordinate system + @param flags */ void CartesianCoordinateSystem::mapLogicalToScene(const QVector& logicalPoints, QVector& scenePoints, std::vector& visiblePoints, MappingFlags flags) const { @@ -295,6 +295,59 @@ } } +/*! + Maps the points in logical coordinates from \c points and fills the \c visiblePoints with the points in logical coordinates restricted to the current intervals. + If there are points, which lies on another the point will not be added a second time. + @param logicalPoints List of points in logical coordinates + @param scenePoints List for the points in scene coordinates + @param visiblePoints List for the logical coordinates restricted to the current region of the coordinate system + @param scenePointsUsed List of bools which scene point was already used + */ +void CartesianCoordinateSystem::mapLogicalToScene(int startIndex, int endIndex, const QVector& logicalPoints, + QVector& scenePoints, std::vector& visiblePoints, QVector>& scenePointsUsed, double minLogicalDiffX, double minLogicalDiffY, MappingFlags flags) const { + const QRectF pageRect = d->plot->dataRect(); + const bool noPageClipping = pageRect.isNull() || (flags & SuppressPageClipping); + + for (const auto* xScale : d->xScales) { + if (!xScale) continue; + + for (const auto* yScale : d->yScales) { + if (!yScale) continue; + + for (int i = startIndex; i <= endIndex; ++i) { + const QPointF& point = logicalPoints.at(i); + + double x = point.x(); + if (!xScale->contains(x)) + continue; + + if (!xScale->map(&x)) + continue; + + double y = point.y(); + if (!yScale->contains(y)) + continue; + + if (!yScale->map(&y)) + continue; + + const QPointF mappedPoint(x, y); + if (noPageClipping || rectContainsPoint(pageRect, mappedPoint)) { + + int indexX = (int)((mappedPoint.x() - d->plot->dataRect().x())*minLogicalDiffX); + int indexY = (int)((mappedPoint.y() - d->plot->dataRect().y())*minLogicalDiffY); + if (scenePointsUsed[indexX][indexY]) + continue; + + scenePointsUsed[indexX][indexY] = true; + scenePoints.append(mappedPoint); + visiblePoints[i].flip(); + } + } + } + } +} + QPointF CartesianCoordinateSystem::mapLogicalToScene(QPointF logicalPoint, MappingFlags flags) const { const QRectF pageRect = d->plot->dataRect(); bool noPageClipping = pageRect.isNull() || (flags & SuppressPageClipping); Index: src/backend/worksheet/plots/cartesian/XYCurve.cpp =================================================================== --- src/backend/worksheet/plots/cartesian/XYCurve.cpp +++ src/backend/worksheet/plots/cartesian/XYCurve.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -859,6 +860,7 @@ bool oldValue = isVisible(); setVisible(on); emit q->visibilityChanged(on); + retransform(); return oldValue; } @@ -868,11 +870,16 @@ triggers the update of lines, drop lines, symbols etc. */ void XYCurvePrivate::retransform() { + + if (!isVisible()) + return; + DEBUG("\nXYCurvePrivate::retransform() name = " << name().toStdString() << ", m_suppressRetransform = " << m_suppressRetransform); DEBUG(" plot = " << plot); if (m_suppressRetransform || !plot) return; + { #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform()"); #endif @@ -899,13 +906,53 @@ WAIT_CURSOR; //calculate the scene coordinates - { -#ifdef PERFTRACE_CURVES - PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform(), map logical points to scene coordinates"); -#endif - cSystem->mapLogicalToScene(symbolPointsLogical, symbolPointsScene, visiblePoints); + if (symbolsStyle != Symbol::NoSymbols || valuesType != XYCurve::NoValues ) { + { + #ifdef PERFTRACE_CURVES + PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform(), map logical points to scene coordinates"); + #endif + + float widthDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().width(), Worksheet::Inch); + float heightDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().height(), Worksheet::Inch); + int countPixelX = ceil(widthDatarectInch*QApplication::desktop()->physicalDpiX()); + int countPixelY = ceil(heightDatarectInch*QApplication::desktop()->physicalDpiY()); + + double minLogicalDiffX = 1/(plot->dataRect().width()/countPixelX); + double minLogicalDiffY = 1/(plot->dataRect().height()/countPixelY); + QVector> scenePointsUsed; + // size of the datarect in pixels + scenePointsUsed.resize(countPixelX+1); + for (int i=0; i< countPixelX+1; i++) { + scenePointsUsed[i].resize(countPixelY+1); + + } + int columnProperties = xColumn->properties(); + int startIndex; + int endIndex; + if (columnProperties == AbstractColumn::Properties::MonotonicDecreasing || + columnProperties == AbstractColumn::Properties::MonotonicIncreasing) { + double xMin = cSystem->mapSceneToLogical(plot->dataRect().topLeft()).x(); + double xMax = cSystem->mapSceneToLogical(plot->dataRect().bottomRight()).x(); + startIndex= q->indexForXinPointsVector(xMin, symbolPointsLogical, static_cast(columnProperties)); + endIndex = q->indexForXinPointsVector(xMax, symbolPointsLogical, static_cast(columnProperties)); + + if (startIndex > endIndex) { + int tempIndex = startIndex; + startIndex = endIndex; + endIndex = tempIndex; + } + } else { + startIndex = 0; + endIndex = symbolPointsLogical.size(); + } + + + cSystem->mapLogicalToScene(startIndex, endIndex, symbolPointsLogical, symbolPointsScene, visiblePoints, scenePointsUsed, minLogicalDiffX, minLogicalDiffY); + } } + + m_suppressRecalc = true; updateLines(); updateDropLines(); @@ -915,6 +962,7 @@ updateErrorBars(); RESET_CURSOR; + } } /*! @@ -938,7 +986,7 @@ //take over only valid and non masked points. for (int row = 0; row < xColumn->rowCount(); row++) { if ( xColumn->isValid(row) && yColumn->isValid(row) - && (!xColumn->isMasked(row)) && (!yColumn->isMasked(row)) ) { + && (!xColumn->isMasked(row)) && (!yColumn->isMasked(row)) ) { switch (xColMode) { case AbstractColumn::Numeric: case AbstractColumn::Integer: @@ -979,6 +1027,83 @@ visiblePoints = std::vector(symbolPointsLogical.count(), false); } +/*! + * adds a line, which connects two points, but only if the don't lie on the same xAxis pixel. + * If they lie on the same x pixel, draw a vertical line between the minimum and maximum y value. So all points are included + * \param p0 first point + * \param p1 second point + * \param minY + * \param maxY + * \param overlap if at the previous call was an overlap between the previous two points + * \param minLogicalDiffX + * \param minLogicalDiffY + * \param pixelDiff x pixel distance between two points + */ +void XYCurvePrivate::addLine(QPointF p0, QPointF p1, double& minY, double& maxY, bool& overlap, double minLogicalDiffX, double minLogicalDiffY, int& pixelDiff) { + if (samePixel(p0.x() * minLogicalDiffX, p1.x() * minLogicalDiffX, pixelDiff)) { + if (overlap) { // second and so on time pixel are same + if (p0.y() > maxY) { + maxY = p0.y(); + } + if (p0.y() < minY) { + minY = p0.y(); + } + } else { // first time pixel are same + if (p0.y() < p1.y()) { + minY = p0.y(); + maxY = p1.y(); + } else { + maxY = p0.y(); + minY = p1.y(); + } + overlap = true; + } + } else { + if (overlap) { // when previously overlap was true, draw the previous line + overlap = false; + + // last point from previous pixel must be evaluated + if (p0.y() > maxY) { + maxY = p0.y(); + } + if (p0.y() < minY) { + minY = p0.y(); + } + + if (p1.x() >= plot->xMin() && p1.x() <= plot->xMax()) { // x inside scene + + if (minY == maxY) { + lines.append(QLineF(p0, p1)); // line from previous point to actual point + } else if (p0.y() == minY) { // draw vertical line + lines.append(QLineF(p0.x(),maxY, p0.x(), minY)); + if (p1.y() >= minY && p1.y() <= maxY && pixelDiff == 1) { + return; + } + lines.append(QLineF(p0,p1)); + } else if (p0.y() == maxY) { // draw vertical line + lines.append(QLineF(p0.x(),maxY, p0.x(), minY)); + if (p1.y() >= minY && p1.y() <= maxY && pixelDiff == 1) { + return; + } + // draw line, only if there is a pixelDiff = 1 otherwise no line needed, because when drawing a new vertical line, this line is already included + lines.append(QLineF(p0,p1)); + } else { // last point nor min nor max + lines.append(QLineF(p0.x(),maxY, p0.x(), minY)); + if (p1.y() >= minY && p1.y() <= maxY && pixelDiff == 1) { + return; + } + lines.append(QLineF(p0,p1)); + } + } else {// x in scene + DEBUG("addLine: not in scene"); + } + } else {// overlap + lines.append(QLineF(p0,p1)); + } + + } +} + /*! recalculates the painter path for the lines connecting the data points. Called each time when the type of this connection is changed. @@ -1003,6 +1128,14 @@ return; } + float widthDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().width(), Worksheet::Inch); + float heightDatarectInch = Worksheet::convertFromSceneUnits(plot->dataRect().height(), Worksheet::Inch); + int countPixelX = ceil(widthDatarectInch*QApplication::desktop()->physicalDpiX()); + int countPixelY = ceil(heightDatarectInch*QApplication::desktop()->physicalDpiY()); + + double minLogicalDiffX = 1/((plot->xMax()-plot->xMin())/countPixelX); + double minLogicalDiffY = 1/((plot->yMax()-plot->yMin())/countPixelY); + //calculate the lines connecting the data points { #ifdef PERFTRACE_CURVES @@ -1010,176 +1143,276 @@ #endif QPointF tempPoint1, tempPoint2; QPointF curPoint, nextPoint; - switch (lineType) { - case XYCurve::NoLine: - break; - case XYCurve::Line: - for (unsigned int i = 0; i < count - 1; i++) { - if (!lineSkipGaps && !connectedPointsLogical[i]) continue; - if (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) continue; - lines.append(QLineF(symbolPointsLogical.at(i), symbolPointsLogical.at(i+1))); - } - break; - case XYCurve::StartHorizontal: - for (unsigned int i = 0; i < count - 1; i++) { - if (!lineSkipGaps && !connectedPointsLogical[i]) continue; - if (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) continue; - curPoint = symbolPointsLogical.at(i); - nextPoint = symbolPointsLogical.at(i+1); - tempPoint1 = QPointF(nextPoint.x(), curPoint.y()); - lines.append(QLineF(curPoint, tempPoint1)); - lines.append(QLineF(tempPoint1, nextPoint)); + + int startIndex; + int endIndex; + bool overlap = false; + double maxY, minY; + int pixelDiff; + QPointF p0; + QPointF p1; + + // find index for xMin and xMax to not loop throug all values + int columnProperties = q->xColumn()->properties(); + if (columnProperties == AbstractColumn::Properties::MonotonicDecreasing || + columnProperties == AbstractColumn::Properties::MonotonicIncreasing) { + double xMin = cSystem->mapSceneToLogical(plot->dataRect().topLeft()).x(); + double xMax = cSystem->mapSceneToLogical(plot->dataRect().bottomRight()).x(); + startIndex= q->indexForX(xMin); + endIndex = q->indexForX(xMax); + + if (startIndex > endIndex) { + int tempIndex = startIndex; + startIndex = endIndex; + endIndex = tempIndex; } - break; - case XYCurve::StartVertical: - for (unsigned int i = 0; i < count - 1; i++) { - if (!lineSkipGaps && !connectedPointsLogical[i]) continue; - if (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) continue; - curPoint = symbolPointsLogical.at(i); - nextPoint = symbolPointsLogical.at(i+1); - tempPoint1 = QPointF(curPoint.x(), nextPoint.y()); - lines.append(QLineF(curPoint, tempPoint1)); - lines.append(QLineF(tempPoint1,nextPoint)); + + startIndex--; // use one value before + endIndex ++; + if (startIndex < 0) + startIndex = 0; + if(endIndex < 0 || endIndex >= count) + endIndex = count-1; + + count = endIndex - startIndex +1; + }else { + startIndex = 0; + endIndex = count-1; + } + + if (columnProperties == AbstractColumn::Properties::Constant) { + tempPoint1 = QPointF(plot->xMin(), plot->yMin()); + tempPoint2 = QPointF(plot->xMin(), plot->yMax()); + lines.append(QLineF(tempPoint1, tempPoint2)); + } else { + switch (lineType) { + case XYCurve::NoLine: + break; + case XYCurve::Line: { + //startIndex+=0; + //endIndex = startIndex+3; + //QPointF entryPoint = symbolPointsLogical[startIndex]; + for (unsigned int i = startIndex; i < endIndex; i++) { + if (!lineSkipGaps && !connectedPointsLogical[i]) + continue; + p0 = symbolPointsLogical[i]; + p1 = symbolPointsLogical[i+1]; + if (lineIncreasingXOnly && (p1.x() < p0.x())) + continue; + addLine(p0, p1, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } + // add last line + if (overlap) { + overlap = false; + addLine(p0,p1, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } + break; } - break; - case XYCurve::MidpointHorizontal: - for (unsigned int i = 0; i < count - 1; i++) { - if (!lineSkipGaps && !connectedPointsLogical[i]) continue; - if (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) continue; - curPoint = symbolPointsLogical.at(i); - nextPoint = symbolPointsLogical.at(i+1); - tempPoint1 = QPointF(curPoint.x() + (nextPoint.x()-curPoint.x())/2, curPoint.y()); - tempPoint2 = QPointF(curPoint.x() + (nextPoint.x()-curPoint.x())/2, nextPoint.y()); - lines.append(QLineF(curPoint, tempPoint1)); - lines.append(QLineF(tempPoint1, tempPoint2)); - lines.append(QLineF(tempPoint2, nextPoint)); + case XYCurve::StartHorizontal: { + for (unsigned int i = startIndex; i < endIndex; i++) { + if (!lineSkipGaps && !connectedPointsLogical[i]) continue; + p0 = symbolPointsLogical[i]; + p1 = symbolPointsLogical[i+1]; + if (lineIncreasingXOnly && (p1.x() < p0.x())) continue; + curPoint = p0; + nextPoint = p1; + tempPoint1 = QPointF(nextPoint.x(), curPoint.y()); + addLine(curPoint, tempPoint1, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + addLine(tempPoint1, nextPoint, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } + // add last line + if (overlap) { + overlap = false; + addLine(tempPoint1,nextPoint, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } + break; } - break; - case XYCurve::MidpointVertical: - for (unsigned int i = 0; i < count - 1; i++) { - if (!lineSkipGaps && !connectedPointsLogical[i]) continue; - if (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) continue; - curPoint = symbolPointsLogical.at(i); - nextPoint = symbolPointsLogical.at(i+1); - tempPoint1 = QPointF(curPoint.x(), curPoint.y() + (nextPoint.y()-curPoint.y())/2); - tempPoint2 = QPointF(nextPoint.x(), curPoint.y() + (nextPoint.y()-curPoint.y())/2); - lines.append(QLineF(curPoint, tempPoint1)); - lines.append(QLineF(tempPoint1, tempPoint2)); - lines.append(QLineF(tempPoint2, nextPoint)); + case XYCurve::StartVertical: { + for (unsigned int i = startIndex; i < endIndex; i++) { + if (!lineSkipGaps && !connectedPointsLogical[i]) continue; + if (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) continue; + curPoint = symbolPointsLogical.at(i); + nextPoint = symbolPointsLogical.at(i+1); + tempPoint1 = QPointF(curPoint.x(), nextPoint.y()); + addLine(curPoint, tempPoint1, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + addLine(tempPoint1, nextPoint, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } + // add last line + if (overlap) { + overlap = false; + addLine(tempPoint1,nextPoint, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } + break; } - break; - case XYCurve::Segments2: { - int skip = 0; - for (unsigned int i = 0; i < count - 1; i++) { - if (skip != 1) { - if ( (!lineSkipGaps && !connectedPointsLogical[i]) - || (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) ) { - skip = 0; - continue; - } - lines.append(QLineF(symbolPointsLogical.at(i), symbolPointsLogical.at(i+1))); - skip++; - } else - skip = 0; + case XYCurve::MidpointHorizontal: { + for (unsigned int i = startIndex; i < endIndex; i++) { + if (!lineSkipGaps && !connectedPointsLogical[i]) continue; + if (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) continue; + curPoint = symbolPointsLogical.at(i); + nextPoint = symbolPointsLogical.at(i+1); + tempPoint1 = QPointF(curPoint.x() + (nextPoint.x()-curPoint.x())/2, curPoint.y()); + tempPoint2 = QPointF(curPoint.x() + (nextPoint.x()-curPoint.x())/2, nextPoint.y()); + addLine(curPoint, tempPoint1, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + addLine(tempPoint1, tempPoint2, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + addLine(tempPoint2, nextPoint, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); } break; + // add last line + if (overlap) { + overlap = false; + addLine(tempPoint2,nextPoint, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } } - case XYCurve::Segments3: { - int skip = 0; - for (unsigned int i = 0; i < count - 1; i++) { - if (skip != 2) { - if ( (!lineSkipGaps && !connectedPointsLogical[i]) - || (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) ) { - skip = 0; - continue; - } - lines.append(QLineF(symbolPointsLogical.at(i), symbolPointsLogical.at(i+1))); - skip++; - } else - skip = 0; + case XYCurve::MidpointVertical: { + for (unsigned int i = startIndex; i < endIndex; i++) { + if (!lineSkipGaps && !connectedPointsLogical[i]) continue; + if (lineIncreasingXOnly && (symbolPointsLogical.at(i+1).x() < symbolPointsLogical.at(i).x())) continue; + curPoint = symbolPointsLogical.at(i); + nextPoint = symbolPointsLogical.at(i+1); + tempPoint1 = QPointF(curPoint.x(), curPoint.y() + (nextPoint.y()-curPoint.y())/2); + tempPoint2 = QPointF(nextPoint.x(), curPoint.y() + (nextPoint.y()-curPoint.y())/2); + addLine(curPoint, tempPoint1, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + addLine(tempPoint1, tempPoint2, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + addLine(tempPoint2, nextPoint, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); } break; + // add last line + if (overlap) { + overlap = false; + addLine(tempPoint2,nextPoint, minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } } - case XYCurve::SplineCubicNatural: - case XYCurve::SplineCubicPeriodic: - case XYCurve::SplineAkimaNatural: - case XYCurve::SplineAkimaPeriodic: { - gsl_interp_accel *acc = gsl_interp_accel_alloc(); - gsl_spline *spline = nullptr; - - double* x = new double[count]; - double* y = new double[count]; - for (unsigned int i = 0; i < count; i++) { - x[i] = symbolPointsLogical.at(i).x(); - y[i] = symbolPointsLogical.at(i).y(); + case XYCurve::Segments2: { + int skip = 0; + for (unsigned int i = startIndex; i < endIndex; i++) { + if (skip != 1) { + if ( (!lineSkipGaps && !connectedPointsLogical[i]) + || (lineIncreasingXOnly && (symbolPointsLogical[i+1].x() < symbolPointsLogical[i].x())) ) { + skip = 0; + continue; + } + addLine(symbolPointsLogical[i], symbolPointsLogical[i+1], minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + skip++; + } else + skip = 0; + } + // add last line + if (overlap) { + overlap = false; + addLine(symbolPointsLogical[endIndex-1],symbolPointsLogical[endIndex], minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } + break; + } + case XYCurve::Segments3: { + int skip = 0; + for (unsigned int i = startIndex; i < endIndex; i++) { + if (skip != 2) { + if ( (!lineSkipGaps && !connectedPointsLogical[i]) + || (lineIncreasingXOnly && (symbolPointsLogical[i+1].x() < symbolPointsLogical[i].x())) ) { + skip = 0; + continue; + } + addLine(symbolPointsLogical[i], symbolPointsLogical[i+1], minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + skip++; + } else + skip = 0; + } + // add last line + if (overlap) { + overlap = false; + addLine(symbolPointsLogical[endIndex-1],symbolPointsLogical[endIndex], minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } + break; } + case XYCurve::SplineCubicNatural: + case XYCurve::SplineCubicPeriodic: + case XYCurve::SplineAkimaNatural: + case XYCurve::SplineAkimaPeriodic: { + gsl_interp_accel *acc = gsl_interp_accel_alloc(); + gsl_spline *spline = nullptr; + + double* x = new double[count]; + double* y = new double[count]; + for (unsigned int i = 0; i < count; i++) { // TODO: interpolating only between the visible points? + x[i] = symbolPointsLogical[i+startIndex].x(); + y[i] = symbolPointsLogical[i+startIndex].y(); + } - gsl_set_error_handler_off(); - if (lineType == XYCurve::SplineCubicNatural) - spline = gsl_spline_alloc(gsl_interp_cspline, count); - else if (lineType == XYCurve::SplineCubicPeriodic) - spline = gsl_spline_alloc(gsl_interp_cspline_periodic, count); - else if (lineType == XYCurve::SplineAkimaNatural) - spline = gsl_spline_alloc(gsl_interp_akima, count); - else if (lineType == XYCurve::SplineAkimaPeriodic) - spline = gsl_spline_alloc(gsl_interp_akima_periodic, count); - - if (!spline) { - QString msg; - if ( (lineType == XYCurve::SplineAkimaNatural || lineType == XYCurve::SplineAkimaPeriodic) && count < 5) - msg = i18n("Error: Akima spline interpolation requires a minimum of 5 points."); - else - msg = i18n("Error: Could not initialize the spline function."); - emit q->info(msg); + gsl_set_error_handler_off(); + if (lineType == XYCurve::SplineCubicNatural) + spline = gsl_spline_alloc(gsl_interp_cspline, count); + else if (lineType == XYCurve::SplineCubicPeriodic) + spline = gsl_spline_alloc(gsl_interp_cspline_periodic, count); + else if (lineType == XYCurve::SplineAkimaNatural) + spline = gsl_spline_alloc(gsl_interp_akima, count); + else if (lineType == XYCurve::SplineAkimaPeriodic) + spline = gsl_spline_alloc(gsl_interp_akima_periodic, count); + + if (!spline) { + QString msg; + if ( (lineType == XYCurve::SplineAkimaNatural || lineType == XYCurve::SplineAkimaPeriodic) && count < 5) + msg = i18n("Error: Akima spline interpolation requires a minimum of 5 points."); + else + msg = i18n("Error: Could not initialize the spline function."); + emit q->info(msg); + + recalcShapeAndBoundingRect(); + delete[] x; + delete[] y; + gsl_interp_accel_free (acc); + return; + } - recalcShapeAndBoundingRect(); - delete[] x; - delete[] y; - gsl_interp_accel_free (acc); - return; - } + int status = gsl_spline_init (spline, x, y, count); + if (status) { + //TODO: check in gsl/interp.c when GSL_EINVAL is thrown + QString gslError; + if (status == GSL_EINVAL) + gslError = i18n("x values must be monotonically increasing."); + else + gslError = gslErrorToString(status); + emit q->info( i18n("Error: %1", gslError) ); + + recalcShapeAndBoundingRect(); + delete[] x; + delete[] y; + gsl_spline_free (spline); + gsl_interp_accel_free (acc); + return; + } - int status = gsl_spline_init (spline, x, y, count); - if (status) { - //TODO: check in gsl/interp.c when GSL_EINVAL is thrown - QString gslError; - if (status == GSL_EINVAL) - gslError = i18n("x values must be monotonically increasing."); - else - gslError = gslErrorToString(status); - emit q->info( i18n("Error: %1", gslError) ); + //create interpolating points + std::vector xinterp, yinterp; + for (unsigned int i = 0; i < count - 1; i++) { + const double x1 = x[i]; + const double x2 = x[i+1]; + double xi, yi; + const double step = fabs(x2 - x1)/(lineInterpolationPointsCount + 1); + + for (int i=0; i < (lineInterpolationPointsCount + 1); i++) { + xi = x1+i*step; + yi = gsl_spline_eval(spline, xi, acc); + xinterp.push_back(xi); + yinterp.push_back(yi); + } + } + + for (unsigned int i = 0; i < xinterp.size() - 1; i++) + addLine(QPointF(xinterp[i],yinterp[i]), QPointF(xinterp[i+1], yinterp[i+1]), minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + addLine(QPointF(xinterp[xinterp.size()-1], yinterp[yinterp.size()-1]), QPointF(x[count-1], y[count-1]), minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + + // add last line + if (overlap) { + overlap = false; + addLine(QPointF(xinterp[xinterp.size()-1], yinterp[yinterp.size()-1]), QPointF(x[count-1], y[count-1]), minY, maxY, overlap, minLogicalDiffX, minLogicalDiffY, pixelDiff); + } - recalcShapeAndBoundingRect(); delete[] x; delete[] y; gsl_spline_free (spline); gsl_interp_accel_free (acc); - return; - } - - //create interpolating points - std::vector xinterp, yinterp; - for (unsigned int i = 0; i < count - 1; i++) { - const double x1 = x[i]; - const double x2 = x[i+1]; - const double step = fabs(x2 - x1)/(lineInterpolationPointsCount + 1); - - for (double xi = x1; xi < x2; xi += step) { - const double yi = gsl_spline_eval(spline, xi, acc); - xinterp.push_back(xi); - yinterp.push_back(yi); - } + break; } - - for (unsigned int i = 0; i < xinterp.size() - 1; i++) - lines.append(QLineF(xinterp[i], yinterp[i], xinterp[i+1], yinterp[i+1])); - lines.append(QLineF(xinterp[xinterp.size()-1], yinterp[yinterp.size()-1], x[count-1], y[count-1])); - - delete[] x; - delete[] y; - gsl_spline_free (spline); - gsl_interp_accel_free (acc); - break; } } } @@ -1207,6 +1440,11 @@ recalcShapeAndBoundingRect(); } +bool XYCurvePrivate::samePixel(double value1, double value2, int &pixelDiff) { + pixelDiff = int(value1) - int(value2); + return pixelDiff == 0; +} + /*! recalculates the painter path for the drop lines. Called each time when the type of the drop lines is changed. @@ -1330,32 +1568,34 @@ switch (valuesType) { case XYCurve::NoValues: case XYCurve::ValuesX: { - for (int i = 0; i < symbolPointsLogical.size(); ++i) { + for (int i = 0; i < symbolPointsScene.size(); ++i) { if (!visiblePoints[i]) continue; - valuesStrings << valuesPrefix + QString::number(symbolPointsLogical.at(i).x()) + valuesSuffix; + valuesStrings << valuesPrefix + QString::number(cSystem->mapSceneToLogical(symbolPointsScene[i]).x()) + valuesSuffix; } break; } case XYCurve::ValuesY: { - for (int i = 0; i < symbolPointsLogical.size(); ++i) { + for (int i = 0; i < symbolPointsScene.size(); ++i) { if (!visiblePoints[i]) continue; - valuesStrings << valuesPrefix + QString::number(symbolPointsLogical.at(i).y()) + valuesSuffix; + valuesStrings << valuesPrefix + QString::number(cSystem->mapSceneToLogical(symbolPointsScene[i]).y()) + valuesSuffix; } break; } case XYCurve::ValuesXY: { - for (int i = 0; i < symbolPointsLogical.size(); ++i) { + for (int i = 0; i < symbolPointsScene.size(); ++i) { if (!visiblePoints[i]) continue; - valuesStrings << valuesPrefix + QString::number(symbolPointsLogical.at(i).x()) + ',' - + QString::number(symbolPointsLogical.at(i).y()) + valuesSuffix; + QPointF logicalValue = cSystem->mapSceneToLogical(symbolPointsScene[i]); + valuesStrings << valuesPrefix + QString::number(logicalValue.x()) + ',' + + QString::number(logicalValue.y()) + valuesSuffix; } break; } case XYCurve::ValuesXYBracketed: { - for (int i = 0; i < symbolPointsLogical.size(); ++i) { + for (int i = 0; i < symbolPointsScene.size(); ++i) { if (!visiblePoints[i]) continue; - valuesStrings << valuesPrefix + '(' + QString::number(symbolPointsLogical.at(i).x()) + ',' - + QString::number(symbolPointsLogical.at(i).y()) +')' + valuesSuffix; + QPointF logicalValue = cSystem->mapSceneToLogical(symbolPointsScene[i]); + valuesStrings << valuesPrefix + '(' + QString::number(logicalValue.x()) + ',' + + QString::number(logicalValue.y()) +')' + valuesSuffix; } break; } Index: src/backend/worksheet/plots/cartesian/XYCurvePrivate.h =================================================================== --- src/backend/worksheet/plots/cartesian/XYCurvePrivate.h +++ src/backend/worksheet/plots/cartesian/XYCurvePrivate.h @@ -34,6 +34,7 @@ class CartesianPlot; class CartesianCoordinateSystem; +class XYCurve; class XYCurvePrivate : public QGraphicsItem { public: @@ -46,6 +47,8 @@ void retransform(); void recalcLogicalPoints(); void updateLines(); + void addLine(QPointF p0, QPointF p1, double &minY, double &maxY, bool &overlap, double minLogicalDiffX, double minLogicalDiffY, int &pixelDiff); + bool samePixel(double value1, double value2, int &pixelDiff); void updateDropLines(); void updateSymbols(); void updateValues();