#include #include #include #include #include #include "GCS.h" namespace { std::vector solveLineDistance( double startX, double startY, double endX, double endY, double targetLength, bool vertical ) { GCS::Line line; line.p1 = GCS::Point(&startX, &startY); line.p2 = GCS::Point(&endX, &endY); GCS::System system; double fixedStartX = startX; double fixedStartY = startY; system.addConstraintCoordinateX(line.p1, &fixedStartX, 1, true); system.addConstraintCoordinateY(line.p1, &fixedStartY, 2, true); if (vertical) { system.addConstraintVertical(line, 3, true); } else { system.addConstraintHorizontal(line, 3, true); } system.addConstraintP2PDistance(line.p1, line.p2, &targetLength, 4, true); GCS::VEC_pD parameters {&startX, &startY, &endX, &endY}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = std::hypot(endX - startX, endY - startY) - targetLength; return {static_cast(status), startX, startY, endX, endY, residual}; } std::vector solveHorizontalDistance( double startX, double startY, double endX, double endY, double targetLength ) { return solveLineDistance(startX, startY, endX, endY, targetLength, false); } std::vector solveVerticalDistance( double startX, double startY, double endX, double endY, double targetLength ) { return solveLineDistance(startX, startY, endX, endY, targetLength, true); } std::vector solveDistanceX( double startX, double startY, double endX, double endY, double targetDistance ) { GCS::Line line; line.p1 = GCS::Point(&startX, &startY); line.p2 = GCS::Point(&endX, &endY); GCS::System system; double fixedStartX = startX; double fixedStartY = startY; system.addConstraintCoordinateX(line.p1, &fixedStartX, 1, true); system.addConstraintCoordinateY(line.p1, &fixedStartY, 2, true); system.addConstraintHorizontal(line, 3, true); system.addConstraintDifference(&startX, &endX, &targetDistance, 4, true); GCS::VEC_pD parameters {&startX, &startY, &endX, &endY}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = (endX - startX) - targetDistance; return {static_cast(status), startX, startY, endX, endY, residual}; } std::vector solveDistanceY( double startX, double startY, double endX, double endY, double targetDistance ) { GCS::Line line; line.p1 = GCS::Point(&startX, &startY); line.p2 = GCS::Point(&endX, &endY); GCS::System system; double fixedStartX = startX; double fixedStartY = startY; system.addConstraintCoordinateX(line.p1, &fixedStartX, 1, true); system.addConstraintCoordinateY(line.p1, &fixedStartY, 2, true); system.addConstraintVertical(line, 3, true); system.addConstraintDifference(&startY, &endY, &targetDistance, 4, true); GCS::VEC_pD parameters {&startX, &startY, &endX, &endY}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = (endY - startY) - targetDistance; return {static_cast(status), startX, startY, endX, endY, residual}; } std::vector solveAngle( double startX, double startY, double endX, double endY, double targetLength, double targetAngle ) { GCS::Line line; line.p1 = GCS::Point(&startX, &startY); line.p2 = GCS::Point(&endX, &endY); GCS::System system; double fixedStartX = startX; double fixedStartY = startY; system.addConstraintCoordinateX(line.p1, &fixedStartX, 1, true); system.addConstraintCoordinateY(line.p1, &fixedStartY, 2, true); system.addConstraintP2PDistance(line.p1, line.p2, &targetLength, 3, true); system.addConstraintP2PAngle(line.p1, line.p2, &targetAngle, 4, true); GCS::VEC_pD parameters {&startX, &startY, &endX, &endY}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double lengthResidual = std::hypot(endX - startX, endY - startY) - targetLength; const double angleResidual = std::atan2(endY - startY, endX - startX) - targetAngle; const double normalizedAngleResidual = std::atan2(std::sin(angleResidual), std::cos(angleResidual)); return {static_cast(status), startX, startY, endX, endY, lengthResidual, normalizedAngleResidual}; } std::vector solveCircleRadius( double centerX, double centerY, double radius, double targetRadius ) { GCS::Circle circle; circle.center = GCS::Point(¢erX, ¢erY); circle.rad = &radius; GCS::System system; double fixedCenterX = centerX; double fixedCenterY = centerY; system.addConstraintCoordinateX(circle.center, &fixedCenterX, 1, true); system.addConstraintCoordinateY(circle.center, &fixedCenterY, 2, true); system.addConstraintCircleRadius(circle, &targetRadius, 3, true); GCS::VEC_pD parameters {¢erX, ¢erY, &radius}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = radius - targetRadius; return {static_cast(status), centerX, centerY, radius, residual}; } std::vector solveCircleDiameter( double centerX, double centerY, double radius, double targetDiameter ) { GCS::Circle circle; circle.center = GCS::Point(¢erX, ¢erY); circle.rad = &radius; GCS::System system; double fixedCenterX = centerX; double fixedCenterY = centerY; system.addConstraintCoordinateX(circle.center, &fixedCenterX, 1, true); system.addConstraintCoordinateY(circle.center, &fixedCenterY, 2, true); system.addConstraintCircleDiameter(circle, &targetDiameter, 3, true); GCS::VEC_pD parameters {¢erX, ¢erY, &radius}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = radius * 2.0 - targetDiameter; return {static_cast(status), centerX, centerY, radius, residual}; } std::vector solveEqualLines( double firstStartX, double firstStartY, double firstEndX, double firstEndY, double secondStartX, double secondStartY, double secondEndX, double secondEndY ) { GCS::Line first; first.p1 = GCS::Point(&firstStartX, &firstStartY); first.p2 = GCS::Point(&firstEndX, &firstEndY); GCS::Line second; second.p1 = GCS::Point(&secondStartX, &secondStartY); second.p2 = GCS::Point(&secondEndX, &secondEndY); GCS::System system; double fixedFirstStartX = firstStartX; double fixedFirstStartY = firstStartY; double fixedFirstEndX = firstEndX; double fixedFirstEndY = firstEndY; double fixedSecondStartX = secondStartX; double fixedSecondStartY = secondStartY; system.addConstraintCoordinateX(first.p1, &fixedFirstStartX, 1, true); system.addConstraintCoordinateY(first.p1, &fixedFirstStartY, 2, true); system.addConstraintCoordinateX(first.p2, &fixedFirstEndX, 3, true); system.addConstraintCoordinateY(first.p2, &fixedFirstEndY, 4, true); system.addConstraintCoordinateX(second.p1, &fixedSecondStartX, 5, true); system.addConstraintCoordinateY(second.p1, &fixedSecondStartY, 6, true); system.addConstraintEqualLength(first, second, 7, true); GCS::VEC_pD parameters { &firstStartX, &firstStartY, &firstEndX, &firstEndY, &secondStartX, &secondStartY, &secondEndX, &secondEndY }; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double firstLength = std::hypot(firstEndX - firstStartX, firstEndY - firstStartY); const double secondLength = std::hypot(secondEndX - secondStartX, secondEndY - secondStartY); return { static_cast(status), firstStartX, firstStartY, firstEndX, firstEndY, secondStartX, secondStartY, secondEndX, secondEndY, secondLength - firstLength }; } std::vector solveEqualCircles( double firstCenterX, double firstCenterY, double firstRadius, double secondCenterX, double secondCenterY, double secondRadius ) { GCS::Circle first; first.center = GCS::Point(&firstCenterX, &firstCenterY); first.rad = &firstRadius; GCS::Circle second; second.center = GCS::Point(&secondCenterX, &secondCenterY); second.rad = &secondRadius; GCS::System system; double fixedFirstCenterX = firstCenterX; double fixedFirstCenterY = firstCenterY; double fixedSecondCenterX = secondCenterX; double fixedSecondCenterY = secondCenterY; double fixedFirstRadius = firstRadius; system.addConstraintCoordinateX(first.center, &fixedFirstCenterX, 1, true); system.addConstraintCoordinateY(first.center, &fixedFirstCenterY, 2, true); system.addConstraintCoordinateX(second.center, &fixedSecondCenterX, 3, true); system.addConstraintCoordinateY(second.center, &fixedSecondCenterY, 4, true); system.addConstraintEqual(first.rad, &fixedFirstRadius, 5, true); system.addConstraintEqualRadius(first, second, 6, true); GCS::VEC_pD parameters { &firstCenterX, &firstCenterY, &firstRadius, &secondCenterX, &secondCenterY, &secondRadius }; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = secondRadius - firstRadius; return { static_cast(status), firstCenterX, firstCenterY, firstRadius, secondCenterX, secondCenterY, secondRadius, residual }; } std::vector solveTangentCircles( double firstCenterX, double firstCenterY, double firstRadius, double secondCenterX, double secondCenterY, double secondRadius ) { GCS::Circle first; first.center = GCS::Point(&firstCenterX, &firstCenterY); first.rad = &firstRadius; GCS::Circle second; second.center = GCS::Point(&secondCenterX, &secondCenterY); second.rad = &secondRadius; GCS::System system; double fixedFirstCenterX = firstCenterX; double fixedFirstCenterY = firstCenterY; double fixedSecondCenterX = secondCenterX; double fixedSecondCenterY = secondCenterY; double fixedFirstRadius = firstRadius; system.addConstraintCoordinateX(first.center, &fixedFirstCenterX, 1, true); system.addConstraintCoordinateY(first.center, &fixedFirstCenterY, 2, true); system.addConstraintCoordinateX(second.center, &fixedSecondCenterX, 3, true); system.addConstraintCoordinateY(second.center, &fixedSecondCenterY, 4, true); system.addConstraintEqual(first.rad, &fixedFirstRadius, 5, true); system.addConstraintTangent(first, second, 6, true); GCS::VEC_pD parameters { &firstCenterX, &firstCenterY, &firstRadius, &secondCenterX, &secondCenterY, &secondRadius }; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double centerDistance = std::hypot(secondCenterX - firstCenterX, secondCenterY - firstCenterY); const double residual = centerDistance - firstRadius - secondRadius; return { static_cast(status), firstCenterX, firstCenterY, firstRadius, secondCenterX, secondCenterY, secondRadius, residual }; } std::vector solvePointSymmetry( double firstX, double firstY, double secondX, double secondY, double centerX, double centerY ) { GCS::Point first(&firstX, &firstY); GCS::Point second(&secondX, &secondY); GCS::Point center(¢erX, ¢erY); GCS::System system; double fixedFirstX = firstX; double fixedFirstY = firstY; double fixedCenterX = centerX; double fixedCenterY = centerY; system.addConstraintCoordinateX(first, &fixedFirstX, 1, true); system.addConstraintCoordinateY(first, &fixedFirstY, 2, true); system.addConstraintCoordinateX(center, &fixedCenterX, 3, true); system.addConstraintCoordinateY(center, &fixedCenterY, 4, true); system.addConstraintP2PSymmetric(first, second, center, 5, true); GCS::VEC_pD parameters {&firstX, &firstY, &secondX, &secondY, ¢erX, ¢erY}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = std::hypot( (firstX + secondX) * 0.5 - centerX, (firstY + secondY) * 0.5 - centerY ); return { static_cast(status), firstX, firstY, secondX, secondY, centerX, centerY, residual }; } std::vector solvePointOnLine( double pointX, double pointY, double startX, double startY, double endX, double endY, bool vertical ) { GCS::Point point(&pointX, &pointY); GCS::Line line; line.p1 = GCS::Point(&startX, &startY); line.p2 = GCS::Point(&endX, &endY); GCS::System system; double fixedStartX = startX; double fixedStartY = startY; double fixedEndX = endX; double fixedEndY = endY; system.addConstraintCoordinateX(line.p1, &fixedStartX, 1, true); system.addConstraintCoordinateY(line.p1, &fixedStartY, 2, true); system.addConstraintCoordinateX(line.p2, &fixedEndX, 3, true); system.addConstraintCoordinateY(line.p2, &fixedEndY, 4, true); if (vertical) { system.addConstraintVertical(line, 5, true); system.addConstraintCoordinateY(point, &pointY, 6, true); } else { system.addConstraintHorizontal(line, 5, true); system.addConstraintCoordinateX(point, &pointX, 6, true); } system.addConstraintPointOnLine(point, line, 7, true); GCS::VEC_pD parameters {&pointX, &pointY, &startX, &startY, &endX, &endY}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double dx = endX - startX; const double dy = endY - startY; const double length = std::hypot(dx, dy); const double residual = length == 0.0 ? 1.0 : std::abs((pointX - startX) * dy - (pointY - startY) * dx) / length; return {static_cast(status), pointX, pointY, startX, startY, endX, endY, residual}; } std::vector solvePointOnCircle( double pointX, double pointY, double centerX, double centerY, double radius ) { GCS::Point point(&pointX, &pointY); GCS::Circle circle; circle.center = GCS::Point(¢erX, ¢erY); circle.rad = &radius; GCS::System system; double fixedCenterX = centerX; double fixedCenterY = centerY; double fixedRadius = radius; system.addConstraintCoordinateX(circle.center, &fixedCenterX, 1, true); system.addConstraintCoordinateY(circle.center, &fixedCenterY, 2, true); system.addConstraintEqual(circle.rad, &fixedRadius, 3, true); double fixedPointX = pointX; system.addConstraintCoordinateX(point, &fixedPointX, 4, true); system.addConstraintPointOnCircle(point, circle, 5, true); GCS::VEC_pD parameters {&pointX, &pointY, ¢erX, ¢erY, &radius}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = std::hypot(pointX - centerX, pointY - centerY) - radius; return {static_cast(status), pointX, pointY, centerX, centerY, radius, residual}; } std::vector solvePointOnArc( double pointX, double pointY, double centerX, double centerY, double radius, double startAngle, double endAngle ) { if (!(radius > 0.0)) throw std::invalid_argument("Arc radius must be positive."); GCS::Point point(&pointX, &pointY); GCS::Arc arc; arc.center = GCS::Point(¢erX, ¢erY); arc.rad = &radius; arc.startAngle = &startAngle; arc.endAngle = &endAngle; GCS::System system; double fixedCenterX = centerX; double fixedCenterY = centerY; double fixedRadius = radius; double fixedPointX = pointX; system.addConstraintCoordinateX(arc.center, &fixedCenterX, 1, true); system.addConstraintCoordinateY(arc.center, &fixedCenterY, 2, true); system.addConstraintEqual(arc.rad, &fixedRadius, 3, true); system.addConstraintCoordinateX(point, &fixedPointX, 4, true); system.addConstraintPointOnArc(point, arc, 5, true); GCS::VEC_pD parameters {&pointX, &pointY, ¢erX, ¢erY, &radius}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = std::hypot(pointX - centerX, pointY - centerY) - radius; return {static_cast(status), pointX, pointY, centerX, centerY, radius, startAngle, endAngle, residual}; } std::vector solvePointOnEllipse( double pointX, double pointY, double centerX, double centerY, double focusX, double focusY, double minorRadius ) { if (!(minorRadius > 0.0)) throw std::invalid_argument("Ellipse minor radius must be positive."); const double focusDistance = std::hypot(focusX - centerX, focusY - centerY); const double majorRadius = std::hypot(focusDistance, minorRadius); if (!(majorRadius > 0.0) || focusDistance >= majorRadius) throw std::invalid_argument("Ellipse focus and radius are invalid."); GCS::Point point(&pointX, &pointY); GCS::Ellipse ellipse; ellipse.center = GCS::Point(¢erX, ¢erY); ellipse.focus1 = GCS::Point(&focusX, &focusY); ellipse.radmin = &minorRadius; GCS::System system; double fixedCenterX = centerX; double fixedCenterY = centerY; double fixedFocusX = focusX; double fixedFocusY = focusY; double fixedMinorRadius = minorRadius; double fixedPointX = pointX; system.addConstraintCoordinateX(ellipse.center, &fixedCenterX, 1, true); system.addConstraintCoordinateY(ellipse.center, &fixedCenterY, 2, true); system.addConstraintCoordinateX(ellipse.focus1, &fixedFocusX, 3, true); system.addConstraintCoordinateY(ellipse.focus1, &fixedFocusY, 4, true); system.addConstraintEqual(ellipse.radmin, &fixedMinorRadius, 5, true); system.addConstraintCoordinateX(point, &fixedPointX, 6, true); system.addConstraintPointOnEllipse(point, ellipse, 7, true); GCS::VEC_pD parameters {&pointX, &pointY, ¢erX, ¢erY, &focusX, &focusY, &minorRadius}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double focus2X = centerX - (focusX - centerX); const double focus2Y = centerY - (focusY - centerY); const double residual = std::hypot(pointX - focusX, pointY - focusY) + std::hypot(pointX - focus2X, pointY - focus2Y) - 2.0 * majorRadius; return {static_cast(status), pointX, pointY, centerX, centerY, focusX, focusY, minorRadius, residual}; } std::vector solveEllipseInternalAlignment( double centerX, double centerY, double majorRadius, double minorRadius, double rotation, int alignmentType ) { if (!std::isfinite(centerX) || !std::isfinite(centerY) || !std::isfinite(rotation) || !(majorRadius > minorRadius) || !(minorRadius > 0.0)) { throw std::invalid_argument("InternalAlignment requires a finite non-circular ellipse."); } if (alignmentType < 1 || alignmentType > 4) { throw std::invalid_argument("Ellipse InternalAlignment type must be in the range 1..4."); } const double focalDistance = std::sqrt(majorRadius * majorRadius - minorRadius * minorRadius); const double majorX = std::cos(rotation); const double majorY = std::sin(rotation); const double minorX = -majorY; const double minorY = majorX; double focusX = centerX + focalDistance * majorX; double focusY = centerY + focalDistance * majorY; double helperStartX; double helperStartY; double helperEndX; double helperEndY; if (alignmentType == 1) { helperStartX = centerX + majorRadius * majorX; helperStartY = centerY + majorRadius * majorY; helperEndX = centerX - majorRadius * majorX; helperEndY = centerY - majorRadius * majorY; } else if (alignmentType == 2) { helperStartX = centerX + minorRadius * minorX; helperStartY = centerY + minorRadius * minorY; helperEndX = centerX - minorRadius * minorX; helperEndY = centerY - minorRadius * minorY; } else { const double direction = alignmentType == 3 ? 1.0 : -1.0; helperStartX = centerX + direction * focalDistance * majorX; helperStartY = centerY + direction * focalDistance * majorY; helperEndX = helperStartX; helperEndY = helperStartY; } GCS::Ellipse ellipse; ellipse.center = GCS::Point(¢erX, ¢erY); ellipse.focus1 = GCS::Point(&focusX, &focusY); ellipse.radmin = &minorRadius; GCS::Point helperStart(&helperStartX, &helperStartY); GCS::Point helperEnd(&helperEndX, &helperEndY); GCS::System system; double fixedCenterX = centerX; double fixedCenterY = centerY; double fixedFocusX = focusX; double fixedFocusY = focusY; double fixedMinorRadius = minorRadius; system.addConstraintCoordinateX(ellipse.center, &fixedCenterX, 1, true); system.addConstraintCoordinateY(ellipse.center, &fixedCenterY, 2, true); system.addConstraintCoordinateX(ellipse.focus1, &fixedFocusX, 3, true); system.addConstraintCoordinateY(ellipse.focus1, &fixedFocusY, 4, true); system.addConstraintEqual(ellipse.radmin, &fixedMinorRadius, 5, true); if (alignmentType == 1) system.addConstraintInternalAlignmentEllipseMajorDiameter(ellipse, helperStart, helperEnd, 6, true); else if (alignmentType == 2) system.addConstraintInternalAlignmentEllipseMinorDiameter(ellipse, helperStart, helperEnd, 6, true); else if (alignmentType == 3) system.addConstraintInternalAlignmentEllipseFocus1(ellipse, helperStart, 6, true); else system.addConstraintInternalAlignmentEllipseFocus2(ellipse, helperStart, 6, true); GCS::VEC_pD parameters { ¢erX, ¢erY, &focusX, &focusY, &minorRadius, &helperStartX, &helperStartY }; if (alignmentType <= 2) { parameters.push_back(&helperEndX); parameters.push_back(&helperEndY); } const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = system.calculateConstraintErrorByTag(6); return { static_cast(status), centerX, centerY, focusX, focusY, minorRadius, helperStartX, helperStartY, helperEndX, helperEndY, residual }; } std::vector solveEllipseInternalAlignmentSet( double centerX, double centerY, double majorRadius, double minorRadius, double rotation ) { if (!std::isfinite(centerX) || !std::isfinite(centerY) || !std::isfinite(rotation) || !(majorRadius > minorRadius) || !(minorRadius > 0.0)) { throw std::invalid_argument("InternalAlignment requires a finite non-circular ellipse."); } const double focalDistance = std::sqrt(majorRadius * majorRadius - minorRadius * minorRadius); const double majorX = std::cos(rotation); const double majorY = std::sin(rotation); const double minorX = -majorY; const double minorY = majorX; double focusX = centerX + focalDistance * majorX; double focusY = centerY + focalDistance * majorY; double majorStartX = centerX + majorRadius * majorX; double majorStartY = centerY + majorRadius * majorY; double majorEndX = centerX - majorRadius * majorX; double majorEndY = centerY - majorRadius * majorY; double minorStartX = centerX + minorRadius * minorX; double minorStartY = centerY + minorRadius * minorY; double minorEndX = centerX - minorRadius * minorX; double minorEndY = centerY - minorRadius * minorY; double focus1X = focusX; double focus1Y = focusY; double focus2X = centerX - focalDistance * majorX; double focus2Y = centerY - focalDistance * majorY; GCS::Ellipse ellipse; ellipse.center = GCS::Point(¢erX, ¢erY); ellipse.focus1 = GCS::Point(&focusX, &focusY); ellipse.radmin = &minorRadius; GCS::Point majorStart(&majorStartX, &majorStartY); GCS::Point majorEnd(&majorEndX, &majorEndY); GCS::Point minorStart(&minorStartX, &minorStartY); GCS::Point minorEnd(&minorEndX, &minorEndY); GCS::Point focus1(&focus1X, &focus1Y); GCS::Point focus2(&focus2X, &focus2Y); GCS::System system; double fixedCenterX = centerX; double fixedCenterY = centerY; double fixedFocusX = focusX; double fixedFocusY = focusY; double fixedMinorRadius = minorRadius; system.addConstraintCoordinateX(ellipse.center, &fixedCenterX, 1, true); system.addConstraintCoordinateY(ellipse.center, &fixedCenterY, 2, true); system.addConstraintCoordinateX(ellipse.focus1, &fixedFocusX, 3, true); system.addConstraintCoordinateY(ellipse.focus1, &fixedFocusY, 4, true); system.addConstraintEqual(ellipse.radmin, &fixedMinorRadius, 5, true); system.addConstraintInternalAlignmentEllipseMajorDiameter(ellipse, majorStart, majorEnd, 6, true); system.addConstraintInternalAlignmentEllipseMinorDiameter(ellipse, minorStart, minorEnd, 7, true); system.addConstraintInternalAlignmentEllipseFocus1(ellipse, focus1, 8, true); system.addConstraintInternalAlignmentEllipseFocus2(ellipse, focus2, 9, true); GCS::VEC_pD parameters { ¢erX, ¢erY, &focusX, &focusY, &minorRadius, &majorStartX, &majorStartY, &majorEndX, &majorEndY, &minorStartX, &minorStartY, &minorEndX, &minorEndY, &focus1X, &focus1Y, &focus2X, &focus2Y }; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double majorResidual = system.calculateConstraintErrorByTag(6); const double minorResidual = system.calculateConstraintErrorByTag(7); const double focus1Residual = system.calculateConstraintErrorByTag(8); const double focus2Residual = system.calculateConstraintErrorByTag(9); return { static_cast(status), centerX, centerY, focusX, focusY, minorRadius, majorResidual, minorResidual, focus1Residual, focus2Residual }; } std::vector solvePointOnCubicBspline( double pointX, double pointY, double pole0X, double pole0Y, double pole1X, double pole1Y, double pole2X, double pole2Y, double pole3X, double pole3Y, double pointParameter ) { std::vector poleX {pole0X, pole1X, pole2X, pole3X}; std::vector poleY {pole0Y, pole1Y, pole2Y, pole3Y}; std::vector poles; poles.reserve(4); for (std::size_t index = 0; index < poleX.size(); ++index) { poles.emplace_back(&poleX[index], &poleY[index]); } std::vector weights(4, 1.0); std::vector weightPointers; weightPointers.reserve(4); for (double& weight : weights) weightPointers.push_back(&weight); std::vector knots {0.0, 1.0}; std::vector knotPointers {&knots[0], &knots[1]}; GCS::BSpline bspline; bspline.start = poles.front(); bspline.end = poles.back(); bspline.poles = poles; bspline.weights = weightPointers; bspline.knots = knotPointers; bspline.mult = {4, 4}; bspline.degree = 3; bspline.periodic = false; bspline.setupFlattenedKnots(); GCS::Point point(&pointX, &pointY); GCS::System system; double fixedPointX = pointX; system.addConstraintCoordinateX(point, &fixedPointX, 1, true); system.addConstraintPointOnBSpline(point, bspline, &pointParameter, 2, true); GCS::VEC_pD parameters {&pointX, &pointY, &pointParameter}; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const GCS::DeriVector2 curvePoint = bspline.Value(pointParameter, 1.0); const double residualX = pointX - curvePoint.x; const double residualY = pointY - curvePoint.y; return {static_cast(status), pointX, pointY, pointParameter, residualX, residualY}; } std::vector solveCubicBsplineWeight( double pole0X, double pole0Y, double pole1X, double pole1Y, double pole2X, double pole2Y, double pole3X, double pole3Y, double weight0, double weight1, double weight2, double weight3, int controlPointIndex, double targetWeight ) { if (controlPointIndex < 0 || controlPointIndex > 3) { throw std::invalid_argument("B-spline Weight control-point index must be in the range 0..3."); } if (!(weight0 > 0.0) || !(weight1 > 0.0) || !(weight2 > 0.0) || !(weight3 > 0.0) || !(targetWeight > 0.0) || !std::isfinite(targetWeight)) { throw std::invalid_argument("B-spline weights must be positive and finite."); } std::vector poleX {pole0X, pole1X, pole2X, pole3X}; std::vector poleY {pole0Y, pole1Y, pole2Y, pole3Y}; std::vector poles; poles.reserve(4); for (std::size_t index = 0; index < poleX.size(); ++index) { poles.emplace_back(&poleX[index], &poleY[index]); } std::vector weights {weight0, weight1, weight2, weight3}; std::vector weightPointers; weightPointers.reserve(4); for (double& weight : weights) weightPointers.push_back(&weight); std::vector knots {0.0, 1.0}; std::vector knotPointers {&knots[0], &knots[1]}; GCS::BSpline bspline; bspline.start = poles.front(); bspline.end = poles.back(); bspline.poles = poles; bspline.weights = weightPointers; bspline.knots = knotPointers; bspline.mult = {4, 4}; bspline.degree = 3; bspline.periodic = false; bspline.setupFlattenedKnots(); double helperCenterX = poleX[controlPointIndex]; double helperCenterY = poleY[controlPointIndex]; double helperRadius = weights[controlPointIndex]; GCS::Circle helper; helper.center = GCS::Point(&helperCenterX, &helperCenterY); helper.rad = &helperRadius; GCS::System system; system.addConstraintInternalAlignmentBSplineControlPoint(bspline, helper, controlPointIndex, 1, true); system.addConstraintCircleRadius(helper, &targetWeight, 2, true); GCS::VEC_pD parameters { &weights[controlPointIndex], &helperCenterX, &helperCenterY, &helperRadius }; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double alignmentResidual = system.calculateConstraintErrorByTag(1); const double weightResidual = system.calculateConstraintErrorByTag(2); return { static_cast(status), weights[0], weights[1], weights[2], weights[3], helperCenterX, helperCenterY, helperRadius, alignmentResidual, weightResidual }; } std::vector solveLineRelation( double firstStartX, double firstStartY, double firstEndX, double firstEndY, double secondStartX, double secondStartY, double secondEndX, double secondEndY, double secondLength, bool perpendicular ) { GCS::Line first; first.p1 = GCS::Point(&firstStartX, &firstStartY); first.p2 = GCS::Point(&firstEndX, &firstEndY); GCS::Line second; second.p1 = GCS::Point(&secondStartX, &secondStartY); second.p2 = GCS::Point(&secondEndX, &secondEndY); GCS::System system; double fixedFirstStartX = firstStartX; double fixedFirstStartY = firstStartY; double fixedFirstEndX = firstEndX; double fixedFirstEndY = firstEndY; double fixedSecondStartX = secondStartX; double fixedSecondStartY = secondStartY; system.addConstraintCoordinateX(first.p1, &fixedFirstStartX, 1, true); system.addConstraintCoordinateY(first.p1, &fixedFirstStartY, 2, true); system.addConstraintCoordinateX(first.p2, &fixedFirstEndX, 3, true); system.addConstraintCoordinateY(first.p2, &fixedFirstEndY, 4, true); system.addConstraintCoordinateX(second.p1, &fixedSecondStartX, 5, true); system.addConstraintCoordinateY(second.p1, &fixedSecondStartY, 6, true); if (perpendicular) { system.addConstraintPerpendicular(first, second, 7, true); } else { system.addConstraintParallel(first, second, 7, true); } system.addConstraintP2PDistance(second.p1, second.p2, &secondLength, 8, true); GCS::VEC_pD parameters { &firstStartX, &firstStartY, &firstEndX, &firstEndY, &secondStartX, &secondStartY, &secondEndX, &secondEndY }; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double firstDx = firstEndX - firstStartX; const double firstDy = firstEndY - firstStartY; const double secondDx = secondEndX - secondStartX; const double secondDy = secondEndY - secondStartY; const double scale = std::hypot(firstDx, firstDy) * std::hypot(secondDx, secondDy); const double residual = scale == 0.0 ? 1.0 : (perpendicular ? (firstDx * secondDx + firstDy * secondDy) / scale : (firstDx * secondDy - firstDy * secondDx) / scale); return { static_cast(status), firstStartX, firstStartY, firstEndX, firstEndY, secondStartX, secondStartY, secondEndX, secondEndY, residual }; } std::vector solveParallelLines( double firstStartX, double firstStartY, double firstEndX, double firstEndY, double secondStartX, double secondStartY, double secondEndX, double secondEndY, double secondLength ) { return solveLineRelation( firstStartX, firstStartY, firstEndX, firstEndY, secondStartX, secondStartY, secondEndX, secondEndY, secondLength, false ); } std::vector solvePerpendicularLines( double firstStartX, double firstStartY, double firstEndX, double firstEndY, double secondStartX, double secondStartY, double secondEndX, double secondEndY, double secondLength ) { return solveLineRelation( firstStartX, firstStartY, firstEndX, firstEndY, secondStartX, secondStartY, secondEndX, secondEndY, secondLength, true ); } std::vector solveCoincidentLinePoints( double firstStartX, double firstStartY, double firstEndX, double firstEndY, double secondStartX, double secondStartY, double secondEndX, double secondEndY, double secondLength, bool firstEnd, bool secondEnd ) { GCS::Line first; first.p1 = GCS::Point(&firstStartX, &firstStartY); first.p2 = GCS::Point(&firstEndX, &firstEndY); GCS::Line second; second.p1 = GCS::Point(&secondStartX, &secondStartY); second.p2 = GCS::Point(&secondEndX, &secondEndY); GCS::System system; double fixedFirstStartX = firstStartX; double fixedFirstStartY = firstStartY; double fixedFirstEndX = firstEndX; double fixedFirstEndY = firstEndY; system.addConstraintCoordinateX(first.p1, &fixedFirstStartX, 1, true); system.addConstraintCoordinateY(first.p1, &fixedFirstStartY, 2, true); system.addConstraintCoordinateX(first.p2, &fixedFirstEndX, 3, true); system.addConstraintCoordinateY(first.p2, &fixedFirstEndY, 4, true); GCS::Point& firstPoint = firstEnd ? first.p2 : first.p1; GCS::Point& secondPoint = secondEnd ? second.p2 : second.p1; system.addConstraintP2PCoincident(firstPoint, secondPoint, 5, true); system.addConstraintP2PDistance(second.p1, second.p2, &secondLength, 6, true); GCS::VEC_pD parameters { &firstStartX, &firstStartY, &firstEndX, &firstEndY, &secondStartX, &secondStartY, &secondEndX, &secondEndY }; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double selectedFirstX = firstEnd ? firstEndX : firstStartX; const double selectedFirstY = firstEnd ? firstEndY : firstStartY; const double selectedSecondX = secondEnd ? secondEndX : secondStartX; const double selectedSecondY = secondEnd ? secondEndY : secondStartY; const double residual = std::hypot(selectedFirstX - selectedSecondX, selectedFirstY - selectedSecondY); return { static_cast(status), firstStartX, firstStartY, firstEndX, firstEndY, secondStartX, secondStartY, secondEndX, secondEndY, residual }; } std::vector solveCoincidentLines( double firstStartX, double firstStartY, double firstEndX, double firstEndY, double secondStartX, double secondStartY, double secondEndX, double secondEndY, double secondLength ) { return solveCoincidentLinePoints( firstStartX, firstStartY, firstEndX, firstEndY, secondStartX, secondStartY, secondEndX, secondEndY, secondLength, true, false ); } std::vector solveSnellsLawLines( double firstStartX, double firstStartY, double firstEndX, double firstEndY, double secondStartX, double secondStartY, double secondEndX, double secondEndY, double boundaryStartX, double boundaryStartY, double boundaryEndX, double boundaryEndY, double refractiveRatio, bool firstEnd, bool secondEnd ) { const double firstLength = std::hypot(firstEndX - firstStartX, firstEndY - firstStartY); double secondLength = std::hypot(secondEndX - secondStartX, secondEndY - secondStartY); const double boundaryLength = std::hypot(boundaryEndX - boundaryStartX, boundaryEndY - boundaryStartY); if (!(firstLength > 0.0) || !(secondLength > 0.0) || !(boundaryLength > 0.0)) { throw std::invalid_argument("SnellsLaw requires three non-degenerate lines."); } if (!std::isfinite(refractiveRatio) || !(refractiveRatio > 0.0)) { throw std::invalid_argument("SnellsLaw refractive ratio must be positive and finite."); } GCS::Line first; first.p1 = GCS::Point(&firstStartX, &firstStartY); first.p2 = GCS::Point(&firstEndX, &firstEndY); GCS::Line second; second.p1 = GCS::Point(&secondStartX, &secondStartY); second.p2 = GCS::Point(&secondEndX, &secondEndY); GCS::Line boundary; boundary.p1 = GCS::Point(&boundaryStartX, &boundaryStartY); boundary.p2 = GCS::Point(&boundaryEndX, &boundaryEndY); GCS::Point& firstPoint = firstEnd ? first.p2 : first.p1; GCS::Point& secondPoint = secondEnd ? second.p2 : second.p1; const double junctionDistance = std::hypot(*firstPoint.x - *secondPoint.x, *firstPoint.y - *secondPoint.y); const double boundaryDistance = std::abs( (*firstPoint.x - boundaryStartX) * (boundaryEndY - boundaryStartY) - (*firstPoint.y - boundaryStartY) * (boundaryEndX - boundaryStartX) ) / boundaryLength; if (junctionDistance > 1e-7 || boundaryDistance > 1e-7) { throw std::invalid_argument("SnellsLaw ray endpoints must coincide on the boundary."); } double n1 = refractiveRatio; double n2 = 1.0; if (std::abs(refractiveRatio) >= 1.0) { n1 = 1.0; n2 = refractiveRatio; } else { n1 = 1.0 / refractiveRatio; n2 = 1.0; } GCS::System system; double fixedFirstStartX = firstStartX; double fixedFirstStartY = firstStartY; double fixedFirstEndX = firstEndX; double fixedFirstEndY = firstEndY; double fixedBoundaryStartX = boundaryStartX; double fixedBoundaryStartY = boundaryStartY; double fixedBoundaryEndX = boundaryEndX; double fixedBoundaryEndY = boundaryEndY; system.addConstraintCoordinateX(first.p1, &fixedFirstStartX, 1, true); system.addConstraintCoordinateY(first.p1, &fixedFirstStartY, 2, true); system.addConstraintCoordinateX(first.p2, &fixedFirstEndX, 3, true); system.addConstraintCoordinateY(first.p2, &fixedFirstEndY, 4, true); system.addConstraintCoordinateX(boundary.p1, &fixedBoundaryStartX, 5, true); system.addConstraintCoordinateY(boundary.p1, &fixedBoundaryStartY, 6, true); system.addConstraintCoordinateX(boundary.p2, &fixedBoundaryEndX, 7, true); system.addConstraintCoordinateY(boundary.p2, &fixedBoundaryEndY, 8, true); system.addConstraintP2PCoincident(firstPoint, secondPoint, 9, true); system.addConstraintP2PDistance(second.p1, second.p2, &secondLength, 10, true); system.addConstraintSnellsLaw( first, second, boundary, firstPoint, &n1, &n2, !firstEnd, secondEnd, 11, true ); GCS::VEC_pD parameters { &firstStartX, &firstStartY, &firstEndX, &firstEndY, &secondStartX, &secondStartY, &secondEndX, &secondEndY, &boundaryStartX, &boundaryStartY, &boundaryEndX, &boundaryEndY }; const int status = system.solve(parameters, true, GCS::DogLeg, false); if (status <= GCS::Converged) { system.applySolution(); } const double residual = system.calculateConstraintErrorByTag(11); return { static_cast(status), firstStartX, firstStartY, firstEndX, firstEndY, secondStartX, secondStartY, secondEndX, secondEndY, boundaryStartX, boundaryStartY, boundaryEndX, boundaryEndY, residual }; } } // namespace EMSCRIPTEN_BINDINGS(web_freecad_planegcs) { emscripten::register_vector("DoubleVector"); emscripten::function("solveHorizontalDistance", &solveHorizontalDistance); emscripten::function("solveVerticalDistance", &solveVerticalDistance); emscripten::function("solveDistanceX", &solveDistanceX); emscripten::function("solveDistanceY", &solveDistanceY); emscripten::function("solveAngle", &solveAngle); emscripten::function("solveCircleRadius", &solveCircleRadius); emscripten::function("solveCircleDiameter", &solveCircleDiameter); emscripten::function("solveEqualLines", &solveEqualLines); emscripten::function("solveEqualCircles", &solveEqualCircles); emscripten::function("solveTangentCircles", &solveTangentCircles); emscripten::function("solvePointSymmetry", &solvePointSymmetry); emscripten::function("solvePointOnLine", &solvePointOnLine); emscripten::function("solvePointOnCircle", &solvePointOnCircle); emscripten::function("solvePointOnArc", &solvePointOnArc); emscripten::function("solvePointOnEllipse", &solvePointOnEllipse); emscripten::function("solveEllipseInternalAlignment", &solveEllipseInternalAlignment); emscripten::function("solveEllipseInternalAlignmentSet", &solveEllipseInternalAlignmentSet); emscripten::function("solvePointOnCubicBspline", &solvePointOnCubicBspline); emscripten::function("solveCubicBsplineWeight", &solveCubicBsplineWeight); emscripten::function("solveParallelLines", &solveParallelLines); emscripten::function("solvePerpendicularLines", &solvePerpendicularLines); emscripten::function("solveCoincidentLines", &solveCoincidentLines); emscripten::function("solveCoincidentLinePoints", &solveCoincidentLinePoints); emscripten::function("solveSnellsLawLines", &solveSnellsLawLines); }