// SPDX-License-Identifier: LGPL-2.1-or-later #include #include #include #include #include #include #include #include "SketcherTestHelpers.h" using namespace SketcherTestHelpers; TEST_F(SketchObjectTest, createSketchObject) // NOLINT { // Arrange // Act // Assert } TEST_F(SketchObjectTest, testGeoIdFromShapeTypeEdge) { // Arrange // TODO: Do we need to separate existing vs non-existing? // It would need to be implemented in code as well. Data::IndexedName name("Edge", 1); int geoId; Sketcher::PointPos posId; // Act getObject()->geoIdFromShapeType(name, geoId, posId); // Assert EXPECT_EQ(geoId, 0); EXPECT_EQ(posId, Sketcher::PointPos::none); } TEST_F(SketchObjectTest, testGeoIdFromShapeTypeVertex) { // Arrange // For operating on vertices, there is newName a check if the vertex exists. Base::Vector3d p1(0.0, 0.0, 0.0), p2(1.0, 0.0, 0.0); std::unique_ptr geoline(new Part::GeomLineSegment()); static_cast(geoline.get())->setPoints(p1, p2); getObject()->addGeometry(geoline.get()); // TODO: Do we need to separate existing vs non-existing? // It would need to be implemented in code as well. Data::IndexedName name("Vertex", 1); int geoId; Sketcher::PointPos posId; // Act getObject()->geoIdFromShapeType(name, geoId, posId); // Assert EXPECT_EQ(geoId, 0); EXPECT_EQ(posId, Sketcher::PointPos::start); } TEST_F(SketchObjectTest, testGeoIdFromShapeTypeExternalEdge) { // Arrange // TODO: Do we need to separate existing vs non-existing? // It would need to be implemented in code as well. Data::IndexedName name("ExternalEdge", 1); int geoId; Sketcher::PointPos posId; // Act getObject()->geoIdFromShapeType(name, geoId, posId); // Assert EXPECT_EQ(geoId, Sketcher::GeoEnum::RefExt); EXPECT_EQ(posId, Sketcher::PointPos::none); } TEST_F(SketchObjectTest, testGeoIdFromShapeTypeHAxis) { // Arrange Data::IndexedName name("H_Axis"); int geoId; Sketcher::PointPos posId; // Act getObject()->geoIdFromShapeType(name, geoId, posId); // Assert EXPECT_EQ(geoId, Sketcher::GeoEnum::HAxis); EXPECT_EQ(posId, Sketcher::PointPos::none); } TEST_F(SketchObjectTest, testGeoIdFromShapeTypeVAxis) { // Arrange Data::IndexedName name("V_Axis"); int geoId; Sketcher::PointPos posId; // Act getObject()->geoIdFromShapeType(name, geoId, posId); // Assert EXPECT_EQ(geoId, Sketcher::GeoEnum::VAxis); EXPECT_EQ(posId, Sketcher::PointPos::none); } TEST_F(SketchObjectTest, testGeoIdFromShapeTypeRootPoint) { // Arrange Data::IndexedName name("RootPoint"); int geoId; Sketcher::PointPos posId; // Act getObject()->geoIdFromShapeType(name, geoId, posId); // Assert EXPECT_EQ(geoId, Sketcher::GeoEnum::RtPnt); EXPECT_EQ(posId, Sketcher::PointPos::start); } TEST_F(SketchObjectTest, testGetPointFromGeomPoint) { // Arrange Base::Vector3d coords(1.0, 2.0, 0.0); Part::GeomPoint point(coords); // Act auto ptStart = Sketcher::SketchObject::getPoint(&point, Sketcher::PointPos::start); auto ptMid = Sketcher::SketchObject::getPoint(&point, Sketcher::PointPos::mid); auto ptEnd = Sketcher::SketchObject::getPoint(&point, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&point, Sketcher::PointPos::none); // Assert EXPECT_DOUBLE_EQ(ptStart[0], 1.0); EXPECT_DOUBLE_EQ(ptStart[1], 2.0); EXPECT_DOUBLE_EQ(ptMid[0], 1.0); EXPECT_DOUBLE_EQ(ptMid[1], 2.0); EXPECT_DOUBLE_EQ(ptEnd[0], 1.0); EXPECT_DOUBLE_EQ(ptEnd[1], 2.0); } TEST_F(SketchObjectTest, testGetPointFromGeomLineSegment) { // Arrange Base::Vector3d coords1(1.0, 2.0, 0.0); Base::Vector3d coords2(3.0, 4.0, 0.0); Part::GeomLineSegment lineSeg; lineSeg.setPoints(coords1, coords2); // Act auto ptStart = Sketcher::SketchObject::getPoint(&lineSeg, Sketcher::PointPos::start); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptMid = Sketcher::SketchObject::getPoint(&lineSeg, Sketcher::PointPos::mid); auto ptEnd = Sketcher::SketchObject::getPoint(&lineSeg, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&lineSeg, Sketcher::PointPos::none); // Assert EXPECT_DOUBLE_EQ(ptStart[0], 1.0); EXPECT_DOUBLE_EQ(ptStart[1], 2.0); EXPECT_DOUBLE_EQ(ptEnd[0], 3.0); EXPECT_DOUBLE_EQ(ptEnd[1], 4.0); } TEST_F(SketchObjectTest, testGetPointFromGeomCircle) { // Arrange Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double radius = 3.0; Part::GeomCircle circle; circle.setCenter(coordsCenter); circle.setRadius(radius); // Act // TODO: Maybe we want this to give an error instead of some default value auto ptStart = Sketcher::SketchObject::getPoint(&circle, Sketcher::PointPos::start); auto ptMid = Sketcher::SketchObject::getPoint(&circle, Sketcher::PointPos::mid); // TODO: Maybe we want this to give an error instead of some default value auto ptEnd = Sketcher::SketchObject::getPoint(&circle, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&circle, Sketcher::PointPos::none); // Assert // NOTE: Presently, start/end points of a circle are defined as the point on circle right of the // the center EXPECT_DOUBLE_EQ(ptStart[0], 1.0 + radius); EXPECT_DOUBLE_EQ(ptStart[1], 2.0); EXPECT_DOUBLE_EQ(ptEnd[0], 1.0 + radius); EXPECT_DOUBLE_EQ(ptEnd[1], 2.0); EXPECT_DOUBLE_EQ(ptMid[0], 1.0); EXPECT_DOUBLE_EQ(ptMid[1], 2.0); } TEST_F(SketchObjectTest, testGetPointFromGeomEllipse) { // Arrange Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double majorRadius = 4.0; double minorRadius = 3.0; Part::GeomEllipse ellipse; ellipse.setCenter(coordsCenter); ellipse.setMajorRadius(majorRadius); ellipse.setMinorRadius(minorRadius); // Act // TODO: Maybe we want this to give an error instead of some default value auto ptStart = Sketcher::SketchObject::getPoint(&ellipse, Sketcher::PointPos::start); auto ptMid = Sketcher::SketchObject::getPoint(&ellipse, Sketcher::PointPos::mid); // TODO: Maybe we want this to give an error instead of some default value auto ptEnd = Sketcher::SketchObject::getPoint(&ellipse, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&ellipse, Sketcher::PointPos::none); // Assert // NOTE: Presently, start/end points of an ellipse are defined as the point on the major axis in // it's "positive" direction EXPECT_DOUBLE_EQ(ptStart[0], 1.0 + majorRadius); EXPECT_DOUBLE_EQ(ptStart[1], 2.0); EXPECT_DOUBLE_EQ(ptEnd[0], 1.0 + majorRadius); EXPECT_DOUBLE_EQ(ptEnd[1], 2.0); EXPECT_DOUBLE_EQ(ptMid[0], 1.0); EXPECT_DOUBLE_EQ(ptMid[1], 2.0); } TEST_F(SketchObjectTest, testGetPointFromGeomArcOfCircle) { // Arrange Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double radius = 3.0, startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; Part::GeomArcOfCircle arcOfCircle; arcOfCircle.setCenter(coordsCenter); arcOfCircle.setRadius(radius); arcOfCircle.setRange(startParam, endParam, true); // Act auto ptStart = Sketcher::SketchObject::getPoint(&arcOfCircle, Sketcher::PointPos::start); auto ptMid = Sketcher::SketchObject::getPoint(&arcOfCircle, Sketcher::PointPos::mid); auto ptEnd = Sketcher::SketchObject::getPoint(&arcOfCircle, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&arcOfCircle, Sketcher::PointPos::none); // Assert // NOTE: parameters for arc of circle are CCW angles from positive x-axis EXPECT_DOUBLE_EQ(ptStart[0], 1.0 + cos(startParam) * radius); EXPECT_DOUBLE_EQ(ptStart[1], 2.0 + sin(startParam) * radius); EXPECT_DOUBLE_EQ(ptEnd[0], 1.0 + cos(endParam) * radius); EXPECT_DOUBLE_EQ(ptEnd[1], 2.0 + sin(endParam) * radius); EXPECT_DOUBLE_EQ(ptMid[0], 1.0); EXPECT_DOUBLE_EQ(ptMid[1], 2.0); } TEST_F(SketchObjectTest, testGetPointFromGeomArcOfEllipse) { // Arrange Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double majorRadius = 4.0; double minorRadius = 3.0; double startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; Part::GeomArcOfEllipse arcOfEllipse; arcOfEllipse.setCenter(coordsCenter); arcOfEllipse.setMajorRadius(majorRadius); arcOfEllipse.setMinorRadius(minorRadius); arcOfEllipse.setRange(startParam, endParam, true); // Act auto ptStart = Sketcher::SketchObject::getPoint(&arcOfEllipse, Sketcher::PointPos::start); auto ptMid = Sketcher::SketchObject::getPoint(&arcOfEllipse, Sketcher::PointPos::mid); auto ptEnd = Sketcher::SketchObject::getPoint(&arcOfEllipse, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&arcOfEllipse, Sketcher::PointPos::none); // Assert // NOTE: parameters for arc of ellipse are CCW angles from positive x-axis EXPECT_DOUBLE_EQ(ptStart[0], 1.0 + cos(startParam) * majorRadius); EXPECT_DOUBLE_EQ(ptStart[1], 2.0 + sin(startParam) * minorRadius); EXPECT_DOUBLE_EQ(ptEnd[0], 1.0 + cos(endParam) * majorRadius); EXPECT_DOUBLE_EQ(ptEnd[1], 2.0 + sin(endParam) * minorRadius); EXPECT_DOUBLE_EQ(ptMid[0], 1.0); EXPECT_DOUBLE_EQ(ptMid[1], 2.0); } TEST_F(SketchObjectTest, testGetPointFromGeomArcOfHyperbola) { // Arrange Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double majorRadius = 4.0; double minorRadius = 3.0; double startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; Part::GeomArcOfHyperbola arcOfHyperbola; arcOfHyperbola.setCenter(coordsCenter); arcOfHyperbola.setMajorRadius(majorRadius); arcOfHyperbola.setMinorRadius(minorRadius); arcOfHyperbola.setRange(startParam, endParam, true); // Act [[maybe_unused]] auto ptStart = Sketcher::SketchObject::getPoint(&arcOfHyperbola, Sketcher::PointPos::start); auto ptMid = Sketcher::SketchObject::getPoint(&arcOfHyperbola, Sketcher::PointPos::mid); [[maybe_unused]] auto ptEnd = Sketcher::SketchObject::getPoint(&arcOfHyperbola, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&arcOfHyperbola, Sketcher::PointPos::none); // Assert // FIXME: Figure out how this is defined // EXPECT_DOUBLE_EQ(ptStart[0], 1.0); // EXPECT_DOUBLE_EQ(ptStart[1], 2.0); // EXPECT_DOUBLE_EQ(ptEnd[0], 1.0); // EXPECT_DOUBLE_EQ(ptEnd[1], 2.0); EXPECT_DOUBLE_EQ(ptMid[0], 1.0); EXPECT_DOUBLE_EQ(ptMid[1], 2.0); } TEST_F(SketchObjectTest, testGetPointFromGeomArcOfParabola) { // Arrange Base::Vector3d coordsCenter(1.0, 2.0, 0.0); double focal = 3.0; double startParam = std::numbers::pi / 3, endParam = std::numbers::pi * 1.5; Part::GeomArcOfParabola arcOfParabola; arcOfParabola.setCenter(coordsCenter); arcOfParabola.setFocal(focal); arcOfParabola.setRange(startParam, endParam, true); // Act [[maybe_unused]] auto ptStart = Sketcher::SketchObject::getPoint(&arcOfParabola, Sketcher::PointPos::start); auto ptMid = Sketcher::SketchObject::getPoint(&arcOfParabola, Sketcher::PointPos::mid); [[maybe_unused]] auto ptEnd = Sketcher::SketchObject::getPoint(&arcOfParabola, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&arcOfParabola, Sketcher::PointPos::none); // Assert // FIXME: Figure out how this is defined // EXPECT_DOUBLE_EQ(ptStart[0], 1.0); // EXPECT_DOUBLE_EQ(ptStart[1], 2.0); // EXPECT_DOUBLE_EQ(ptEnd[0], 1.0); // EXPECT_DOUBLE_EQ(ptEnd[1], 2.0); EXPECT_DOUBLE_EQ(ptMid[0], 1.0); EXPECT_DOUBLE_EQ(ptMid[1], 2.0); } TEST_F(SketchObjectTest, testGetPointFromGeomBSplineCurveNonPeriodic) { // Arrange int degree = 3; std::vector poles; poles.emplace_back(1, 0, 0); poles.emplace_back(1, 1, 0); poles.emplace_back(1, 0.5, 0); poles.emplace_back(0, 1, 0); poles.emplace_back(0, 0, 0); std::vector weights(5, 1.0); std::vector knotsNonPeriodic = {0.0, 1.0, 2.0}; std::vector multiplicitiesNonPeriodic = {degree + 1, 1, degree + 1}; Part::GeomBSplineCurve nonPeriodicBSpline(poles, weights, knotsNonPeriodic, multiplicitiesNonPeriodic, degree, false); // Act auto ptStart = Sketcher::SketchObject::getPoint(&nonPeriodicBSpline, Sketcher::PointPos::start); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptMid = Sketcher::SketchObject::getPoint(&nonPeriodicBSpline, Sketcher::PointPos::mid); auto ptEnd = Sketcher::SketchObject::getPoint(&nonPeriodicBSpline, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&nonPeriodicBSpline, Sketcher::PointPos::none); // Assert EXPECT_DOUBLE_EQ(ptStart[0], poles.front()[0]); EXPECT_DOUBLE_EQ(ptStart[1], poles.front()[1]); EXPECT_DOUBLE_EQ(ptEnd[0], poles.back()[0]); EXPECT_DOUBLE_EQ(ptEnd[1], poles.back()[1]); } TEST_F(SketchObjectTest, testGetPointFromGeomBSplineCurvePeriodic) { // Arrange int degree = 3; std::vector poles; poles.emplace_back(1, 0, 0); poles.emplace_back(1, 1, 0); poles.emplace_back(1, 0.5, 0); poles.emplace_back(0, 1, 0); poles.emplace_back(0, 0, 0); std::vector weights(5, 1.0); std::vector knotsPeriodic = {0.0, 0.3, 1.0, 1.5, 1.8, 2.0}; std::vector multiplicitiesPeriodic(6, 1); Part::GeomBSplineCurve periodicBSpline(poles, weights, knotsPeriodic, multiplicitiesPeriodic, degree, true); // Act // TODO: Maybe we want this to give an error instead of some default value auto ptStart = Sketcher::SketchObject::getPoint(&periodicBSpline, Sketcher::PointPos::start); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptMid = Sketcher::SketchObject::getPoint(&periodicBSpline, Sketcher::PointPos::mid); // TODO: Maybe we want this to give an error instead of some default value auto ptEnd = Sketcher::SketchObject::getPoint(&periodicBSpline, Sketcher::PointPos::end); // TODO: Maybe we want this to give an error instead of some default value [[maybe_unused]] auto ptNone = Sketcher::SketchObject::getPoint(&periodicBSpline, Sketcher::PointPos::none); // Assert // With non-trivial values for weights, knots, mults, etc, getting the coordinates is // non-trivial as well. This is the best we can do. EXPECT_DOUBLE_EQ(ptStart[0], ptEnd[0]); EXPECT_DOUBLE_EQ(ptStart[1], ptEnd[1]); } TEST_F(SketchObjectTest, testConstraintAfterDeletingGeo) { // Arrange int geoId1 = 42, geoId2 = 10, geoId3 = 0, geoId4 = -8; Sketcher::Constraint* nullConstr = nullptr; Sketcher::Constraint constr1; constr1.Type = Sketcher::ConstraintType::Coincident; constr1.First = geoId1; constr1.FirstPos = Sketcher::PointPos::start; constr1.Second = geoId2; constr1.SecondPos = Sketcher::PointPos::end; Sketcher::Constraint constr2; constr2.Type = Sketcher::ConstraintType::Tangent; constr2.First = geoId4; constr2.FirstPos = Sketcher::PointPos::none; constr2.Second = geoId3; constr2.SecondPos = Sketcher::PointPos::none; constr2.Third = geoId1; constr2.ThirdPos = Sketcher::PointPos::start; // Act auto nullConstrAfter = getObject()->getConstraintAfterDeletingGeo(nullConstr, 5); // Assert EXPECT_EQ(nullConstrAfter, nullptr); // Act getObject()->changeConstraintAfterDeletingGeo(nullConstr, 5); // Assert EXPECT_EQ(nullConstr, nullptr); // Act // delete typical in-sketch geo auto constr1PtrAfter1 = getObject()->getConstraintAfterDeletingGeo(&constr1, 5); // delete external geo (negative id) auto constr1PtrAfter2 = getObject()->getConstraintAfterDeletingGeo(&constr1, -5); // Delete a geo involved in the constraint auto constr1PtrAfter3 = getObject()->getConstraintAfterDeletingGeo(&constr1, 10); // Assert EXPECT_EQ(constr1.Type, Sketcher::ConstraintType::Coincident); EXPECT_EQ(constr1.First, geoId1); EXPECT_EQ(constr1.Second, geoId2); EXPECT_EQ(constr1PtrAfter1->First, geoId1 - 1); EXPECT_EQ(constr1PtrAfter1->Second, geoId2 - 1); EXPECT_EQ(constr1PtrAfter2->Third, Sketcher::GeoEnum::GeoUndef); EXPECT_EQ(constr1PtrAfter3.get(), nullptr); // Act getObject()->changeConstraintAfterDeletingGeo(&constr2, -3); // Assert EXPECT_EQ(constr2.Type, Sketcher::ConstraintType::Tangent); EXPECT_EQ(constr2.First, geoId4 + 1); EXPECT_EQ(constr2.Second, geoId3); EXPECT_EQ(constr2.Third, geoId1); // Act // Delete a geo involved in the constraint getObject()->changeConstraintAfterDeletingGeo(&constr2, 0); // Assert EXPECT_EQ(constr2.Type, Sketcher::ConstraintType::None); } TEST_F(SketchObjectTest, testDeleteExposeInternalGeometryOfEllipse) { // Arrange Part::GeomEllipse ellipse; setupEllipse(ellipse); int geoId = getObject()->addGeometry(&ellipse); // Act getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoId); // Assert // Ensure there's only one curve EXPECT_EQ(getObject()->getHighestCurveIndex(), 0); // Act // "Expose" internal geometry getObject()->exposeInternalGeometry(geoId); // Assert // Ensure all internal geometry is satisfied // TODO: Also try to ensure types of geometries that have this type const auto constraints = getObject()->Constraints.getValues(); for (auto alignmentType : {Sketcher::InternalAlignmentType::EllipseMajorDiameter, Sketcher::InternalAlignmentType::EllipseMinorDiameter, Sketcher::InternalAlignmentType::EllipseFocus1, Sketcher::InternalAlignmentType::EllipseFocus2}) { // TODO: Ensure there exists one and only one curve with this type int numConstraintsOfThisType = std::count_if( constraints.begin(), constraints.end(), [&geoId, &alignmentType](const auto* constr) { return constr->Type == Sketcher::ConstraintType::InternalAlignment && constr->AlignmentType == alignmentType && constr->Second == geoId; } ); EXPECT_EQ(numConstraintsOfThisType, 1); } // Act // Delete internal geometry (again) getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoId); // Assert // Ensure there's only one curve EXPECT_EQ(getObject()->getHighestCurveIndex(), 0); } TEST_F(SketchObjectTest, testDeleteExposeInternalGeometryOfHyperbola) { // Arrange Part::GeomArcOfHyperbola aoh; setupArcOfHyperbola(aoh); int geoId = getObject()->addGeometry(&aoh); // Act getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoId); // Assert // Ensure there's only one curve EXPECT_EQ(getObject()->getHighestCurveIndex(), 0); // Act // "Expose" internal geometry getObject()->exposeInternalGeometry(geoId); // Assert // Ensure all internal geometry is satisfied // TODO: Also try to ensure types of geometries that have this type const auto constraints = getObject()->Constraints.getValues(); for (auto alignmentType : {Sketcher::InternalAlignmentType::HyperbolaMajor, Sketcher::InternalAlignmentType::HyperbolaMinor, Sketcher::InternalAlignmentType::HyperbolaFocus}) { // TODO: Ensure there exists one and only one curve with this type int numConstraintsOfThisType = std::count_if( constraints.begin(), constraints.end(), [&geoId, &alignmentType](const auto* constr) { return constr->Type == Sketcher::ConstraintType::InternalAlignment && constr->AlignmentType == alignmentType && constr->Second == geoId; } ); EXPECT_EQ(numConstraintsOfThisType, 1); } // Act // Delete internal geometry (again) getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoId); // Assert // Ensure there's only one curve EXPECT_EQ(getObject()->getHighestCurveIndex(), 0); } TEST_F(SketchObjectTest, testDeleteExposeInternalGeometryOfParabola) { // Arrange Part::GeomArcOfParabola aoh; setupArcOfParabola(aoh); int geoId = getObject()->addGeometry(&aoh); // Act getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoId); // Assert // Ensure there's only one curve EXPECT_EQ(getObject()->getHighestCurveIndex(), 0); // Act // "Expose" internal geometry getObject()->exposeInternalGeometry(geoId); // Assert // Ensure all internal geometry is satisfied // TODO: Also try to ensure types of geometries that have this type const auto constraints = getObject()->Constraints.getValues(); for (auto alignmentType : {Sketcher::InternalAlignmentType::ParabolaFocalAxis, Sketcher::InternalAlignmentType::ParabolaFocus}) { // TODO: Ensure there exists one and only one curve with this type int numConstraintsOfThisType = std::count_if( constraints.begin(), constraints.end(), [&geoId, &alignmentType](const auto* constr) { return constr->Type == Sketcher::ConstraintType::InternalAlignment && constr->AlignmentType == alignmentType && constr->Second == geoId; } ); EXPECT_EQ(numConstraintsOfThisType, 1); } // Act // Delete internal geometry (again) getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoId); // Assert // Ensure there's only one curve EXPECT_EQ(getObject()->getHighestCurveIndex(), 0); } TEST_F(SketchObjectTest, testDeleteExposeInternalGeometryOfBSpline) { // NOTE: We test only non-periodic B-spline here. Periodic B-spline should behave exactly the // same. // Arrange auto nonPeriodicBSpline = createTypicalNonPeriodicBSpline(); int geoId = getObject()->addGeometry(nonPeriodicBSpline.get()); // Act getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoId); // Assert // Ensure there's only one curve EXPECT_EQ(getObject()->getHighestCurveIndex(), 0); // Act // "Expose" internal geometry getObject()->exposeInternalGeometry(geoId); // Assert // Ensure all internal geometry is satisfied // TODO: Also try to ensure types of geometries that have this type const auto constraints = getObject()->Constraints.getValues(); std::map numConstraintsOfThisType; for (auto alignmentType : {Sketcher::InternalAlignmentType::BSplineControlPoint, Sketcher::InternalAlignmentType::BSplineKnotPoint}) { // TODO: Ensure there exists one and only one curve with this type numConstraintsOfThisType[alignmentType] = std::count_if( constraints.begin(), constraints.end(), [&geoId, &alignmentType](const auto* constr) { return constr->Type == Sketcher::ConstraintType::InternalAlignment && constr->AlignmentType == alignmentType && constr->Second == geoId; } ); } EXPECT_EQ( numConstraintsOfThisType[Sketcher::InternalAlignmentType::BSplineControlPoint], nonPeriodicBSpline->countPoles() ); EXPECT_EQ( numConstraintsOfThisType[Sketcher::InternalAlignmentType::BSplineKnotPoint], nonPeriodicBSpline->countKnots() ); // Act // Delete internal geometry (again) getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoId); // Assert // Ensure there's only one curve EXPECT_EQ(getObject()->getHighestCurveIndex(), 0); } // TODO: Needs to be done for other curves too but currently they are working as intended TEST_F(SketchObjectTest, testDeleteOnlyUnusedInternalGeometryOfBSpline) { // NOTE: We test only non-periodic B-spline here. Periodic B-spline should behave exactly the // same. // Arrange auto nonPeriodicBSpline = createTypicalNonPeriodicBSpline(); int geoIdBsp = getObject()->addGeometry(nonPeriodicBSpline.get()); // Ensure "exposed" internal geometry getObject()->exposeInternalGeometry(geoIdBsp); Base::Vector3d coords(1.0, 1.0, 0.0); Part::GeomPoint point(coords); int geoIdPnt = getObject()->addGeometry(&point); const auto constraints = getObject()->Constraints.getValues(); auto it = std::find_if(constraints.begin(), constraints.end(), [&geoIdBsp](const auto* constr) { return constr->Type == Sketcher::ConstraintType::InternalAlignment && constr->AlignmentType == Sketcher::InternalAlignmentType::BSplineControlPoint && constr->Second == geoIdBsp && constr->InternalAlignmentIndex == 1; }); // One Assert to avoid EXPECT_NE(it, constraints.end()); auto constraint = new Sketcher::Constraint(); // Ownership will be transferred to the sketch constraint->Type = Sketcher::ConstraintType::Coincident; constraint->First = geoIdPnt; constraint->FirstPos = Sketcher::PointPos::start; constraint->Second = (*it)->First; constraint->SecondPos = Sketcher::PointPos::mid; getObject()->addConstraint(constraint); // Act getObject()->deleteUnusedInternalGeometryAndUpdateGeoId(geoIdBsp); // Assert // Ensure there are 3 curves: the B-spline, its pole, and the point coincident on the pole EXPECT_EQ(getObject()->getHighestCurveIndex(), 2); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionNoUnits1) { std::string expr = Sketcher::SketchObject::reverseAngleConstraintExpression("180 - 60"); EXPECT_EQ(expr, std::string("60")); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionNoUnits2) { std::string expr = Sketcher::SketchObject::reverseAngleConstraintExpression("60"); EXPECT_EQ(expr, std::string("180 - (60)")); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionWithUnits1) { std::string expr = Sketcher::SketchObject::reverseAngleConstraintExpression("180 ° - 60 °"); EXPECT_EQ(expr, std::string("60 °")); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionWithUnits2) { std::string expr = Sketcher::SketchObject::reverseAngleConstraintExpression("60 °"); EXPECT_EQ(expr, std::string("180 ° - (60 °)")); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionWithUnits3) { std::string expr = Sketcher::SketchObject::reverseAngleConstraintExpression("60 deg"); EXPECT_EQ(expr, std::string("180 ° - (60 deg)")); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionWithUnits4) { std::string expr = Sketcher::SketchObject::reverseAngleConstraintExpression("1rad"); EXPECT_EQ(expr, std::string("180 ° - (1rad)")); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionApplyAndReverse1) { std::string expr = "180"; expr = Sketcher::SketchObject::reverseAngleConstraintExpression(expr); expr = Sketcher::SketchObject::reverseAngleConstraintExpression(expr); EXPECT_EQ(expr, std::string("(180)")); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionApplyAndReverse2) { std::string expr = "(30 + 15) * 2 / 3"; expr = Sketcher::SketchObject::reverseAngleConstraintExpression(expr); expr = Sketcher::SketchObject::reverseAngleConstraintExpression(expr); EXPECT_EQ(expr, std::string("((30 + 15) * 2 / 3)")); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionSimple) { // Arrange auto constraint = new Sketcher::Constraint(); // Ownership will be transferred to the sketch constraint->Type = Sketcher::ConstraintType::Angle; auto id = getObject()->addConstraint(constraint); App::ObjectIdentifier path(App::ObjectIdentifier::parse(getObject(), "Constraints[0]")); std::shared_ptr shared_expr(App::Expression::parse(getObject(), "0")); getObject()->setExpression(path, shared_expr); getObject()->setConstraintExpression(id, "180 - (60)"); // Act getObject()->reverseAngleConstraintToSupplementary(constraint, id); // Assert EXPECT_EQ(std::string("60"), getObject()->getConstraintExpression(id)); } TEST_F(SketchObjectTest, testReverseAngleConstraintToSupplementaryExpressionApplyAndReverse) { // Arrange auto constraint = new Sketcher::Constraint(); // Ownership will be transferred to the sketch constraint->Type = Sketcher::ConstraintType::Angle; auto id = getObject()->addConstraint(constraint); App::ObjectIdentifier path(App::ObjectIdentifier::parse(getObject(), "Constraints[0]")); std::shared_ptr shared_expr(App::Expression::parse(getObject(), "0")); getObject()->setExpression(path, shared_expr); getObject()->setConstraintExpression(id, "32 °"); // Act getObject()->reverseAngleConstraintToSupplementary(constraint, id); getObject()->reverseAngleConstraintToSupplementary(constraint, id); // Assert EXPECT_EQ(std::string("32 °"), getObject()->getConstraintExpression(id)); } TEST_F(SketchObjectTest, testGetElementName) { // Arrange Base::Vector3d p1(0.0, 0.0, 0.0), p2(1.0, 0.0, 0.0); std::unique_ptr geoline(new Part::GeomLineSegment()); static_cast(geoline.get())->setPoints(p1, p2); auto id = getObject()->addGeometry(geoline.get()); long tag; getObject()->getGeometryId(id, tag); // We need to look up the tag that got assigned std::ostringstream oss; oss << "g" << tag; auto tagName = oss.str(); getObject()->recomputeFeature(); // or ->execute() // Act // unless it's Export, we are really just testing the superclass App::GeoFeature::getElementName // call. auto forward_normal_name = getObject()->getElementName( (tagName + ";SKT").c_str(), App::GeoFeature::ElementNameType::Normal ); auto reverse_normal_name = getObject()->getElementName("Vertex2", App::GeoFeature::ElementNameType::Normal); auto reverse_export_name = getObject()->getElementName("Vertex1", App::GeoFeature::ElementNameType::Export); auto map = getObject()->Shape.getShape().getElementMap(); ASSERT_EQ(map.size(), 3); EXPECT_STREQ(map[0].name.toString().c_str(), (tagName + ";SKT").c_str()); EXPECT_EQ(map[0].index.toString(), "Edge1"); EXPECT_STREQ(map[1].name.toString().c_str(), (tagName + "v1;SKT").c_str()); EXPECT_EQ(map[1].index.toString(), "Vertex1"); EXPECT_STREQ(map[2].name.toString().c_str(), (tagName + "v2;SKT").c_str()); EXPECT_EQ(map[2].index.toString(), "Vertex2"); // Assert EXPECT_STREQ(forward_normal_name.newName.c_str(), (";" + tagName + ";SKT.Edge1").c_str()); EXPECT_STREQ(forward_normal_name.oldName.c_str(), "Edge1"); EXPECT_STREQ(reverse_normal_name.newName.c_str(), (";" + tagName + "v2;SKT.Vertex2").c_str()); EXPECT_STREQ(reverse_normal_name.oldName.c_str(), "Vertex2"); EXPECT_STREQ(reverse_export_name.newName.c_str(), (";" + tagName + "v1;SKT.Vertex1").c_str()); EXPECT_STREQ(reverse_export_name.oldName.c_str(), "Vertex1"); }