double ExportFileAbstractBase::linearlyInterpolateYRadiusFromTwoPoints (double xThetaValue, const DocumentModelCoords &modelCoords, const QPointF &posGraphBefore, const QPointF &posGraph) const { // X coordinate scaling is linear or log double s; if (modelCoords.coordScaleXTheta() == COORD_SCALE_LINEAR) { s = (xThetaValue - posGraphBefore.x()) / (posGraph.x() - posGraphBefore.x()); } else { s = (qLn (xThetaValue) - qLn (posGraphBefore.x())) / (qLn (posGraph.x()) - qLn (posGraphBefore.x())); } // Y coordinate scaling is linear or log double yRadius; if (modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR) { yRadius = (1.0 - s) * posGraphBefore.y() + s * posGraph.y(); } else { yRadius = qExp ((1.0 - s) * qLn (posGraphBefore.y()) + s * qLn (posGraph.y())); } return yRadius; }
void Checker::createSide (int pointRadius, const QList<Point> &points, const DocumentModelCoords &modelCoords, double xFrom, double yFrom, double xTo, double yTo, const Transformation &transformation, SideSegments &sideSegments) { LOG4CPP_INFO_S ((*mainCat)) << "Checker::createSide" << " pointRadius=" << pointRadius << " xFrom=" << xFrom << " yFrom=" << yFrom << " xTo=" << xTo << " yTo=" << yTo << " transformation=" << transformation; // Originally a complicated algorithm tried to intercept a straight line from (xFrom,yFrom) to (xTo,yTo). That did not work well since: // 1) Calculations for mostly orthogonal cartesian coordinates worked less well with non-orthogonal polar coordinates // 2) Ambiguity in polar coordinates between the shorter and longer paths between (theta0,radius) and (theta1,radius) // // Current algorithm breaks up the interval between (xMin,yMin) and (xMax,yMax) into many smaller pieces and stitches the // desired pieces together. For straight lines in linear graphs this algorithm is very much overkill, but there is no significant // penalty and this approach works in every situation // Should give single-pixel resolution on most images, and 'good enough' resolution on extremely large images const int NUM_STEPS = 1000; bool stateSegmentIsActive = false; QPointF posStartScreen (0, 0); // Loop through steps. Final step i=NUM_STEPS does final processing if a segment is active for (int i = 0; i <= NUM_STEPS; i++) { double s = (double) i / (double) NUM_STEPS; // Interpolate coordinates assuming normal linear scaling double xGraph = (1.0 - s) * xFrom + s * xTo; double yGraph = (1.0 - s) * yFrom + s * yTo; // Replace interpolated coordinates using log scaling if appropriate, preserving the same ranges if (modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) { xGraph = qExp ((1.0 - s) * qLn (xFrom) + s * qLn (xTo)); } if (modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) { yGraph = qExp ((1.0 - s) * qLn (yFrom) + s * qLn (yTo)); } QPointF pointScreen; transformation.transformRawGraphToScreen (QPointF (xGraph, yGraph), pointScreen); double distanceToNearestPoint = minScreenDistanceFromPoints (pointScreen, points); if ((distanceToNearestPoint < pointRadius) || (i == NUM_STEPS)) { // Too close to point, so point is not included in side. Or this is the final iteration of the loop if (stateSegmentIsActive) { // State transition finishActiveSegment (modelCoords, posStartScreen, pointScreen, yFrom, yTo, transformation, sideSegments); stateSegmentIsActive = false; } } else { // Outside point, so include point in side if (!stateSegmentIsActive) { // State transition stateSegmentIsActive = true; posStartScreen = pointScreen; } } } }