void Checker::finishActiveSegment (const DocumentModelCoords &modelCoords, const QPointF &posStartScreen, const QPointF &posEndScreen, double yFrom, double yTo, const Transformation &transformation, SideSegments &sideSegments) const { LOG4CPP_INFO_S ((*mainCat)) << "Checker::finishActiveSegment" << " posStartScreen=" << QPointFToString (posStartScreen).toLatin1().data() << " posEndScreen=" << QPointFToString (posEndScreen).toLatin1().data() << " yFrom=" << yFrom << " yTo=" << yTo; QGraphicsItem *item; if ((modelCoords.coordsType() == COORDS_TYPE_POLAR) && (yFrom == yTo)) { // Linear cartesian radius double radiusLinearCartesian = yFrom; if (modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) { radiusLinearCartesian = transformation.logToLinearRadius(yFrom, modelCoords.originRadius()); } else { radiusLinearCartesian -= modelCoords.originRadius(); } // Draw along an arc since this is a side of constant radius, and we have polar coordinates item = ellipseItem (transformation, radiusLinearCartesian, posStartScreen, posEndScreen); } else { // Draw straight line item = lineItem (posStartScreen, posEndScreen); } sideSegments.push_back (item); bindItemToScene (item); }
QPointF Transformation::cartesianFromCartesianOrPolar (const DocumentModelCoords &modelCoords, const QPointF &posGraphIn) { // Initialize assuming input coordinates are already cartesian QPointF posGraphCartesian = posGraphIn; if (modelCoords.coordsType() == COORDS_TYPE_POLAR) { // Input coordinates are polar so convert them double angleRadians = 0; // Initialized to prevent compiler warning switch (modelCoords.coordUnitsTheta()) { case COORD_UNITS_POLAR_THETA_DEGREES: case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES: case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS: case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS_NSEW: angleRadians = posGraphIn.x () * PI / 180.0; break; case COORD_UNITS_POLAR_THETA_GRADIANS: angleRadians = posGraphIn.x () * PI / 200.0; break; case COORD_UNITS_POLAR_THETA_RADIANS: angleRadians = posGraphIn.x (); break; case COORD_UNITS_POLAR_THETA_TURNS: angleRadians = posGraphIn.x () * 2.0 * PI; break; default: ENGAUGE_ASSERT (false); } double radius = posGraphIn.y (); posGraphCartesian.setX (radius * cos (angleRadians)); posGraphCartesian.setY (radius * sin (angleRadians)); } return posGraphCartesian; }
QPointF Transformation::cartesianOrPolarFromCartesian (const DocumentModelCoords &modelCoords, const QPointF &posGraphIn) { // Initialize assuming output coordinates are to be cartesian QPointF posGraphCartesianOrPolar = posGraphIn; if (modelCoords.coordsType() == COORDS_TYPE_POLAR) { // Output coordinates are to be polar so convert them double angleRadians = qAtan2 (posGraphIn.y (), posGraphIn.x ()); switch (modelCoords.coordUnitsTheta()) { case COORD_UNITS_POLAR_THETA_DEGREES: case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES: case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS: case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS_NSEW: posGraphCartesianOrPolar.setX (angleRadians * 180.0 / PI); break; case COORD_UNITS_POLAR_THETA_GRADIANS: posGraphCartesianOrPolar.setX (angleRadians * 200.0 / PI); break; case COORD_UNITS_POLAR_THETA_RADIANS: posGraphCartesianOrPolar.setX (angleRadians); break; case COORD_UNITS_POLAR_THETA_TURNS: posGraphCartesianOrPolar.setX (angleRadians / 2.0 / PI); break; default: ENGAUGE_ASSERT (false); } double radius = qSqrt (posGraphIn.x () * posGraphIn.x () + posGraphIn.y () * posGraphIn.y ()); posGraphCartesianOrPolar.setY (radius); } return posGraphCartesianOrPolar; }
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 FormatCoordsUnits::unformattedToFormatted (double xThetaUnformatted, double yRadiusUnformatted, const DocumentModelCoords &modelCoords, QString &xThetaFormatted, QString &yRadiusFormatted, const Transformation &transformation) const { LOG4CPP_DEBUG_S ((*mainCat)) << "FormatCoordsUnits::unformattedToFormatted"; FormatCoordsUnitsStrategyNonPolarTheta formatNonPolarTheta; FormatCoordsUnitsStrategyPolarTheta formatPolarTheta; if (modelCoords.coordsType() == COORDS_TYPE_CARTESIAN) { xThetaFormatted = formatNonPolarTheta.unformattedToFormatted (xThetaUnformatted, modelCoords.coordUnitsX(), modelCoords.coordUnitsDate(), modelCoords.coordUnitsTime(), IS_X_THETA, transformation, yRadiusUnformatted); yRadiusFormatted = formatNonPolarTheta.unformattedToFormatted (yRadiusUnformatted, modelCoords.coordUnitsY(), modelCoords.coordUnitsDate(), modelCoords.coordUnitsTime(), IS_NOT_X_THETA, transformation, xThetaUnformatted); } else { xThetaFormatted = formatPolarTheta.unformattedToFormatted (xThetaUnformatted, modelCoords.coordUnitsTheta(), transformation, yRadiusUnformatted); yRadiusFormatted = formatNonPolarTheta.unformattedToFormatted (yRadiusUnformatted, modelCoords.coordUnitsRadius(), modelCoords.coordUnitsDate(), modelCoords.coordUnitsTime(), IS_NOT_X_THETA, transformation, xThetaUnformatted); } }
void FormatCoordsUnits::formattedToUnformatted (const QString &xThetaFormatted, const QString &yRadiusFormatted, const DocumentModelCoords &modelCoords, double &xThetaUnformatted, double &yRadiusUnformatted) const { LOG4CPP_DEBUG_S ((*mainCat)) << "FormatCoordsUnits::formattedToUnformatted"; FormatCoordsUnitsStrategyNonPolarTheta formatNonPolarTheta; FormatCoordsUnitsStrategyPolarTheta formatPolarTheta; if (modelCoords.coordsType() == COORDS_TYPE_CARTESIAN) { xThetaUnformatted = formatNonPolarTheta.formattedToUnformatted (xThetaFormatted, modelCoords.coordUnitsX(), modelCoords.coordUnitsDate(), modelCoords.coordUnitsTime()); yRadiusUnformatted = formatNonPolarTheta.formattedToUnformatted (yRadiusFormatted, modelCoords.coordUnitsY(), modelCoords.coordUnitsDate(), modelCoords.coordUnitsTime()); } else { xThetaUnformatted = formatPolarTheta.formattedToUnformatted (xThetaFormatted, modelCoords.coordUnitsTheta()); yRadiusUnformatted = formatNonPolarTheta.formattedToUnformatted (yRadiusFormatted, modelCoords.coordUnitsRadius(), modelCoords.coordUnitsDate(), modelCoords.coordUnitsTime()); } }
void Checker::adjustPolarAngleRanges (const DocumentModelCoords &modelCoords, const Transformation &transformation, const QList<Point> &points, double &xMin, double &xMax, double &yMin) const { LOG4CPP_INFO_S ((*mainCat)) << "Checker::adjustPolarAngleRanges transformation=" << transformation; const double UNIT_LENGTH = 1.0; QString path; // For logging if (modelCoords.coordsType() == COORDS_TYPE_POLAR) { // Range minimum is at origin yMin = modelCoords.originRadius(); path = QString ("yMin=%1 ").arg (yMin); // For logging // Perform special processing to account for periodicity of polar coordinates. Start with unit vectors // in the directions of the three axis points double angle0 = points.at(0).posGraph().x(); double angle1 = points.at(1).posGraph().x(); double angle2 = points.at(2).posGraph().x(); QPointF pos0 = transformation.cartesianFromCartesianOrPolar(modelCoords, QPointF (angle0, UNIT_LENGTH)); QPointF pos1 = transformation.cartesianFromCartesianOrPolar(modelCoords, QPointF (angle1, UNIT_LENGTH)); QPointF pos2 = transformation.cartesianFromCartesianOrPolar(modelCoords, QPointF (angle2, UNIT_LENGTH)); // Identify the axis point that is more in the center of the other two axis points. The arc is then drawn // from one of the other two points to the other. Center point has smaller angles with the other points double sumAngle0 = angleBetweenVectors(pos0, pos1) + angleBetweenVectors(pos0, pos2); double sumAngle1 = angleBetweenVectors(pos1, pos0) + angleBetweenVectors(pos1, pos2); double sumAngle2 = angleBetweenVectors(pos2, pos0) + angleBetweenVectors(pos2, pos1); if ((sumAngle0 <= sumAngle1) && (sumAngle0 <= sumAngle2)) { // Point 0 is in the middle. Either or neither of points 1 and 2 may be along point 0 if ((angleFromVectorToVector (pos0, pos1) < 0) || (angleFromVectorToVector (pos0, pos2) > 0)) { path += QString ("from 1=%1 through 0 to 2=%2").arg (angle1).arg (angle2); xMin = angle1; xMax = angle2; } else { path += QString ("from 2=%1 through 0 to 1=%2").arg (angle2).arg (angle1); xMin = angle2; xMax = angle1; } } else if ((sumAngle1 <= sumAngle0) && (sumAngle1 <= sumAngle2)) { // Point 1 is in the middle. Either or neither of points 0 and 2 may be along point 1 if ((angleFromVectorToVector (pos1, pos0) < 0) || (angleFromVectorToVector (pos1, pos2) > 0)) { path += QString ("from 0=%1 through 1 to 2=%2").arg (angle0).arg (angle2); xMin = angle0; xMax = angle2; } else { path += QString ("from 2=%1 through 1 to 0=%2").arg (angle2).arg (angle0); xMin = angle2; xMax = angle0; } } else { // Point 2 is in the middle. Either or neither of points 0 and 1 may be along point 2 if ((angleFromVectorToVector (pos2, pos0) < 0) || (angleFromVectorToVector (pos2, pos1) > 0)) { path += QString ("from 0=%1 through 2 to 1=%2").arg (angle0).arg (angle1); xMin = angle0; xMax = angle1; } else { path += QString ("from 1=%1 through 2 to 0=%2").arg (angle1).arg (angle0); xMin = angle1; xMax = angle0; } } // Make sure theta is increasing while (xMax < xMin) { double thetaPeriod = modelCoords.thetaPeriod(); path += QString (" xMax+=%1").arg (thetaPeriod); xMax += thetaPeriod; } } LOG4CPP_INFO_S ((*mainCat)) << "Checker::adjustPolarAngleRanges path=(" << path.toLatin1().data() << ")"; }
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; } } } }