diff --git a/tests/src/Mod/Sketcher/App/planegcs/CMakeLists.txt b/tests/src/Mod/Sketcher/App/planegcs/CMakeLists.txt index dffcf38874..40d07edb2e 100644 --- a/tests/src/Mod/Sketcher/App/planegcs/CMakeLists.txt +++ b/tests/src/Mod/Sketcher/App/planegcs/CMakeLists.txt @@ -3,3 +3,9 @@ target_sources( PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/GCS.cpp ) + +target_sources( + Sketcher_tests_run + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/Constraints.cpp +) diff --git a/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp b/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp new file mode 100644 index 0000000000..670553d180 --- /dev/null +++ b/tests/src/Mod/Sketcher/App/planegcs/Constraints.cpp @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifdef WIN32 +#define _USE_MATH_DEFINES +#endif +#include + +#include "gtest/gtest.h" + +#include "Mod/Sketcher/App/planegcs/GCS.h" +#include "Mod/Sketcher/App/planegcs/Geo.h" +#include "Mod/Sketcher/App/planegcs/Constraints.h" + +class SystemTest: public GCS::System +{ +public: + size_t getNumberOfConstraints(int tagID = -1) + { + return _getNumberOfConstraints(tagID); + } +}; + +class ConstraintsTest: public ::testing::Test +{ +protected: + void SetUp() override + { + _system = std::make_unique(); + } + + void TearDown() override + { + _system.reset(); + } + + SystemTest* System() + { + return _system.get(); + } + +private: + std::unique_ptr _system; +}; + +TEST_F(ConstraintsTest, tangentBSplineAndArc) // NOLINT +{ + // Arrange + // TODO: Add arc, B-spline, and point + double pointX = 3.5, arcStartX = 5.0, arcEndX = 0.0, arcCenterX = 0.0; + double pointY = 3.5, arcStartY = 0.0, arcEndY = 5.0, arcCenterY = 0.0; + GCS::Point point, arcStart, arcEnd, arcCenter; + point.x = &pointX; + point.y = &pointY; + arcStart.x = &arcStartX; + arcStart.y = &arcStartY; + arcEnd.x = &arcEndX; + arcEnd.y = &arcEndY; + arcCenter.x = &arcCenterX; + arcCenter.y = &arcCenterY; + double arcRadius = 5.0, arcStartAngle = 0.0, arcEndAngle = M_PI / 2; + double desiredAngle = M_PI; + double bSplineStartX = 0.0, bSplineEndX = 16.0; + double bSplineStartY = 10.0, bSplineEndY = -10.0; + GCS::Point bSplineStart, bSplineEnd; + bSplineStart.x = &bSplineStartX; + bSplineStart.y = &bSplineStartY; + bSplineEnd.x = &bSplineEndX; + bSplineEnd.y = &bSplineEndY; + std::vector bSplineControlPointsX(5); + std::vector bSplineControlPointsY(5); + bSplineControlPointsX[0] = 0.0; + bSplineControlPointsY[0] = 10.0; + bSplineControlPointsX[1] = 0.0; + bSplineControlPointsY[1] = 6.0; + bSplineControlPointsX[2] = 6.0; + bSplineControlPointsY[2] = 0.5; + bSplineControlPointsX[3] = 16.0; + bSplineControlPointsY[3] = 0.5; + bSplineControlPointsX[4] = 16.0; + bSplineControlPointsY[4] = -10.0; + std::vector bSplineControlPoints(5); + for (size_t i = 0; i < bSplineControlPoints.size(); ++i) { + bSplineControlPoints[i].x = &bSplineControlPointsX[i]; + bSplineControlPoints[i].y = &bSplineControlPointsY[i]; + } + std::vector weights(bSplineControlPoints.size(), 1.0); + std::vector weightsAsPtr; + std::vector knots(bSplineControlPoints.size()); + std::vector knotsAsPtr; + std::vector mult(bSplineControlPoints.size(), 1); + mult.front() = 4; // Hardcoded for cubic + mult.back() = 4; // Hardcoded for cubic + for (size_t i = 0; i < bSplineControlPoints.size(); ++i) { + weightsAsPtr.push_back(&weights[i]); + knots[i] = i; + knotsAsPtr.push_back(&knots[i]); + } + GCS::Arc arc; + arc.start = arcStart; + arc.end = arcEnd; + arc.center = arcCenter; + arc.rad = &arcRadius; + arc.startAngle = &arcStartAngle; + arc.endAngle = &arcEndAngle; + GCS::BSpline bspline; + bspline.start = bSplineStart; + bspline.end = bSplineEnd; + bspline.poles = bSplineControlPoints; + bspline.weights = weightsAsPtr; + bspline.knots = knotsAsPtr; + bspline.mult = mult; + bspline.degree = 3; + bspline.periodic = false; + double bsplineParam = 0.35; + + std::vector params = {point.x, + point.y, + arcStart.x, + arcStart.y, + arcEnd.x, + arcEnd.y, + arcCenter.x, + arcCenter.y, + &arcRadius, + bSplineStart.x, + bSplineStart.y, + bSplineEnd.x, + bSplineEnd.y, + &bSplineControlPointsX[0], + &bSplineControlPointsY[0], + &bSplineControlPointsX[1], + &bSplineControlPointsY[1], + &bSplineControlPointsX[2], + &bSplineControlPointsY[2], + &bSplineControlPointsX[3], + &bSplineControlPointsY[3], + &bSplineControlPointsX[4], + &bSplineControlPointsY[4], + &desiredAngle, + &bsplineParam}; + params.insert(params.end(), weightsAsPtr.begin(), weightsAsPtr.end()); + params.insert(params.end(), knotsAsPtr.begin(), knotsAsPtr.end()); + + // Act + // TODO: Apply constraint and solve + System()->addConstraintArcRules(arc); + System()->addConstraintPointOnArc(point, arc, 0, true); + System()->addConstraintPointOnBSpline(point, bspline, &bsplineParam, 0, true); + System()->addConstraintAngleViaPointAndParam(bspline, + arc, + point, + &bsplineParam, + &desiredAngle, + 0, + true); + int solveResult = System()->solve(params); + if (solveResult == GCS::Success) { + System()->applySolution(); + } + + // Assert + EXPECT_EQ(solveResult, GCS::Success); + // is point on arc? + EXPECT_DOUBLE_EQ((arcRadius) * (arcRadius), + (pointX - arcCenterX) * (pointX - arcCenterX) + + (pointY - arcCenterY) * (pointY - arcCenterY)); + // is point on B-spline? + GCS::DeriVector2 pointAtBSplineParam = bspline.Value(bsplineParam, 1.0); + EXPECT_DOUBLE_EQ(pointAtBSplineParam.x, pointX); + EXPECT_DOUBLE_EQ(pointAtBSplineParam.y, pointY); + // TODO: are tangents at relevant parameter equal? + GCS::DeriVector2 centerToPoint((pointX - arcCenterX), (pointY - arcCenterY)); + GCS::DeriVector2 tangentBSplineAtPoint(pointAtBSplineParam.dx, pointAtBSplineParam.dy); + double dprd; + // FIXME: This error is probably too high. Fixing this may require improving the solver, + // however. + EXPECT_NEAR(std::fabs(centerToPoint.crossProdNorm(tangentBSplineAtPoint, dprd)) + / (centerToPoint.length() * tangentBSplineAtPoint.length()), + 1.0, + 0.005); +}