From 9ee9dc02a4e426655ca71747794d8a97ea28f9d6 Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Tue, 24 Feb 2026 15:34:22 +0000 Subject: [PATCH] Modeling Data - Fix IsClosed/IsPeriodic tolerance and add TKG2d GTests (#1109) IsClosed() methods across curve and surface classes used gp::Resolution() (~1e-290) for point distance comparison, making the check practically unusable for floating-point evaluated points. Replaced with Precision::Computational() (~DBL_EPSILON) which correctly represents machine-precision arithmetic equality. TrimmedCurve (2D/3D) and RectangularTrimmedSurface IsClosed() and IsPeriodic() now detect when the trim spans an integer multiple of the basis geometry period using std::remainder(), instead of unconditionally returning false for trimmed parametric ranges. Added 17 new GTest files (271 tests) for TKG2d covering previously untested packages: Adaptor2d, Geom2dLProp, LProp, and fundamental Geom2d classes (Circle, Ellipse, Line, Hyperbola, Parabola, TrimmedCurve, Transformation, VectorWithMagnitude, Direction, CartesianPoint, AxisPlacement). IsClosed() tolerance fix applied to: - Geom2d_TrimmedCurve, Geom_TrimmedCurve - Geom2d_BSplineCurve, Geom_BSplineCurve - Geom2d_OffsetCurve, Geom_OffsetCurve IsPeriodic() / IsUPeriodic() / IsVPeriodic() fix applied to: - Geom2d_TrimmedCurve, Geom_TrimmedCurve - Geom_RectangularTrimmedSurface (also IsUClosed/IsVClosed) --- .../TKG2d/GTests/Adaptor2d_Line2d_Test.cxx | 227 ++++++++++++++ .../GTests/Adaptor2d_OffsetCurve_Test.cxx | 211 +++++++++++++ src/ModelingData/TKG2d/GTests/FILES.cmake | 17 ++ .../GTests/Geom2dLProp_CLProps2d_Test.cxx | 231 ++++++++++++++ .../GTests/Geom2dLProp_CurAndInf2d_Test.cxx | 158 ++++++++++ .../GTests/Geom2d_AxisPlacement_Test.cxx | 185 ++++++++++++ .../GTests/Geom2d_CartesianPoint_Test.cxx | 136 +++++++++ .../TKG2d/GTests/Geom2d_Circle_Test.cxx | 177 +++++++++++ .../TKG2d/GTests/Geom2d_Direction_Test.cxx | 159 ++++++++++ .../TKG2d/GTests/Geom2d_Ellipse_Test.cxx | 173 +++++++++++ .../TKG2d/GTests/Geom2d_Hyperbola_Test.cxx | 152 ++++++++++ .../TKG2d/GTests/Geom2d_Line_Test.cxx | 188 ++++++++++++ .../TKG2d/GTests/Geom2d_Parabola_Test.cxx | 141 +++++++++ .../GTests/Geom2d_Transformation_Test.cxx | 284 ++++++++++++++++++ .../TKG2d/GTests/Geom2d_TrimmedCurve_Test.cxx | 197 ++++++++++++ .../Geom2d_VectorWithMagnitude_Test.cxx | 222 ++++++++++++++ .../GTests/LProp_AnalyticCurInf_Test.cxx | 136 +++++++++ .../TKG2d/GTests/LProp_CurAndInf_Test.cxx | 129 ++++++++ .../TKG2d/Geom2d/Geom2d_BSplineCurve_1.cxx | 2 +- .../TKG2d/Geom2d/Geom2d_OffsetCurve.cxx | 2 +- .../TKG2d/Geom2d/Geom2d_TrimmedCurve.cxx | 21 +- .../TKG2d/Geom2d/Geom2d_TrimmedCurve.hxx | 9 +- .../TKG3d/Geom/Geom_BSplineCurve_1.cxx | 3 +- .../TKG3d/Geom/Geom_OffsetCurve.cxx | 2 +- .../Geom/Geom_RectangularTrimmedSurface.cxx | 52 +++- .../Geom/Geom_RectangularTrimmedSurface.hxx | 16 +- .../TKG3d/Geom/Geom_TrimmedCurve.cxx | 19 +- .../TKG3d/Geom/Geom_TrimmedCurve.hxx | 8 +- 28 files changed, 3219 insertions(+), 38 deletions(-) create mode 100644 src/ModelingData/TKG2d/GTests/Adaptor2d_Line2d_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Adaptor2d_OffsetCurve_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2dLProp_CLProps2d_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2dLProp_CurAndInf2d_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_AxisPlacement_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_CartesianPoint_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_Circle_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_Direction_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_Ellipse_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_Hyperbola_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_Line_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_Parabola_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_Transformation_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_TrimmedCurve_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2d_VectorWithMagnitude_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/LProp_AnalyticCurInf_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/LProp_CurAndInf_Test.cxx diff --git a/src/ModelingData/TKG2d/GTests/Adaptor2d_Line2d_Test.cxx b/src/ModelingData/TKG2d/GTests/Adaptor2d_Line2d_Test.cxx new file mode 100644 index 0000000000..cd0bae6444 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Adaptor2d_Line2d_Test.cxx @@ -0,0 +1,227 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class Adaptor2d_Line2dTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Line through origin along direction (1, 0) with bounds [0, 10] + myLine = new Adaptor2d_Line2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), 0.0, 10.0); + + // Line through (1, 2) along direction (3, 4)/5 with bounds [-5, 5] + myDiagLine = + new Adaptor2d_Line2d(gp_Pnt2d(1.0, 2.0), gp_Dir2d(3.0 / 5.0, 4.0 / 5.0), -5.0, 5.0); + } + + occ::handle myLine; + occ::handle myDiagLine; +}; + +TEST_F(Adaptor2d_Line2dTest, DefaultConstructor) +{ + Adaptor2d_Line2d aLine; + EXPECT_EQ(aLine.GetType(), GeomAbs_Line); +} + +TEST_F(Adaptor2d_Line2dTest, ParameterBounds) +{ + EXPECT_DOUBLE_EQ(myLine->FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(myLine->LastParameter(), 10.0); + EXPECT_DOUBLE_EQ(myDiagLine->FirstParameter(), -5.0); + EXPECT_DOUBLE_EQ(myDiagLine->LastParameter(), 5.0); +} + +TEST_F(Adaptor2d_Line2dTest, Continuity) +{ + EXPECT_EQ(myLine->Continuity(), GeomAbs_CN); +} + +TEST_F(Adaptor2d_Line2dTest, NbIntervals) +{ + EXPECT_EQ(myLine->NbIntervals(GeomAbs_C0), 1); + EXPECT_EQ(myLine->NbIntervals(GeomAbs_CN), 1); +} + +TEST_F(Adaptor2d_Line2dTest, Intervals) +{ + NCollection_Array1 anIntervals(1, 2); + myLine->Intervals(anIntervals, GeomAbs_CN); + EXPECT_DOUBLE_EQ(anIntervals(1), 0.0); + EXPECT_DOUBLE_EQ(anIntervals(2), 10.0); +} + +TEST_F(Adaptor2d_Line2dTest, IsNotClosed) +{ + EXPECT_FALSE(myLine->IsClosed()); +} + +TEST_F(Adaptor2d_Line2dTest, IsNotPeriodic) +{ + EXPECT_FALSE(myLine->IsPeriodic()); +} + +TEST_F(Adaptor2d_Line2dTest, GetType) +{ + EXPECT_EQ(myLine->GetType(), GeomAbs_Line); +} + +TEST_F(Adaptor2d_Line2dTest, Value_AtOrigin) +{ + const gp_Pnt2d aPnt = myLine->Value(0.0); + EXPECT_NEAR(aPnt.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, Value_AtParameter) +{ + const gp_Pnt2d aPnt = myLine->Value(5.0); + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, Value_DiagonalLine) +{ + // At U=0, point should be origin (1, 2) + const gp_Pnt2d aPnt0 = myDiagLine->Value(0.0); + EXPECT_NEAR(aPnt0.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aPnt0.Y(), 2.0, Precision::Confusion()); + + // At U=5, point should be (1 + 3, 2 + 4) = (4, 6) + const gp_Pnt2d aPnt5 = myDiagLine->Value(5.0); + EXPECT_NEAR(aPnt5.X(), 4.0, Precision::Confusion()); + EXPECT_NEAR(aPnt5.Y(), 6.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, D0) +{ + gp_Pnt2d aPnt; + myLine->D0(3.0, aPnt); + EXPECT_NEAR(aPnt.X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, D1_FirstDerivativeIsDirection) +{ + gp_Pnt2d aPnt; + gp_Vec2d aVec; + myLine->D1(5.0, aPnt, aVec); + + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aVec.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aVec.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, D2_SecondDerivativeIsZero) +{ + gp_Pnt2d aPnt; + gp_Vec2d aV1, aV2; + myLine->D2(5.0, aPnt, aV1, aV2); + + EXPECT_NEAR(aV2.Magnitude(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, D3_ThirdDerivativeIsZero) +{ + gp_Pnt2d aPnt; + gp_Vec2d aV1, aV2, aV3; + myLine->D3(5.0, aPnt, aV1, aV2, aV3); + + EXPECT_NEAR(aV3.Magnitude(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, DN_FirstOrderMatchesD1) +{ + const gp_Vec2d aDN1 = myLine->DN(5.0, 1); + EXPECT_NEAR(aDN1.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aDN1.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, DN_HigherOrderIsZero) +{ + const gp_Vec2d aDN2 = myLine->DN(5.0, 2); + EXPECT_NEAR(aDN2.Magnitude(), 0.0, Precision::Confusion()); + + const gp_Vec2d aDN3 = myLine->DN(5.0, 3); + EXPECT_NEAR(aDN3.Magnitude(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, Resolution) +{ + // For a unit-speed line, resolution should equal the input tolerance + const double aResolution = myLine->Resolution(0.001); + EXPECT_NEAR(aResolution, 0.001, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, LineGeometry) +{ + const gp_Lin2d aLin = myLine->Line(); + EXPECT_NEAR(aLin.Direction().X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aLin.Direction().Y(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aLin.Location().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aLin.Location().Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, Load_WithLine) +{ + Adaptor2d_Line2d aLine; + gp_Lin2d aLin(gp_Pnt2d(1.0, 1.0), gp_Dir2d(0.0, 1.0)); + aLine.Load(aLin); + + const gp_Pnt2d aPnt = aLine.Value(3.0); + EXPECT_NEAR(aPnt.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 4.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, Load_WithBounds) +{ + Adaptor2d_Line2d aLine; + gp_Lin2d aLin(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + aLine.Load(aLin, 2.0, 8.0); + + EXPECT_DOUBLE_EQ(aLine.FirstParameter(), 2.0); + EXPECT_DOUBLE_EQ(aLine.LastParameter(), 8.0); +} + +TEST_F(Adaptor2d_Line2dTest, Trim) +{ + occ::handle aTrimmed = myLine->Trim(2.0, 7.0, Precision::Confusion()); + EXPECT_DOUBLE_EQ(aTrimmed->FirstParameter(), 2.0); + EXPECT_DOUBLE_EQ(aTrimmed->LastParameter(), 7.0); + + const gp_Pnt2d aPnt = aTrimmed->Value(2.0); + EXPECT_NEAR(aPnt.X(), 2.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_Line2dTest, ShallowCopy) +{ + occ::handle aCopy = myLine->ShallowCopy(); + EXPECT_DOUBLE_EQ(aCopy->FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(aCopy->LastParameter(), 10.0); + EXPECT_EQ(aCopy->GetType(), GeomAbs_Line); + + const gp_Pnt2d aPnt = aCopy->Value(5.0); + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Adaptor2d_OffsetCurve_Test.cxx b/src/ModelingData/TKG2d/GTests/Adaptor2d_OffsetCurve_Test.cxx new file mode 100644 index 0000000000..3c5bc49309 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Adaptor2d_OffsetCurve_Test.cxx @@ -0,0 +1,211 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class Adaptor2d_OffsetCurveTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Horizontal line through origin, bounds [0, 10] + myBaseLine = new Adaptor2d_Line2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), 0.0, 10.0); + } + + occ::handle myBaseLine; +}; + +TEST_F(Adaptor2d_OffsetCurveTest, DefaultConstructor) +{ + Adaptor2d_OffsetCurve anOffset; + EXPECT_DOUBLE_EQ(anOffset.Offset(), 0.0); +} + +TEST_F(Adaptor2d_OffsetCurveTest, ConstructWithCurve_OffsetIsZero) +{ + // Constructor with only curve sets offset to 0 and bounds to 0,0 + Adaptor2d_OffsetCurve anOffset(myBaseLine); + EXPECT_DOUBLE_EQ(anOffset.Offset(), 0.0); + EXPECT_DOUBLE_EQ(anOffset.FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(anOffset.LastParameter(), 0.0); +} + +TEST_F(Adaptor2d_OffsetCurveTest, ConstructWithOffset) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + EXPECT_DOUBLE_EQ(anOffset.Offset(), 3.0); + EXPECT_DOUBLE_EQ(anOffset.FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(anOffset.LastParameter(), 10.0); +} + +TEST_F(Adaptor2d_OffsetCurveTest, ConstructWithOffsetAndBounds) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 2.0, 1.0, 8.0); + EXPECT_DOUBLE_EQ(anOffset.Offset(), 2.0); + EXPECT_DOUBLE_EQ(anOffset.FirstParameter(), 1.0); + EXPECT_DOUBLE_EQ(anOffset.LastParameter(), 8.0); +} + +TEST_F(Adaptor2d_OffsetCurveTest, Value_PositiveOffset) +{ + // Offsetting a horizontal +X line by +3 shifts Y by -3 + // (OCCT 2D offset uses right-hand normal: Z cross tangent) + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + const gp_Pnt2d aPnt = anOffset.Value(5.0); + + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), -3.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_OffsetCurveTest, Value_NegativeOffset) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, -2.0); + const gp_Pnt2d aPnt = anOffset.Value(5.0); + + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 2.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_OffsetCurveTest, Value_ZeroOffset_MatchesBaseCurve) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 0.0); + + for (double u = 0.0; u <= 10.0; u += 2.0) + { + const gp_Pnt2d aOffPnt = anOffset.Value(u); + const gp_Pnt2d aBasePnt = myBaseLine->Value(u); + EXPECT_TRUE(aOffPnt.IsEqual(aBasePnt, Precision::Confusion())); + } +} + +TEST_F(Adaptor2d_OffsetCurveTest, D1_OffsetLine) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + + gp_Pnt2d aPnt; + gp_Vec2d aVec; + anOffset.D1(5.0, aPnt, aVec); + + // First derivative of an offset line is same as the base line direction + EXPECT_NEAR(aVec.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aVec.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Adaptor2d_OffsetCurveTest, Continuity) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + // Offset of CN line is CN + EXPECT_EQ(anOffset.Continuity(), GeomAbs_CN); +} + +TEST_F(Adaptor2d_OffsetCurveTest, NbIntervals) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + EXPECT_EQ(anOffset.NbIntervals(GeomAbs_CN), 1); +} + +TEST_F(Adaptor2d_OffsetCurveTest, GetType_Line) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + EXPECT_EQ(anOffset.GetType(), GeomAbs_Line); +} + +TEST_F(Adaptor2d_OffsetCurveTest, IsNotClosed) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + EXPECT_FALSE(anOffset.IsClosed()); +} + +TEST_F(Adaptor2d_OffsetCurveTest, IsNotPeriodic) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + EXPECT_FALSE(anOffset.IsPeriodic()); +} + +TEST_F(Adaptor2d_OffsetCurveTest, LoadCurve_ResetsOffset) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 5.0); + EXPECT_DOUBLE_EQ(anOffset.Offset(), 5.0); + + // Load a new curve resets offset to 0 + occ::handle aNewLine = + new Adaptor2d_Line2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(0.0, 1.0), 0.0, 5.0); + anOffset.Load(aNewLine); + EXPECT_DOUBLE_EQ(anOffset.Offset(), 0.0); +} + +TEST_F(Adaptor2d_OffsetCurveTest, LoadOffset) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine); + EXPECT_DOUBLE_EQ(anOffset.Offset(), 0.0); + + anOffset.Load(7.5); + EXPECT_DOUBLE_EQ(anOffset.Offset(), 7.5); +} + +TEST_F(Adaptor2d_OffsetCurveTest, LoadOffsetWithBounds) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine); + anOffset.Load(4.0, 2.0, 6.0); + + EXPECT_DOUBLE_EQ(anOffset.Offset(), 4.0); + EXPECT_DOUBLE_EQ(anOffset.FirstParameter(), 2.0); + EXPECT_DOUBLE_EQ(anOffset.LastParameter(), 6.0); +} + +TEST_F(Adaptor2d_OffsetCurveTest, Curve_ReturnsBaseCurve) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + const occ::handle& aCurve = anOffset.Curve(); + EXPECT_FALSE(aCurve.IsNull()); + EXPECT_EQ(aCurve->GetType(), GeomAbs_Line); +} + +TEST_F(Adaptor2d_OffsetCurveTest, Trim) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + occ::handle aTrimmed = anOffset.Trim(2.0, 7.0, Precision::Confusion()); + + EXPECT_DOUBLE_EQ(aTrimmed->FirstParameter(), 2.0); + EXPECT_DOUBLE_EQ(aTrimmed->LastParameter(), 7.0); +} + +TEST_F(Adaptor2d_OffsetCurveTest, ShallowCopy) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0, 1.0, 9.0); + occ::handle aCopy = anOffset.ShallowCopy(); + + EXPECT_DOUBLE_EQ(aCopy->FirstParameter(), 1.0); + EXPECT_DOUBLE_EQ(aCopy->LastParameter(), 9.0); + + const gp_Pnt2d aPntOrig = anOffset.Value(5.0); + const gp_Pnt2d aPntCopy = aCopy->Value(5.0); + EXPECT_TRUE(aPntOrig.IsEqual(aPntCopy, Precision::Confusion())); +} + +TEST_F(Adaptor2d_OffsetCurveTest, Resolution) +{ + Adaptor2d_OffsetCurve anOffset(myBaseLine, 3.0); + const double aRes = anOffset.Resolution(0.001); + EXPECT_GT(aRes, 0.0); +} diff --git a/src/ModelingData/TKG2d/GTests/FILES.cmake b/src/ModelingData/TKG2d/GTests/FILES.cmake index 75a9c27bec..106aeae91e 100644 --- a/src/ModelingData/TKG2d/GTests/FILES.cmake +++ b/src/ModelingData/TKG2d/GTests/FILES.cmake @@ -2,10 +2,23 @@ set(OCCT_TKG2d_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_TKG2d_GTests_FILES + Adaptor2d_Line2d_Test.cxx + Adaptor2d_OffsetCurve_Test.cxx + Geom2d_AxisPlacement_Test.cxx Geom2d_BSplineCurve_Test.cxx Geom2d_BezierCurve_Test.cxx + Geom2d_CartesianPoint_Test.cxx + Geom2d_Circle_Test.cxx Geom2d_CurveEval_Test.cxx + Geom2d_Direction_Test.cxx + Geom2d_Ellipse_Test.cxx + Geom2d_Hyperbola_Test.cxx + Geom2d_Line_Test.cxx Geom2d_OffsetCurve_Test.cxx + Geom2d_Parabola_Test.cxx + Geom2d_Transformation_Test.cxx + Geom2d_TrimmedCurve_Test.cxx + Geom2d_VectorWithMagnitude_Test.cxx Geom2dAdaptor_Curve_Test.cxx Geom2dAPI_InterCurveCurve_Test.cxx Geom2dEval_ArchimedeanSpiralCurve_Test.cxx @@ -22,4 +35,8 @@ set(OCCT_TKG2d_GTests_FILES Geom2dGridEval_Hyperbola_Test.cxx Geom2dGridEval_Parabola_Test.cxx Geom2dHash_CurveHasher_Test.cxx + Geom2dLProp_CLProps2d_Test.cxx + Geom2dLProp_CurAndInf2d_Test.cxx + LProp_AnalyticCurInf_Test.cxx + LProp_CurAndInf_Test.cxx ) diff --git a/src/ModelingData/TKG2d/GTests/Geom2dLProp_CLProps2d_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2dLProp_CLProps2d_Test.cxx new file mode 100644 index 0000000000..ce67fffb21 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2dLProp_CLProps2d_Test.cxx @@ -0,0 +1,231 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class Geom2dLProp_CLProps2dTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Circle centered at origin, radius 5 + gp_Circ2d aCirc(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 5.0); + myCircle = new Geom2d_Circle(aCirc); + + // Line through origin along X + gp_Lin2d aLin(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + myLine = new Geom2d_Line(aLin); + + // Ellipse centered at origin, major radius 10, minor radius 5 + gp_Elips2d anElips(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 10.0, 5.0); + myEllipse = new Geom2d_Ellipse(anElips); + } + + occ::handle myCircle; + occ::handle myLine; + occ::handle myEllipse; +}; + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_Value) +{ + Geom2dLProp_CLProps2d aProps(myCircle, 0.0, 2, Precision::Confusion()); + + const gp_Pnt2d& aPnt = aProps.Value(); + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_SetParameter) +{ + Geom2dLProp_CLProps2d aProps(myCircle, 2, Precision::Confusion()); + + aProps.SetParameter(M_PI / 2.0); + const gp_Pnt2d& aPnt = aProps.Value(); + EXPECT_NEAR(aPnt.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_TangentDefined) +{ + Geom2dLProp_CLProps2d aProps(myCircle, 0.0, 2, Precision::Confusion()); + EXPECT_TRUE(aProps.IsTangentDefined()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_TangentAtZero) +{ + // At U=0 on a circle, the tangent should point in +Y direction + Geom2dLProp_CLProps2d aProps(myCircle, 0.0, 2, Precision::Confusion()); + + gp_Dir2d aTangent; + aProps.Tangent(aTangent); + EXPECT_NEAR(aTangent.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aTangent.Y(), 1.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_Curvature) +{ + // Curvature of a circle of radius R is 1/R + Geom2dLProp_CLProps2d aProps(myCircle, 0.0, 2, Precision::Confusion()); + + const double aCurvature = aProps.Curvature(); + EXPECT_NEAR(aCurvature, 1.0 / 5.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_CurvatureConstant) +{ + // Curvature on a circle should be the same at every point + Geom2dLProp_CLProps2d aProps(myCircle, 2, Precision::Confusion()); + + for (double u = 0.0; u < 2.0 * M_PI; u += M_PI / 4.0) + { + aProps.SetParameter(u); + EXPECT_NEAR(aProps.Curvature(), 1.0 / 5.0, Precision::Confusion()); + } +} + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_Normal) +{ + // Normal at U=0 on a circle centered at origin should point inward (-X) + Geom2dLProp_CLProps2d aProps(myCircle, 0.0, 2, Precision::Confusion()); + + gp_Dir2d aNormal; + aProps.Normal(aNormal); + // Normal should be perpendicular to tangent + gp_Dir2d aTangent; + aProps.Tangent(aTangent); + EXPECT_NEAR(std::abs(aTangent.X() * aNormal.X() + aTangent.Y() * aNormal.Y()), + 0.0, + Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_CentreOfCurvature) +{ + // Centre of curvature of a circle is its center + Geom2dLProp_CLProps2d aProps(myCircle, 0.0, 2, Precision::Confusion()); + + gp_Pnt2d aCenter; + aProps.CentreOfCurvature(aCenter); + EXPECT_NEAR(aCenter.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aCenter.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Circle_CentreOfCurvature_AtPiHalf) +{ + Geom2dLProp_CLProps2d aProps(myCircle, M_PI / 2.0, 2, Precision::Confusion()); + + gp_Pnt2d aCenter; + aProps.CentreOfCurvature(aCenter); + EXPECT_NEAR(aCenter.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aCenter.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Line_TangentDefined) +{ + Geom2dLProp_CLProps2d aProps(myLine, 0.0, 2, Precision::Confusion()); + EXPECT_TRUE(aProps.IsTangentDefined()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Line_Tangent) +{ + Geom2dLProp_CLProps2d aProps(myLine, 5.0, 2, Precision::Confusion()); + + gp_Dir2d aTangent; + aProps.Tangent(aTangent); + EXPECT_NEAR(aTangent.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aTangent.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Line_CurvatureIsZero) +{ + Geom2dLProp_CLProps2d aProps(myLine, 5.0, 2, Precision::Confusion()); + const double aCurvature = aProps.Curvature(); + EXPECT_NEAR(aCurvature, 0.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Line_CentreOfCurvature_Throws) +{ + // Centre of curvature is not defined for a line (zero curvature) + Geom2dLProp_CLProps2d aProps(myLine, 5.0, 2, Precision::Confusion()); + gp_Pnt2d aCenter; + EXPECT_THROW(aProps.CentreOfCurvature(aCenter), LProp_NotDefined); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Ellipse_CurvatureAtMajorVertex) +{ + // At U=0, ellipse is at major vertex. Curvature = b^2/a^3 for a=10, b=5 + // but actually for parametric ellipse, curvature at end of major axis = b^2/(a^2) * (1/a) + // = b^2/a^2 * 1/a ... let's compute it properly: + // Curvature at parameter 0 for ellipse(a,b) = a/b^2 + // Actually: radius of curvature at major vertex = b^2/a, so curvature = a/b^2 + Geom2dLProp_CLProps2d aProps(myEllipse, 0.0, 2, Precision::Confusion()); + + const double aCurvature = aProps.Curvature(); + const double aExpected = 10.0 / (5.0 * 5.0); // a/b^2 = 10/25 = 0.4 + EXPECT_NEAR(aCurvature, aExpected, 1e-6); +} + +TEST_F(Geom2dLProp_CLProps2dTest, Ellipse_CurvatureAtMinorVertex) +{ + // At U=PI/2, ellipse is at minor vertex. Curvature = b/a^2 + Geom2dLProp_CLProps2d aProps(myEllipse, M_PI / 2.0, 2, Precision::Confusion()); + + const double aCurvature = aProps.Curvature(); + const double aExpected = 5.0 / (10.0 * 10.0); // b/a^2 = 5/100 = 0.05 + EXPECT_NEAR(aCurvature, aExpected, 1e-6); +} + +TEST_F(Geom2dLProp_CLProps2dTest, SetCurve) +{ + // Start with empty props, then set curve + Geom2dLProp_CLProps2d aProps(2, Precision::Confusion()); + aProps.SetCurve(myCircle); + aProps.SetParameter(0.0); + + const gp_Pnt2d& aPnt = aProps.Value(); + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, D1_Circle) +{ + Geom2dLProp_CLProps2d aProps(myCircle, 0.0, 2, Precision::Confusion()); + const gp_Vec2d& aD1 = aProps.D1(); + + // First derivative at U=0 on circle(R=5) is (0, 5) + EXPECT_NEAR(aD1.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aD1.Y(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2dLProp_CLProps2dTest, D2_Circle) +{ + Geom2dLProp_CLProps2d aProps(myCircle, 0.0, 2, Precision::Confusion()); + const gp_Vec2d& aD2 = aProps.D2(); + + // Second derivative at U=0 on circle(R=5) is (-5, 0) + EXPECT_NEAR(aD2.X(), -5.0, Precision::Confusion()); + EXPECT_NEAR(aD2.Y(), 0.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2dLProp_CurAndInf2d_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2dLProp_CurAndInf2d_Test.cxx new file mode 100644 index 0000000000..23174bd815 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2dLProp_CurAndInf2d_Test.cxx @@ -0,0 +1,158 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class Geom2dLProp_CurAndInf2dTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Circle centered at origin, radius 5 + gp_Circ2d aCirc(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 5.0); + myCircle = new Geom2d_Circle(aCirc); + + // Ellipse centered at origin, major radius 10, minor radius 3 + gp_Elips2d anElips(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 10.0, 3.0); + myEllipse = new Geom2d_Ellipse(anElips); + } + + occ::handle myCircle; + occ::handle myEllipse; +}; + +TEST_F(Geom2dLProp_CurAndInf2dTest, Circle_Perform_NoInflections) +{ + // A circle has constant curvature: no inflections or extrema + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.Perform(myCircle); + + EXPECT_TRUE(aAnalyzer.IsDone()); + EXPECT_EQ(aAnalyzer.NbPoints(), 0); +} + +TEST_F(Geom2dLProp_CurAndInf2dTest, Circle_PerformInf_NoInflections) +{ + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.PerformInf(myCircle); + + EXPECT_TRUE(aAnalyzer.IsDone()); + EXPECT_EQ(aAnalyzer.NbPoints(), 0); +} + +TEST_F(Geom2dLProp_CurAndInf2dTest, Circle_PerformCurExt_NoExtrema) +{ + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.PerformCurExt(myCircle); + + EXPECT_TRUE(aAnalyzer.IsDone()); + EXPECT_EQ(aAnalyzer.NbPoints(), 0); +} + +TEST_F(Geom2dLProp_CurAndInf2dTest, Ellipse_PerformCurExt_HasExtrema) +{ + // An ellipse has 4 curvature extrema: + // 2 maxima (at ends of minor axis) and 2 minima (at ends of major axis) + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.PerformCurExt(myEllipse); + + EXPECT_TRUE(aAnalyzer.IsDone()); + EXPECT_EQ(aAnalyzer.NbPoints(), 4); +} + +TEST_F(Geom2dLProp_CurAndInf2dTest, Ellipse_PerformCurExt_Types) +{ + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.PerformCurExt(myEllipse); + + ASSERT_TRUE(aAnalyzer.IsDone()); + ASSERT_EQ(aAnalyzer.NbPoints(), 4); + + // Count min and max curvature points + int aNbMin = 0; + int aNbMax = 0; + for (int i = 1; i <= aAnalyzer.NbPoints(); ++i) + { + const LProp_CIType aType = aAnalyzer.Type(i); + if (aType == LProp_MinCur) + ++aNbMin; + else if (aType == LProp_MaxCur) + ++aNbMax; + } + EXPECT_EQ(aNbMin, 2); + EXPECT_EQ(aNbMax, 2); +} + +TEST_F(Geom2dLProp_CurAndInf2dTest, Ellipse_PerformCurExt_Parameters) +{ + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.PerformCurExt(myEllipse); + + ASSERT_TRUE(aAnalyzer.IsDone()); + ASSERT_EQ(aAnalyzer.NbPoints(), 4); + + // Parameters should be sorted in increasing order + for (int i = 1; i < aAnalyzer.NbPoints(); ++i) + { + EXPECT_LT(aAnalyzer.Parameter(i), aAnalyzer.Parameter(i + 1)); + } +} + +TEST_F(Geom2dLProp_CurAndInf2dTest, Ellipse_PerformInf_NoInflections) +{ + // An ellipse is a convex curve, so it has no inflection points + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.PerformInf(myEllipse); + + EXPECT_TRUE(aAnalyzer.IsDone()); + EXPECT_EQ(aAnalyzer.NbPoints(), 0); +} + +TEST_F(Geom2dLProp_CurAndInf2dTest, Ellipse_Perform_CombinedResult) +{ + // Perform computes both inflections and curvature extrema + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.Perform(myEllipse); + + EXPECT_TRUE(aAnalyzer.IsDone()); + // Ellipse: 4 extrema, 0 inflections + EXPECT_EQ(aAnalyzer.NbPoints(), 4); +} + +TEST_F(Geom2dLProp_CurAndInf2dTest, Ellipse_CurvatureExtremaAtExpectedParameters) +{ + // For an ellipse with major axis along X: + // Curvature extrema at U = 0, PI/2, PI, 3*PI/2 + Geom2dLProp_CurAndInf2d aAnalyzer; + aAnalyzer.PerformCurExt(myEllipse); + + ASSERT_TRUE(aAnalyzer.IsDone()); + ASSERT_EQ(aAnalyzer.NbPoints(), 4); + + const double anExpectedParams[] = {0.0, M_PI / 2.0, M_PI, 3.0 * M_PI / 2.0}; + for (int i = 1; i <= 4; ++i) + { + EXPECT_NEAR(aAnalyzer.Parameter(i), anExpectedParams[i - 1], 1e-6); + } +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_AxisPlacement_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_AxisPlacement_Test.cxx new file mode 100644 index 0000000000..7700d17a7b --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_AxisPlacement_Test.cxx @@ -0,0 +1,185 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +TEST(Geom2d_AxisPlacementTest, ConstructFromAx2d) +{ + gp_Ax2d anAxis(gp_Pnt2d(1.0, 2.0), gp_Dir2d(1.0, 0.0)); + occ::handle anAP = new Geom2d_AxisPlacement(anAxis); + + EXPECT_NEAR(anAP->Location().X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Location().Y(), 2.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Direction().X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Direction().Y(), 0.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, ConstructFromPointAndDir) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(3.0, 4.0), gp_Dir2d(0.0, 1.0)); + + EXPECT_NEAR(anAP->Location().X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Direction().Y(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, SetLocation) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + anAP->SetLocation(gp_Pnt2d(5.0, 6.0)); + + EXPECT_NEAR(anAP->Location().X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Location().Y(), 6.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, SetDirection) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + anAP->SetDirection(gp_Dir2d(0.0, 1.0)); + + EXPECT_NEAR(anAP->Direction().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Direction().Y(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, SetAxis) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + + gp_Ax2d aNewAxis(gp_Pnt2d(10.0, 20.0), gp_Dir2d(0.0, 1.0)); + anAP->SetAxis(aNewAxis); + + EXPECT_NEAR(anAP->Location().X(), 10.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Direction().Y(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Ax2d) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(1.0, 2.0), gp_Dir2d(1.0, 0.0)); + + const gp_Ax2d anAxis = anAP->Ax2d(); + EXPECT_NEAR(anAxis.Location().X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(anAxis.Direction().X(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Angle_Perpendicular) +{ + occ::handle anAP1 = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + occ::handle anAP2 = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(0.0, 1.0)); + + EXPECT_NEAR(anAP1->Angle(anAP2), M_PI / 2.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Angle_Parallel) +{ + occ::handle anAP1 = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + occ::handle anAP2 = + new Geom2d_AxisPlacement(gp_Pnt2d(5.0, 5.0), gp_Dir2d(1.0, 0.0)); + + EXPECT_NEAR(anAP1->Angle(anAP2), 0.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Angle_Opposite) +{ + occ::handle anAP1 = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + occ::handle anAP2 = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(-1.0, 0.0)); + + EXPECT_NEAR(std::abs(anAP1->Angle(anAP2)), M_PI, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Reverse) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + anAP->Reverse(); + + EXPECT_NEAR(anAP->Direction().X(), -1.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Direction().Y(), 0.0, Precision::Confusion()); + // Location unchanged + EXPECT_NEAR(anAP->Location().X(), 0.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Reversed) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + occ::handle aReversed = anAP->Reversed(); + + EXPECT_NEAR(aReversed->Direction().X(), -1.0, Precision::Confusion()); + // Original unchanged + EXPECT_NEAR(anAP->Direction().X(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Transform_Translation) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(1.0, 2.0), gp_Dir2d(1.0, 0.0)); + + gp_Trsf2d aTrsf; + aTrsf.SetTranslation(gp_Vec2d(10.0, 20.0)); + anAP->Transform(aTrsf); + + EXPECT_NEAR(anAP->Location().X(), 11.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Location().Y(), 22.0, Precision::Confusion()); + // Direction unchanged + EXPECT_NEAR(anAP->Direction().X(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Transform_Rotation) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(1.0, 0.0), gp_Dir2d(1.0, 0.0)); + + gp_Trsf2d aTrsf; + aTrsf.SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0); + anAP->Transform(aTrsf); + + EXPECT_NEAR(anAP->Location().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Location().Y(), 1.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Direction().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(anAP->Direction().Y(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_AxisPlacementTest, Copy) +{ + occ::handle anAP = + new Geom2d_AxisPlacement(gp_Pnt2d(1.0, 2.0), gp_Dir2d(0.0, 1.0)); + occ::handle aCopyGeom = anAP->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->Location().X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aCopy->Direction().Y(), 1.0, Precision::Confusion()); + + // Verify independence + aCopy->SetLocation(gp_Pnt2d(99.0, 99.0)); + EXPECT_NEAR(anAP->Location().X(), 1.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_CartesianPoint_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_CartesianPoint_Test.cxx new file mode 100644 index 0000000000..b0a7fcff8c --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_CartesianPoint_Test.cxx @@ -0,0 +1,136 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include + +#include + +#include + +TEST(Geom2d_CartesianPointTest, ConstructFromCoords) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(3.0, 4.0); + EXPECT_NEAR(aPnt->X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(aPnt->Y(), 4.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, ConstructFromPnt2d) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(gp_Pnt2d(5.0, 6.0)); + EXPECT_NEAR(aPnt->X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt->Y(), 6.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, SetCoord) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(0.0, 0.0); + aPnt->SetCoord(7.0, 8.0); + EXPECT_NEAR(aPnt->X(), 7.0, Precision::Confusion()); + EXPECT_NEAR(aPnt->Y(), 8.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, SetPnt2d) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(0.0, 0.0); + aPnt->SetPnt2d(gp_Pnt2d(1.0, 2.0)); + EXPECT_NEAR(aPnt->X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aPnt->Y(), 2.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, SetX_SetY) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(0.0, 0.0); + aPnt->SetX(10.0); + aPnt->SetY(20.0); + EXPECT_NEAR(aPnt->X(), 10.0, Precision::Confusion()); + EXPECT_NEAR(aPnt->Y(), 20.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, Coord) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(3.0, 4.0); + double aX = 0.0, aY = 0.0; + aPnt->Coord(aX, aY); + EXPECT_NEAR(aX, 3.0, Precision::Confusion()); + EXPECT_NEAR(aY, 4.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, Pnt2d) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(3.0, 4.0); + const gp_Pnt2d aGpPnt = aPnt->Pnt2d(); + EXPECT_NEAR(aGpPnt.X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(aGpPnt.Y(), 4.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, Distance) +{ + occ::handle aP1 = new Geom2d_CartesianPoint(0.0, 0.0); + occ::handle aP2 = new Geom2d_CartesianPoint(3.0, 4.0); + EXPECT_NEAR(aP1->Distance(aP2), 5.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, SquareDistance) +{ + occ::handle aP1 = new Geom2d_CartesianPoint(0.0, 0.0); + occ::handle aP2 = new Geom2d_CartesianPoint(3.0, 4.0); + EXPECT_NEAR(aP1->SquareDistance(aP2), 25.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, Distance_SamePoint) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(5.0, 5.0); + EXPECT_NEAR(aPnt->Distance(aPnt), 0.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, Transform_Translation) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(1.0, 2.0); + + gp_Trsf2d aTrsf; + aTrsf.SetTranslation(gp_Vec2d(3.0, 4.0)); + aPnt->Transform(aTrsf); + + EXPECT_NEAR(aPnt->X(), 4.0, Precision::Confusion()); + EXPECT_NEAR(aPnt->Y(), 6.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, Transform_Rotation) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(1.0, 0.0); + + gp_Trsf2d aTrsf; + aTrsf.SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0); + aPnt->Transform(aTrsf); + + EXPECT_NEAR(aPnt->X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aPnt->Y(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_CartesianPointTest, Copy) +{ + occ::handle aPnt = new Geom2d_CartesianPoint(3.0, 4.0); + occ::handle aCopyGeom = aPnt->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(aCopy->Y(), 4.0, Precision::Confusion()); + + aCopy->SetCoord(0.0, 0.0); + EXPECT_NEAR(aPnt->X(), 3.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_Circle_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_Circle_Test.cxx new file mode 100644 index 0000000000..ca2f8c7070 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_Circle_Test.cxx @@ -0,0 +1,177 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class Geom2d_CircleTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Circle centered at origin, radius 5 + gp_Circ2d aCirc(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 5.0); + myCircle = new Geom2d_Circle(aCirc); + } + + occ::handle myCircle; +}; + +TEST_F(Geom2d_CircleTest, ConstructFromCirc2d) +{ + EXPECT_NEAR(myCircle->Radius(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, ConstructFromAxisAndRadius) +{ + occ::handle aCircle = + new Geom2d_Circle(gp_Ax2d(gp_Pnt2d(1.0, 2.0), gp_Dir2d(1.0, 0.0)), 3.0); + + EXPECT_NEAR(aCircle->Radius(), 3.0, Precision::Confusion()); + EXPECT_NEAR(aCircle->Location().X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aCircle->Location().Y(), 2.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, ConstructFromAx22d) +{ + gp_Ax22d anAxis(gp_Pnt2d(3.0, 4.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0)); + occ::handle aCircle = new Geom2d_Circle(anAxis, 7.0); + + EXPECT_NEAR(aCircle->Radius(), 7.0, Precision::Confusion()); + EXPECT_NEAR(aCircle->Location().X(), 3.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, ParameterBounds) +{ + EXPECT_DOUBLE_EQ(myCircle->FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(myCircle->LastParameter(), 2.0 * M_PI); +} + +TEST_F(Geom2d_CircleTest, IsClosedAndPeriodic) +{ + EXPECT_TRUE(myCircle->IsClosed()); + EXPECT_TRUE(myCircle->IsPeriodic()); +} + +TEST_F(Geom2d_CircleTest, Eccentricity) +{ + EXPECT_NEAR(myCircle->Eccentricity(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, SetRadius) +{ + myCircle->SetRadius(10.0); + EXPECT_NEAR(myCircle->Radius(), 10.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, Circ2d) +{ + const gp_Circ2d aCirc = myCircle->Circ2d(); + EXPECT_NEAR(aCirc.Radius(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, EvalD0_AtZero) +{ + const gp_Pnt2d aPnt = myCircle->EvalD0(0.0); + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, EvalD0_AtPiHalf) +{ + const gp_Pnt2d aPnt = myCircle->EvalD0(M_PI / 2.0); + EXPECT_NEAR(aPnt.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, EvalD0_AtPi) +{ + const gp_Pnt2d aPnt = myCircle->EvalD0(M_PI); + EXPECT_NEAR(aPnt.X(), -5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, EvalD1_AtZero) +{ + const auto [aPnt, aD1] = myCircle->EvalD1(0.0); + // Tangent at U=0 is (0, R) = (0, 5) + EXPECT_NEAR(aD1.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aD1.Y(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, EvalD2_AtZero) +{ + const auto [aPnt, aD1, aD2] = myCircle->EvalD2(0.0); + // Second derivative at U=0 is (-R, 0) = (-5, 0) + EXPECT_NEAR(aD2.X(), -5.0, Precision::Confusion()); + EXPECT_NEAR(aD2.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, EvalDN_Order1) +{ + const gp_Vec2d aD1 = myCircle->EvalDN(0.0, 1); + EXPECT_NEAR(aD1.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aD1.Y(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, ReversedParameter) +{ + const double aU = M_PI / 3.0; + const double aRev = myCircle->ReversedParameter(aU); + EXPECT_NEAR(aRev, 2.0 * M_PI - aU, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, Copy) +{ + occ::handle aCopyGeom = myCircle->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->Radius(), 5.0, Precision::Confusion()); + + // Verify independence + aCopy->SetRadius(1.0); + EXPECT_NEAR(myCircle->Radius(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, Transform_Translation) +{ + gp_Trsf2d aTrsf; + aTrsf.SetTranslation(gp_Vec2d(10.0, 20.0)); + myCircle->Transform(aTrsf); + + EXPECT_NEAR(myCircle->Location().X(), 10.0, Precision::Confusion()); + EXPECT_NEAR(myCircle->Location().Y(), 20.0, Precision::Confusion()); + EXPECT_NEAR(myCircle->Radius(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_CircleTest, PointOnCircle_DistanceFromCenter) +{ + // All points on circle should be at distance R from center + for (double u = 0.0; u < 2.0 * M_PI; u += M_PI / 6.0) + { + const gp_Pnt2d aPnt = myCircle->EvalD0(u); + const double aDist = aPnt.Distance(myCircle->Location()); + EXPECT_NEAR(aDist, 5.0, Precision::Confusion()); + } +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_Direction_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_Direction_Test.cxx new file mode 100644 index 0000000000..2f9f79c4e4 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_Direction_Test.cxx @@ -0,0 +1,159 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include + +#include + +#include + +TEST(Geom2d_DirectionTest, ConstructFromCoords) +{ + occ::handle aDir = new Geom2d_Direction(3.0, 4.0); + // Should be normalized + EXPECT_NEAR(aDir->Magnitude(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aDir->X(), 0.6, Precision::Confusion()); + EXPECT_NEAR(aDir->Y(), 0.8, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, ConstructFromDir2d) +{ + occ::handle aDir = new Geom2d_Direction(gp_Dir2d(0.0, 1.0)); + EXPECT_NEAR(aDir->X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aDir->Y(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, ConstructFromZero_Throws) +{ +#ifndef No_Exception + EXPECT_THROW(new Geom2d_Direction(0.0, 0.0), Standard_ConstructionError); +#else + GTEST_SKIP() << "No_Exception is defined; exception behavior is disabled in this build."; +#endif +} + +TEST(Geom2d_DirectionTest, MagnitudeAlwaysOne) +{ + occ::handle aDir = new Geom2d_Direction(100.0, 200.0); + EXPECT_NEAR(aDir->Magnitude(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aDir->SquareMagnitude(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, SetCoord) +{ + occ::handle aDir = new Geom2d_Direction(1.0, 0.0); + aDir->SetCoord(0.0, 5.0); + EXPECT_NEAR(aDir->X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aDir->Y(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, SetCoord_Zero_Throws) +{ +#ifndef No_Exception + occ::handle aDir = new Geom2d_Direction(1.0, 0.0); + EXPECT_THROW(aDir->SetCoord(0.0, 0.0), Standard_ConstructionError); +#else + GTEST_SKIP() << "No_Exception is defined; exception behavior is disabled in this build."; +#endif +} + +TEST(Geom2d_DirectionTest, Dir2d) +{ + occ::handle aDir = new Geom2d_Direction(1.0, 0.0); + const gp_Dir2d aGpDir = aDir->Dir2d(); + EXPECT_NEAR(aGpDir.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aGpDir.Y(), 0.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, Crossed) +{ + occ::handle aDirX = new Geom2d_Direction(1.0, 0.0); + occ::handle aDirY = new Geom2d_Direction(0.0, 1.0); + + // (1,0) x (0,1) = 1 + EXPECT_NEAR(aDirX->Crossed(aDirY), 1.0, Precision::Confusion()); + // (0,1) x (1,0) = -1 + EXPECT_NEAR(aDirY->Crossed(aDirX), -1.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, Angle) +{ + occ::handle aDirX = new Geom2d_Direction(1.0, 0.0); + occ::handle aDirY = new Geom2d_Direction(0.0, 1.0); + + EXPECT_NEAR(aDirX->Angle(aDirY), M_PI / 2.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, Angle_OppositeDirections) +{ + occ::handle aDirPos = new Geom2d_Direction(1.0, 0.0); + occ::handle aDirNeg = new Geom2d_Direction(-1.0, 0.0); + + EXPECT_NEAR(std::abs(aDirPos->Angle(aDirNeg)), M_PI, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, Dot) +{ + occ::handle aDirX = new Geom2d_Direction(1.0, 0.0); + occ::handle aDirY = new Geom2d_Direction(0.0, 1.0); + + EXPECT_NEAR(aDirX->Dot(aDirY), 0.0, Precision::Confusion()); + EXPECT_NEAR(aDirX->Dot(aDirX), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, Reverse) +{ + occ::handle aDir = new Geom2d_Direction(1.0, 0.0); + aDir->Reverse(); + EXPECT_NEAR(aDir->X(), -1.0, Precision::Confusion()); + EXPECT_NEAR(aDir->Y(), 0.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, CrossedWithVectorWithMagnitude) +{ + occ::handle aDir = new Geom2d_Direction(1.0, 0.0); + occ::handle aVec = new Geom2d_VectorWithMagnitude(0.0, 5.0); + + EXPECT_NEAR(aDir->Crossed(aVec), 5.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, Copy) +{ + occ::handle aDir = new Geom2d_Direction(1.0, 0.0); + occ::handle aCopyGeom = aDir->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->X(), 1.0, Precision::Confusion()); + + aCopy->SetCoord(0.0, 1.0); + EXPECT_NEAR(aDir->X(), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_DirectionTest, Transform_Rotation) +{ + gp_Trsf2d aTrsf; + aTrsf.SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0); + + occ::handle aDir = new Geom2d_Direction(1.0, 0.0); + aDir->Transform(aTrsf); + + EXPECT_NEAR(aDir->X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aDir->Y(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aDir->Magnitude(), 1.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_Ellipse_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_Ellipse_Test.cxx new file mode 100644 index 0000000000..9cb311a2c5 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_Ellipse_Test.cxx @@ -0,0 +1,173 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class Geom2d_EllipseTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Ellipse centered at origin, major radius 10, minor radius 5 + gp_Elips2d anElips(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 10.0, 5.0); + myEllipse = new Geom2d_Ellipse(anElips); + } + + occ::handle myEllipse; +}; + +TEST_F(Geom2d_EllipseTest, Radii) +{ + EXPECT_NEAR(myEllipse->MajorRadius(), 10.0, Precision::Confusion()); + EXPECT_NEAR(myEllipse->MinorRadius(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, ParameterBounds) +{ + EXPECT_DOUBLE_EQ(myEllipse->FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(myEllipse->LastParameter(), 2.0 * M_PI); +} + +TEST_F(Geom2d_EllipseTest, IsClosedAndPeriodic) +{ + EXPECT_TRUE(myEllipse->IsClosed()); + EXPECT_TRUE(myEllipse->IsPeriodic()); +} + +TEST_F(Geom2d_EllipseTest, Eccentricity) +{ + // e = sqrt(1 - (b/a)^2) = sqrt(1 - 0.25) = sqrt(0.75) + const double anExpected = std::sqrt(1.0 - (5.0 * 5.0) / (10.0 * 10.0)); + EXPECT_NEAR(myEllipse->Eccentricity(), anExpected, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, Focal) +{ + // Focal distance = sqrt(a^2 - b^2) * 2 + const double anExpected = 2.0 * std::sqrt(10.0 * 10.0 - 5.0 * 5.0); + EXPECT_NEAR(myEllipse->Focal(), anExpected, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, Foci) +{ + const gp_Pnt2d aFocus1 = myEllipse->Focus1(); + const gp_Pnt2d aFocus2 = myEllipse->Focus2(); + const double c = std::sqrt(100.0 - 25.0); // sqrt(a^2 - b^2) + + EXPECT_NEAR(aFocus1.X(), c, Precision::Confusion()); + EXPECT_NEAR(aFocus1.Y(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aFocus2.X(), -c, Precision::Confusion()); + EXPECT_NEAR(aFocus2.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, Parameter) +{ + // Semi-latus rectum = b^2/a = 25/10 = 2.5 + EXPECT_NEAR(myEllipse->Parameter(), 2.5, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, EvalD0_MajorVertex) +{ + // At U=0: point on major axis = (a, 0) = (10, 0) + const gp_Pnt2d aPnt = myEllipse->EvalD0(0.0); + EXPECT_NEAR(aPnt.X(), 10.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, EvalD0_MinorVertex) +{ + // At U=PI/2: point on minor axis = (0, b) = (0, 5) + const gp_Pnt2d aPnt = myEllipse->EvalD0(M_PI / 2.0); + EXPECT_NEAR(aPnt.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, EvalD1_AtZero) +{ + const auto [aPnt, aD1] = myEllipse->EvalD1(0.0); + // At U=0: tangent is (0, b) = (0, 5) + EXPECT_NEAR(aD1.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aD1.Y(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, EvalD2_AtZero) +{ + const auto [aPnt, aD1, aD2] = myEllipse->EvalD2(0.0); + // At U=0: second derivative is (-a, 0) = (-10, 0) + EXPECT_NEAR(aD2.X(), -10.0, Precision::Confusion()); + EXPECT_NEAR(aD2.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, SetMajorRadius) +{ + myEllipse->SetMajorRadius(20.0); + EXPECT_NEAR(myEllipse->MajorRadius(), 20.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, SetMinorRadius) +{ + myEllipse->SetMinorRadius(3.0); + EXPECT_NEAR(myEllipse->MinorRadius(), 3.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, PointOnEllipse_FociDistanceSum) +{ + // For any point on ellipse: d(P, F1) + d(P, F2) = 2a + const gp_Pnt2d aF1 = myEllipse->Focus1(); + const gp_Pnt2d aF2 = myEllipse->Focus2(); + + for (double u = 0.0; u < 2.0 * M_PI; u += M_PI / 6.0) + { + const gp_Pnt2d aPnt = myEllipse->EvalD0(u); + const double aSum = aPnt.Distance(aF1) + aPnt.Distance(aF2); + EXPECT_NEAR(aSum, 2.0 * 10.0, Precision::Confusion()); + } +} + +TEST_F(Geom2d_EllipseTest, Copy) +{ + occ::handle aCopyGeom = myEllipse->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->MajorRadius(), 10.0, Precision::Confusion()); + EXPECT_NEAR(aCopy->MinorRadius(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, Transform_Translation) +{ + gp_Trsf2d aTrsf; + aTrsf.SetTranslation(gp_Vec2d(5.0, 10.0)); + myEllipse->Transform(aTrsf); + + EXPECT_NEAR(myEllipse->Location().X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(myEllipse->Location().Y(), 10.0, Precision::Confusion()); + EXPECT_NEAR(myEllipse->MajorRadius(), 10.0, Precision::Confusion()); +} + +TEST_F(Geom2d_EllipseTest, ReversedParameter) +{ + const double aU = M_PI / 4.0; + EXPECT_NEAR(myEllipse->ReversedParameter(aU), 2.0 * M_PI - aU, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_Hyperbola_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_Hyperbola_Test.cxx new file mode 100644 index 0000000000..f2d6c9a019 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_Hyperbola_Test.cxx @@ -0,0 +1,152 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class Geom2d_HyperbolaTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Hyperbola centered at origin, a=5, b=3 + gp_Hypr2d aHypr(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 5.0, 3.0); + myHyperbola = new Geom2d_Hyperbola(aHypr); + } + + occ::handle myHyperbola; +}; + +TEST_F(Geom2d_HyperbolaTest, Radii) +{ + EXPECT_NEAR(myHyperbola->MajorRadius(), 5.0, Precision::Confusion()); + EXPECT_NEAR(myHyperbola->MinorRadius(), 3.0, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, IsNotClosedNotPeriodic) +{ + EXPECT_FALSE(myHyperbola->IsClosed()); + EXPECT_FALSE(myHyperbola->IsPeriodic()); +} + +TEST_F(Geom2d_HyperbolaTest, Eccentricity) +{ + // e = c/a where c = sqrt(a^2 + b^2) = sqrt(25+9) = sqrt(34) + const double anExpected = std::sqrt(34.0) / 5.0; + EXPECT_NEAR(myHyperbola->Eccentricity(), anExpected, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, Focal) +{ + // 2c = 2*sqrt(a^2 + b^2) + const double anExpected = 2.0 * std::sqrt(25.0 + 9.0); + EXPECT_NEAR(myHyperbola->Focal(), anExpected, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, Foci) +{ + const double c = std::sqrt(25.0 + 9.0); + const gp_Pnt2d aF1 = myHyperbola->Focus1(); + const gp_Pnt2d aF2 = myHyperbola->Focus2(); + + EXPECT_NEAR(aF1.X(), c, Precision::Confusion()); + EXPECT_NEAR(aF2.X(), -c, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, Parameter) +{ + // Semi-latus rectum = b^2/a = 9/5 = 1.8 + EXPECT_NEAR(myHyperbola->Parameter(), 1.8, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, EvalD0_AtZero) +{ + // At U=0: vertex = (a, 0) = (5, 0) since x = a*cosh(0) = a, y = b*sinh(0) = 0 + const gp_Pnt2d aPnt = myHyperbola->EvalD0(0.0); + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, EvalD1_AtZero) +{ + const auto [aPnt, aD1] = myHyperbola->EvalD1(0.0); + // At U=0: dx/du = a*sinh(0) = 0, dy/du = b*cosh(0) = b = 3 + EXPECT_NEAR(aD1.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aD1.Y(), 3.0, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, PointOnHyperbola_FociDistanceDifference) +{ + // For any point on hyperbola: |d(P,F1) - d(P,F2)| = 2a + const gp_Pnt2d aF1 = myHyperbola->Focus1(); + const gp_Pnt2d aF2 = myHyperbola->Focus2(); + + for (double u = -2.0; u <= 2.0; u += 0.5) + { + const gp_Pnt2d aPnt = myHyperbola->EvalD0(u); + const double aDiff = std::abs(aPnt.Distance(aF1) - aPnt.Distance(aF2)); + EXPECT_NEAR(aDiff, 2.0 * 5.0, Precision::Confusion()); + } +} + +TEST_F(Geom2d_HyperbolaTest, SetMajorRadius) +{ + myHyperbola->SetMajorRadius(8.0); + EXPECT_NEAR(myHyperbola->MajorRadius(), 8.0, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, SetMinorRadius) +{ + myHyperbola->SetMinorRadius(4.0); + EXPECT_NEAR(myHyperbola->MinorRadius(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, Copy) +{ + occ::handle aCopyGeom = myHyperbola->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->MajorRadius(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aCopy->MinorRadius(), 3.0, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, Transform_Translation) +{ + gp_Trsf2d aTrsf; + aTrsf.SetTranslation(gp_Vec2d(10.0, 20.0)); + myHyperbola->Transform(aTrsf); + + EXPECT_NEAR(myHyperbola->Location().X(), 10.0, Precision::Confusion()); + EXPECT_NEAR(myHyperbola->Location().Y(), 20.0, Precision::Confusion()); +} + +TEST_F(Geom2d_HyperbolaTest, Asymptotes) +{ + const gp_Ax2d anAsym1 = myHyperbola->Asymptote1(); + const gp_Ax2d anAsym2 = myHyperbola->Asymptote2(); + + // Asymptotes pass through center + EXPECT_NEAR(anAsym1.Location().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(anAsym2.Location().X(), 0.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_Line_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_Line_Test.cxx new file mode 100644 index 0000000000..7e3ea95963 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_Line_Test.cxx @@ -0,0 +1,188 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class Geom2d_LineTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Horizontal line through origin + myLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + } + + occ::handle myLine; +}; + +TEST_F(Geom2d_LineTest, ConstructFromPointAndDir) +{ + EXPECT_NEAR(myLine->Direction().X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(myLine->Direction().Y(), 0.0, Precision::Confusion()); + EXPECT_NEAR(myLine->Location().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(myLine->Location().Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, ConstructFromLin2d) +{ + gp_Lin2d aLin(gp_Pnt2d(1.0, 2.0), gp_Dir2d(0.0, 1.0)); + occ::handle aLine = new Geom2d_Line(aLin); + + EXPECT_NEAR(aLine->Direction().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aLine->Direction().Y(), 1.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, ConstructFromAxis) +{ + gp_Ax2d anAxis(gp_Pnt2d(3.0, 4.0), gp_Dir2d(1.0, 0.0)); + occ::handle aLine = new Geom2d_Line(anAxis); + + EXPECT_NEAR(aLine->Location().X(), 3.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, ParameterBounds) +{ + // OCCT uses large finite bounds (not numeric_limits::max) + EXPECT_LT(myLine->FirstParameter(), -1e+90); + EXPECT_GT(myLine->LastParameter(), 1e+90); +} + +TEST_F(Geom2d_LineTest, IsNotClosedNotPeriodic) +{ + EXPECT_FALSE(myLine->IsClosed()); + EXPECT_FALSE(myLine->IsPeriodic()); +} + +TEST_F(Geom2d_LineTest, Continuity) +{ + EXPECT_EQ(myLine->Continuity(), GeomAbs_CN); + EXPECT_TRUE(myLine->IsCN(100)); +} + +TEST_F(Geom2d_LineTest, EvalD0) +{ + const gp_Pnt2d aPnt = myLine->EvalD0(5.0); + EXPECT_NEAR(aPnt.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, EvalD1_DirectionIsConstant) +{ + const auto [aPnt, aD1] = myLine->EvalD1(5.0); + EXPECT_NEAR(aD1.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aD1.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, EvalD2_IsZero) +{ + const auto [aPnt, aD1, aD2] = myLine->EvalD2(5.0); + EXPECT_NEAR(aD2.Magnitude(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, EvalD3_IsZero) +{ + const auto [aPnt, aD1, aD2, aD3] = myLine->EvalD3(5.0); + EXPECT_NEAR(aD3.Magnitude(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, EvalDN_HigherOrder) +{ + const gp_Vec2d aD4 = myLine->EvalDN(0.0, 4); + EXPECT_NEAR(aD4.Magnitude(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, Distance) +{ + EXPECT_NEAR(myLine->Distance(gp_Pnt2d(5.0, 3.0)), 3.0, Precision::Confusion()); + EXPECT_NEAR(myLine->Distance(gp_Pnt2d(0.0, 0.0)), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, SetDirection) +{ + myLine->SetDirection(gp_Dir2d(0.0, 1.0)); + EXPECT_NEAR(myLine->Direction().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(myLine->Direction().Y(), 1.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, SetLocation) +{ + myLine->SetLocation(gp_Pnt2d(3.0, 4.0)); + EXPECT_NEAR(myLine->Location().X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(myLine->Location().Y(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, Lin2d) +{ + const gp_Lin2d aLin = myLine->Lin2d(); + EXPECT_NEAR(aLin.Direction().X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aLin.Location().X(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, Copy) +{ + occ::handle aCopyGeom = myLine->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->Direction().X(), 1.0, Precision::Confusion()); + + aCopy->SetDirection(gp_Dir2d(0.0, 1.0)); + EXPECT_NEAR(myLine->Direction().X(), 1.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, Transform_Translation) +{ + gp_Trsf2d aTrsf; + aTrsf.SetTranslation(gp_Vec2d(5.0, 10.0)); + myLine->Transform(aTrsf); + + EXPECT_NEAR(myLine->Location().X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(myLine->Location().Y(), 10.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, Transform_Rotation) +{ + gp_Trsf2d aTrsf; + aTrsf.SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0); + myLine->Transform(aTrsf); + + // Horizontal line rotated 90 degrees becomes vertical + EXPECT_NEAR(myLine->Direction().X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(std::abs(myLine->Direction().Y()), 1.0, Precision::Confusion()); +} + +TEST_F(Geom2d_LineTest, ReversedParameter) +{ + EXPECT_DOUBLE_EQ(myLine->ReversedParameter(5.0), -5.0); +} + +TEST_F(Geom2d_LineTest, DiagonalLine_Value) +{ + occ::handle aDiag = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 1.0)); + + const gp_Pnt2d aPnt = aDiag->EvalD0(std::sqrt(2.0)); + EXPECT_NEAR(aPnt.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 1.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_Parabola_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_Parabola_Test.cxx new file mode 100644 index 0000000000..b3f771850d --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_Parabola_Test.cxx @@ -0,0 +1,141 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class Geom2d_ParabolaTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Parabola centered at origin with focal distance 4 + gp_Parab2d aParab(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 4.0); + myParabola = new Geom2d_Parabola(aParab); + } + + occ::handle myParabola; +}; + +TEST_F(Geom2d_ParabolaTest, Focal) +{ + EXPECT_NEAR(myParabola->Focal(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, IsNotClosedNotPeriodic) +{ + EXPECT_FALSE(myParabola->IsClosed()); + EXPECT_FALSE(myParabola->IsPeriodic()); +} + +TEST_F(Geom2d_ParabolaTest, Eccentricity) +{ + EXPECT_NEAR(myParabola->Eccentricity(), 1.0, Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, Focus) +{ + const gp_Pnt2d aFocus = myParabola->Focus(); + EXPECT_NEAR(aFocus.X(), 4.0, Precision::Confusion()); + EXPECT_NEAR(aFocus.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, Parameter) +{ + // p = 2*Focal = 8 + EXPECT_NEAR(myParabola->Parameter(), 8.0, Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, EvalD0_AtZero) +{ + // At U=0: vertex + const gp_Pnt2d aPnt = myParabola->EvalD0(0.0); + EXPECT_NEAR(aPnt.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aPnt.Y(), 0.0, Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, EvalD0_Symmetric) +{ + // Parabola is symmetric: P(U) and P(-U) should have same X, opposite Y + const double aU = 3.0; + const gp_Pnt2d aPntPos = myParabola->EvalD0(aU); + const gp_Pnt2d aPntNeg = myParabola->EvalD0(-aU); + + EXPECT_NEAR(aPntPos.X(), aPntNeg.X(), Precision::Confusion()); + EXPECT_NEAR(aPntPos.Y(), -aPntNeg.Y(), Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, EvalD1_AtZero) +{ + const auto [aPnt, aD1] = myParabola->EvalD1(0.0); + // At vertex: tangent is along Y axis + EXPECT_NEAR(aD1.X(), 0.0, Precision::Confusion()); + EXPECT_GT(aD1.Magnitude(), Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, EvalD2_AtZero) +{ + const auto [aPnt, aD1, aD2] = myParabola->EvalD2(0.0); + // Second derivative at vertex should be along X (opening direction) + EXPECT_GT(aD2.X(), 0.0); +} + +TEST_F(Geom2d_ParabolaTest, PointOnParabola_FocusDirectrixProperty) +{ + // For any point P on parabola: distance(P, Focus) = distance(P, Directrix) + const gp_Pnt2d aFocus = myParabola->Focus(); + const gp_Ax2d aDirectrix = myParabola->Directrix(); + const gp_Lin2d aDirLine(aDirectrix); + + for (double u = -5.0; u <= 5.0; u += 1.0) + { + const gp_Pnt2d aPnt = myParabola->EvalD0(u); + const double aDistFocus = aPnt.Distance(aFocus); + const double aDistDir = aDirLine.Distance(aPnt); + EXPECT_NEAR(aDistFocus, aDistDir, 1e-6); + } +} + +TEST_F(Geom2d_ParabolaTest, SetFocal) +{ + myParabola->SetFocal(10.0); + EXPECT_NEAR(myParabola->Focal(), 10.0, Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, Copy) +{ + occ::handle aCopyGeom = myParabola->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->Focal(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_ParabolaTest, Transform_Translation) +{ + gp_Trsf2d aTrsf; + aTrsf.SetTranslation(gp_Vec2d(5.0, 10.0)); + myParabola->Transform(aTrsf); + + EXPECT_NEAR(myParabola->Location().X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(myParabola->Location().Y(), 10.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_Transformation_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_Transformation_Test.cxx new file mode 100644 index 0000000000..e6aa62f5d9 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_Transformation_Test.cxx @@ -0,0 +1,284 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +TEST(Geom2d_TransformationTest, DefaultConstructor_Identity) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + + EXPECT_EQ(aTrsf->Form(), gp_Identity); + EXPECT_NEAR(aTrsf->ScaleFactor(), 1.0, Precision::Confusion()); + EXPECT_FALSE(aTrsf->IsNegative()); +} + +TEST(Geom2d_TransformationTest, ConstructFromTrsf2d) +{ + gp_Trsf2d aGpTrsf; + aGpTrsf.SetTranslation(gp_Vec2d(3.0, 4.0)); + + occ::handle aTrsf = new Geom2d_Transformation(aGpTrsf); + EXPECT_EQ(aTrsf->Form(), gp_Translation); +} + +TEST(Geom2d_TransformationTest, SetTranslation_Vector) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetTranslation(gp_Vec2d(5.0, 10.0)); + + EXPECT_EQ(aTrsf->Form(), gp_Translation); + + double aX = 1.0, aY = 2.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 6.0, Precision::Confusion()); + EXPECT_NEAR(aY, 12.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, SetTranslation_TwoPoints) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetTranslation(gp_Pnt2d(1.0, 1.0), gp_Pnt2d(4.0, 5.0)); + + double aX = 0.0, aY = 0.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 3.0, Precision::Confusion()); + EXPECT_NEAR(aY, 4.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, SetRotation) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0); + + EXPECT_EQ(aTrsf->Form(), gp_Rotation); + + double aX = 1.0, aY = 0.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 0.0, Precision::Confusion()); + EXPECT_NEAR(aY, 1.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, SetRotation_180Degrees) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetRotation(gp_Pnt2d(0.0, 0.0), M_PI); + + double aX = 3.0, aY = 4.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, -3.0, Precision::Confusion()); + EXPECT_NEAR(aY, -4.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, SetScale) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetScale(gp_Pnt2d(0.0, 0.0), 2.0); + + EXPECT_EQ(aTrsf->Form(), gp_Scale); + EXPECT_NEAR(aTrsf->ScaleFactor(), 2.0, Precision::Confusion()); + + double aX = 3.0, aY = 4.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 6.0, Precision::Confusion()); + EXPECT_NEAR(aY, 8.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, SetScale_Negative) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetScale(gp_Pnt2d(0.0, 0.0), -1.0); + + // In 2D, IsNegative checks determinant sign; scale(-1) has det = (-1)*(-1) = 1 > 0 + EXPECT_FALSE(aTrsf->IsNegative()); + EXPECT_NEAR(aTrsf->ScaleFactor(), -1.0, Precision::Confusion()); + + double aX = 3.0, aY = 4.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, -3.0, Precision::Confusion()); + EXPECT_NEAR(aY, -4.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, SetMirror_Point) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetMirror(gp_Pnt2d(0.0, 0.0)); + + EXPECT_EQ(aTrsf->Form(), gp_PntMirror); + // In 2D, point mirror has det = (-1)*(-1) = 1 > 0, so IsNegative is false + EXPECT_FALSE(aTrsf->IsNegative()); + + double aX = 3.0, aY = 4.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, -3.0, Precision::Confusion()); + EXPECT_NEAR(aY, -4.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, SetMirror_Axis) +{ + // Mirror about X axis + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetMirror(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0))); + + EXPECT_EQ(aTrsf->Form(), gp_Ax1Mirror); + + double aX = 3.0, aY = 4.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 3.0, Precision::Confusion()); + EXPECT_NEAR(aY, -4.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, Inverted) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetTranslation(gp_Vec2d(5.0, 10.0)); + + occ::handle anInv = aTrsf->Inverted(); + + // Applying both should be identity + double aX = 7.0, aY = 3.0; + aTrsf->Transforms(aX, aY); + anInv->Transforms(aX, aY); + EXPECT_NEAR(aX, 7.0, Precision::Confusion()); + EXPECT_NEAR(aY, 3.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, Multiplied) +{ + // Translation + Rotation + occ::handle aTrans = new Geom2d_Transformation(); + aTrans->SetTranslation(gp_Vec2d(1.0, 0.0)); + + occ::handle aRot = new Geom2d_Transformation(); + aRot->SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0); + + // aRot * aTrans: first translate, then rotate + occ::handle aComposed = aRot->Multiplied(aTrans); + + double aX = 0.0, aY = 0.0; + aComposed->Transforms(aX, aY); + // (0,0) -> translate to (1,0) -> rotate 90deg to (0,1) + EXPECT_NEAR(aX, 0.0, Precision::Confusion()); + EXPECT_NEAR(aY, 1.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, Power_Zero_IsIdentity) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 4.0); + aTrsf->Power(0); + + EXPECT_EQ(aTrsf->Form(), gp_Identity); +} + +TEST(Geom2d_TransformationTest, Power_Positive) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0); + aTrsf->Power(2); // Two 90-degree rotations = 180 degrees + + double aX = 1.0, aY = 0.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, -1.0, Precision::Confusion()); + EXPECT_NEAR(aY, 0.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, Value_Matrix) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + // Identity matrix values + EXPECT_NEAR(aTrsf->Value(1, 1), 1.0, Precision::Confusion()); + EXPECT_NEAR(aTrsf->Value(1, 2), 0.0, Precision::Confusion()); + EXPECT_NEAR(aTrsf->Value(2, 1), 0.0, Precision::Confusion()); + EXPECT_NEAR(aTrsf->Value(2, 2), 1.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, Trsf2d_RoundTrip) +{ + gp_Trsf2d aGpTrsf; + aGpTrsf.SetRotation(gp_Pnt2d(1.0, 2.0), M_PI / 3.0); + + occ::handle aTrsf = new Geom2d_Transformation(aGpTrsf); + gp_Trsf2d aBack = aTrsf->Trsf2d(); + + // Verify the round-trip preserves the transformation + double aX1 = 5.0, aY1 = 7.0; + double aX2 = 5.0, aY2 = 7.0; + aGpTrsf.Transforms(aX1, aY1); + aBack.Transforms(aX2, aY2); + EXPECT_NEAR(aX1, aX2, Precision::Confusion()); + EXPECT_NEAR(aY1, aY2, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, Copy) +{ + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetScale(gp_Pnt2d(0.0, 0.0), 3.0); + + occ::handle aCopy = aTrsf->Copy(); + EXPECT_NEAR(aCopy->ScaleFactor(), 3.0, Precision::Confusion()); + + // Verify independence + aCopy->SetScale(gp_Pnt2d(0.0, 0.0), 5.0); + EXPECT_NEAR(aTrsf->ScaleFactor(), 3.0, Precision::Confusion()); +} + +TEST(Geom2d_TransformationTest, SetScale_WithCenter) +{ + // Scaling with center at (2, 3) by factor 2 + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetScale(gp_Pnt2d(2.0, 3.0), 2.0); + + // The center should remain fixed + double aX = 2.0, aY = 3.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 2.0, Precision::Confusion()); + EXPECT_NEAR(aY, 3.0, Precision::Confusion()); + + // A different point should scale from the center + aX = 4.0; + aY = 3.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 6.0, Precision::Confusion()); // 2 + 2*(4-2) = 6 + EXPECT_NEAR(aY, 3.0, Precision::Confusion()); // 3 + 2*(3-3) = 3 +} + +TEST(Geom2d_TransformationTest, SetRotation_WithCenter) +{ + // 90-degree rotation about (1, 1) + occ::handle aTrsf = new Geom2d_Transformation(); + aTrsf->SetRotation(gp_Pnt2d(1.0, 1.0), M_PI / 2.0); + + // Center stays fixed + double aX = 1.0, aY = 1.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 1.0, Precision::Confusion()); + EXPECT_NEAR(aY, 1.0, Precision::Confusion()); + + // (2, 1) relative to center is (1, 0), rotated 90 deg is (0, 1), so result is (1, 2) + aX = 2.0; + aY = 1.0; + aTrsf->Transforms(aX, aY); + EXPECT_NEAR(aX, 1.0, Precision::Confusion()); + EXPECT_NEAR(aY, 2.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_TrimmedCurve_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_TrimmedCurve_Test.cxx new file mode 100644 index 0000000000..0c874d30b5 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_TrimmedCurve_Test.cxx @@ -0,0 +1,197 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class Geom2d_TrimmedCurveTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Line through origin along X + myLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + + // Circle centered at origin, radius 5 + gp_Circ2d aCirc(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), 5.0); + myCircle = new Geom2d_Circle(aCirc); + } + + occ::handle myLine; + occ::handle myCircle; +}; + +TEST_F(Geom2d_TrimmedCurveTest, ConstructFromLine) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0); + + EXPECT_DOUBLE_EQ(aTrim->FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(aTrim->LastParameter(), 10.0); +} + +TEST_F(Geom2d_TrimmedCurveTest, ConstructFromCircle) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI); + + EXPECT_DOUBLE_EQ(aTrim->FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(aTrim->LastParameter(), M_PI); +} + +TEST_F(Geom2d_TrimmedCurveTest, StartAndEndPoints) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myLine, 2.0, 8.0); + + const gp_Pnt2d aStart = aTrim->StartPoint(); + const gp_Pnt2d anEnd = aTrim->EndPoint(); + + EXPECT_NEAR(aStart.X(), 2.0, Precision::Confusion()); + EXPECT_NEAR(anEnd.X(), 8.0, Precision::Confusion()); +} + +TEST_F(Geom2d_TrimmedCurveTest, BasisCurve) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI); + + occ::handle aBasis = aTrim->BasisCurve(); + EXPECT_FALSE(aBasis.IsNull()); +} + +TEST_F(Geom2d_TrimmedCurveTest, EvalD0_MatchesBasisCurve) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI); + + for (double u = 0.0; u <= M_PI; u += M_PI / 4.0) + { + const gp_Pnt2d aTrimPnt = aTrim->EvalD0(u); + const gp_Pnt2d aBasePnt = myCircle->EvalD0(u); + EXPECT_TRUE(aTrimPnt.IsEqual(aBasePnt, Precision::Confusion())); + } +} + +TEST_F(Geom2d_TrimmedCurveTest, EvalD1_MatchesBasisCurve) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0); + + const auto [aTrimPnt, aTrimD1] = aTrim->EvalD1(5.0); + const auto [aBasePnt, aBaseD1] = myLine->EvalD1(5.0); + + EXPECT_TRUE(aTrimPnt.IsEqual(aBasePnt, Precision::Confusion())); + EXPECT_NEAR(aTrimD1.X(), aBaseD1.X(), Precision::Confusion()); + EXPECT_NEAR(aTrimD1.Y(), aBaseD1.Y(), Precision::Confusion()); +} + +TEST_F(Geom2d_TrimmedCurveTest, Continuity) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0); + EXPECT_EQ(aTrim->Continuity(), GeomAbs_CN); +} + +TEST_F(Geom2d_TrimmedCurveTest, IsCN) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0); + EXPECT_TRUE(aTrim->IsCN(10)); +} + +TEST_F(Geom2d_TrimmedCurveTest, IsClosed_OpenArc) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI); + EXPECT_FALSE(aTrim->IsClosed()); +} + +TEST_F(Geom2d_TrimmedCurveTest, IsClosed_FullCircle) +{ + // Full-period trim on a periodic curve should be reported as closed + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, 2.0 * M_PI); + + EXPECT_TRUE(aTrim->IsClosed()); +} + +TEST_F(Geom2d_TrimmedCurveTest, IsPeriodic_PartialArc) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI); + EXPECT_FALSE(aTrim->IsPeriodic()); +} + +TEST_F(Geom2d_TrimmedCurveTest, IsPeriodic_FullPeriod) +{ + // Full-period trim on a periodic curve should be reported as periodic + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, 2.0 * M_PI); + EXPECT_TRUE(aTrim->IsPeriodic()); +} + +TEST_F(Geom2d_TrimmedCurveTest, IsPeriodic_Line) +{ + // Line is not periodic, so trimmed line should not be periodic + occ::handle aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0); + EXPECT_FALSE(aTrim->IsPeriodic()); +} + +TEST_F(Geom2d_TrimmedCurveTest, SetTrim) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0); + aTrim->SetTrim(3.0, 7.0); + + EXPECT_DOUBLE_EQ(aTrim->FirstParameter(), 3.0); + EXPECT_DOUBLE_EQ(aTrim->LastParameter(), 7.0); +} + +TEST_F(Geom2d_TrimmedCurveTest, Copy) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI); + occ::handle aCopyGeom = aTrim->Copy(); + occ::handle aCopy = occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_DOUBLE_EQ(aCopy->FirstParameter(), 0.0); + EXPECT_DOUBLE_EQ(aCopy->LastParameter(), M_PI); +} + +TEST_F(Geom2d_TrimmedCurveTest, Transform_Translation) +{ + occ::handle aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0); + + gp_Trsf2d aTrsf; + aTrsf.SetTranslation(gp_Vec2d(5.0, 5.0)); + aTrim->Transform(aTrsf); + + const gp_Pnt2d aStart = aTrim->StartPoint(); + EXPECT_NEAR(aStart.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aStart.Y(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_TrimmedCurveTest, CircleArc_StartEndPoints) +{ + // Quarter circle arc + occ::handle aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI / 2.0); + + const gp_Pnt2d aStart = aTrim->StartPoint(); + const gp_Pnt2d anEnd = aTrim->EndPoint(); + + EXPECT_NEAR(aStart.X(), 5.0, Precision::Confusion()); + EXPECT_NEAR(aStart.Y(), 0.0, Precision::Confusion()); + EXPECT_NEAR(anEnd.X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(anEnd.Y(), 5.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2d_VectorWithMagnitude_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2d_VectorWithMagnitude_Test.cxx new file mode 100644 index 0000000000..c9d225fe78 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2d_VectorWithMagnitude_Test.cxx @@ -0,0 +1,222 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class Geom2d_VectorWithMagnitudeTest : public ::testing::Test +{ +protected: + void SetUp() override + { + myVec1 = new Geom2d_VectorWithMagnitude(3.0, 4.0); + myVec2 = new Geom2d_VectorWithMagnitude(1.0, 0.0); + } + + occ::handle myVec1; + occ::handle myVec2; +}; + +TEST_F(Geom2d_VectorWithMagnitudeTest, ConstructFromCoords) +{ + EXPECT_NEAR(myVec1->X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(myVec1->Y(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, ConstructFromVec2d) +{ + occ::handle aVec = + new Geom2d_VectorWithMagnitude(gp_Vec2d(5.0, 12.0)); + EXPECT_NEAR(aVec->Magnitude(), 13.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, ConstructFromTwoPoints) +{ + occ::handle aVec = + new Geom2d_VectorWithMagnitude(gp_Pnt2d(1.0, 2.0), gp_Pnt2d(4.0, 6.0)); + EXPECT_NEAR(aVec->X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(aVec->Y(), 4.0, Precision::Confusion()); + EXPECT_NEAR(aVec->Magnitude(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Magnitude) +{ + EXPECT_NEAR(myVec1->Magnitude(), 5.0, Precision::Confusion()); + EXPECT_NEAR(myVec2->Magnitude(), 1.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, SquareMagnitude) +{ + EXPECT_NEAR(myVec1->SquareMagnitude(), 25.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Added) +{ + occ::handle aSum = myVec1->Added(myVec2); + EXPECT_NEAR(aSum->X(), 4.0, Precision::Confusion()); + EXPECT_NEAR(aSum->Y(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Subtracted) +{ + occ::handle aDiff = myVec1->Subtracted(myVec2); + EXPECT_NEAR(aDiff->X(), 2.0, Precision::Confusion()); + EXPECT_NEAR(aDiff->Y(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Multiplied) +{ + occ::handle aScaled = myVec1->Multiplied(2.0); + EXPECT_NEAR(aScaled->X(), 6.0, Precision::Confusion()); + EXPECT_NEAR(aScaled->Y(), 8.0, Precision::Confusion()); + EXPECT_NEAR(aScaled->Magnitude(), 10.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Divided) +{ + occ::handle aScaled = myVec1->Divided(5.0); + EXPECT_NEAR(aScaled->X(), 0.6, Precision::Confusion()); + EXPECT_NEAR(aScaled->Y(), 0.8, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Normalize) +{ + occ::handle aVec = new Geom2d_VectorWithMagnitude(3.0, 4.0); + aVec->Normalize(); + EXPECT_NEAR(aVec->Magnitude(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aVec->X(), 0.6, Precision::Confusion()); + EXPECT_NEAR(aVec->Y(), 0.8, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Normalized) +{ + occ::handle aNorm = myVec1->Normalized(); + EXPECT_NEAR(aNorm->Magnitude(), 1.0, Precision::Confusion()); + // Original unchanged + EXPECT_NEAR(myVec1->Magnitude(), 5.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Crossed) +{ + // Cross product in 2D is scalar: (3,4) x (1,0) = 3*0 - 4*1 = -4 + const double aCross = myVec1->Crossed(myVec2); + EXPECT_NEAR(aCross, -4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Dot) +{ + // (3,4) . (1,0) = 3 + const double aDot = myVec1->Dot(myVec2); + EXPECT_NEAR(aDot, 3.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Angle) +{ + // Angle from (1,0) to (0,1) = PI/2 + occ::handle aVecY = new Geom2d_VectorWithMagnitude(0.0, 1.0); + const double anAngle = myVec2->Angle(aVecY); + EXPECT_NEAR(anAngle, M_PI / 2.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Angle_Negative) +{ + // Angle from (0,1) to (1,0) = -PI/2 + occ::handle aVecY = new Geom2d_VectorWithMagnitude(0.0, 1.0); + const double anAngle = aVecY->Angle(myVec2); + EXPECT_NEAR(anAngle, -M_PI / 2.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Reverse) +{ + occ::handle aVec = new Geom2d_VectorWithMagnitude(3.0, 4.0); + aVec->Reverse(); + EXPECT_NEAR(aVec->X(), -3.0, Precision::Confusion()); + EXPECT_NEAR(aVec->Y(), -4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, SetCoord) +{ + myVec1->SetCoord(7.0, 8.0); + EXPECT_NEAR(myVec1->X(), 7.0, Precision::Confusion()); + EXPECT_NEAR(myVec1->Y(), 8.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Vec2d) +{ + const gp_Vec2d aVec = myVec1->Vec2d(); + EXPECT_NEAR(aVec.X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(aVec.Y(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Copy) +{ + occ::handle aCopyGeom = myVec1->Copy(); + occ::handle aCopy = + occ::down_cast(aCopyGeom); + + ASSERT_FALSE(aCopy.IsNull()); + EXPECT_NEAR(aCopy->X(), 3.0, Precision::Confusion()); + EXPECT_NEAR(aCopy->Y(), 4.0, Precision::Confusion()); + + aCopy->SetCoord(0.0, 0.0); + EXPECT_NEAR(myVec1->X(), 3.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Add_InPlace) +{ + myVec1->Add(myVec2); + EXPECT_NEAR(myVec1->X(), 4.0, Precision::Confusion()); + EXPECT_NEAR(myVec1->Y(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Subtract_InPlace) +{ + myVec1->Subtract(myVec2); + EXPECT_NEAR(myVec1->X(), 2.0, Precision::Confusion()); + EXPECT_NEAR(myVec1->Y(), 4.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Multiply_InPlace) +{ + myVec1->Multiply(3.0); + EXPECT_NEAR(myVec1->X(), 9.0, Precision::Confusion()); + EXPECT_NEAR(myVec1->Y(), 12.0, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Divide_InPlace) +{ + myVec1->Divide(5.0); + EXPECT_NEAR(myVec1->X(), 0.6, Precision::Confusion()); + EXPECT_NEAR(myVec1->Y(), 0.8, Precision::Confusion()); +} + +TEST_F(Geom2d_VectorWithMagnitudeTest, Transform_Rotation) +{ + gp_Trsf2d aTrsf; + aTrsf.SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0); + + occ::handle aVec = new Geom2d_VectorWithMagnitude(1.0, 0.0); + aVec->Transform(aTrsf); + + EXPECT_NEAR(aVec->X(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aVec->Y(), 1.0, Precision::Confusion()); +} diff --git a/src/ModelingData/TKG2d/GTests/LProp_AnalyticCurInf_Test.cxx b/src/ModelingData/TKG2d/GTests/LProp_AnalyticCurInf_Test.cxx new file mode 100644 index 0000000000..0093fa4d07 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/LProp_AnalyticCurInf_Test.cxx @@ -0,0 +1,136 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include +#include + +#include + +#include + +TEST(LProp_AnalyticCurInfTest, Line_NoExtrema) +{ + // A line has constant zero curvature - no extrema + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Line, 0.0, 10.0, aResult); + EXPECT_EQ(aResult.NbPoints(), 0); +} + +TEST(LProp_AnalyticCurInfTest, Circle_NoExtrema) +{ + // A circle has constant curvature - no extrema + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Circle, 0.0, 2.0 * M_PI, aResult); + EXPECT_EQ(aResult.NbPoints(), 0); +} + +TEST(LProp_AnalyticCurInfTest, Ellipse_FourExtrema) +{ + // An ellipse has 4 curvature extrema in a full period + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Ellipse, 0.0, 2.0 * M_PI, aResult); + EXPECT_EQ(aResult.NbPoints(), 4); +} + +TEST(LProp_AnalyticCurInfTest, Ellipse_ExtremaTypes) +{ + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Ellipse, 0.0, 2.0 * M_PI, aResult); + ASSERT_EQ(aResult.NbPoints(), 4); + + int aNbMin = 0; + int aNbMax = 0; + for (int i = 1; i <= aResult.NbPoints(); ++i) + { + if (aResult.Type(i) == LProp_MinCur) + ++aNbMin; + else if (aResult.Type(i) == LProp_MaxCur) + ++aNbMax; + } + EXPECT_EQ(aNbMin, 2); + EXPECT_EQ(aNbMax, 2); +} + +TEST(LProp_AnalyticCurInfTest, Ellipse_ExtremaAtExpectedParameters) +{ + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Ellipse, 0.0, 2.0 * M_PI, aResult); + ASSERT_EQ(aResult.NbPoints(), 4); + + // Extrema at 0, PI/2, PI, 3*PI/2 + const double anExpected[] = {0.0, M_PI / 2.0, M_PI, 3.0 * M_PI / 2.0}; + for (int i = 1; i <= 4; ++i) + { + EXPECT_NEAR(aResult.Parameter(i), anExpected[i - 1], 1e-10); + } +} + +TEST(LProp_AnalyticCurInfTest, Ellipse_PartialRange) +{ + // Only first quadrant: should have 2 extrema (at 0 and PI/2) + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Ellipse, 0.0, M_PI / 2.0, aResult); + EXPECT_EQ(aResult.NbPoints(), 2); +} + +TEST(LProp_AnalyticCurInfTest, Parabola_MinRadiusAtVertex) +{ + // Parabola has minimum radius of curvature (= maximum curvature) at vertex. + // LProp_AnalyticCurInf reports this as LProp_MinCur (minimum radius of curvature). + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Parabola, -10.0, 10.0, aResult); + ASSERT_EQ(aResult.NbPoints(), 1); + EXPECT_NEAR(aResult.Parameter(1), 0.0, 1e-10); + EXPECT_EQ(aResult.Type(1), LProp_MinCur); +} + +TEST(LProp_AnalyticCurInfTest, Hyperbola_MinRadiusAtVertex) +{ + // Hyperbola has minimum radius of curvature at vertex (parameter 0). + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Hyperbola, -5.0, 5.0, aResult); + ASSERT_EQ(aResult.NbPoints(), 1); + EXPECT_NEAR(aResult.Parameter(1), 0.0, 1e-10); + EXPECT_EQ(aResult.Type(1), LProp_MinCur); +} + +TEST(LProp_AnalyticCurInfTest, Ellipse_ParametersSorted) +{ + LProp_AnalyticCurInf anAnalyzer; + LProp_CurAndInf aResult; + + anAnalyzer.Perform(GeomAbs_Ellipse, 0.0, 2.0 * M_PI, aResult); + + for (int i = 1; i < aResult.NbPoints(); ++i) + { + EXPECT_LE(aResult.Parameter(i), aResult.Parameter(i + 1)); + } +} diff --git a/src/ModelingData/TKG2d/GTests/LProp_CurAndInf_Test.cxx b/src/ModelingData/TKG2d/GTests/LProp_CurAndInf_Test.cxx new file mode 100644 index 0000000000..c7ef527480 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/LProp_CurAndInf_Test.cxx @@ -0,0 +1,129 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include + +#include + +TEST(LProp_CurAndInfTest, DefaultConstructor_IsEmpty) +{ + LProp_CurAndInf aResult; + EXPECT_TRUE(aResult.IsEmpty()); + EXPECT_EQ(aResult.NbPoints(), 0); +} + +TEST(LProp_CurAndInfTest, AddInflection) +{ + LProp_CurAndInf aResult; + aResult.AddInflection(1.5); + + EXPECT_FALSE(aResult.IsEmpty()); + EXPECT_EQ(aResult.NbPoints(), 1); + EXPECT_DOUBLE_EQ(aResult.Parameter(1), 1.5); + EXPECT_EQ(aResult.Type(1), LProp_Inflection); +} + +TEST(LProp_CurAndInfTest, AddExtCur_Minimum) +{ + LProp_CurAndInf aResult; + aResult.AddExtCur(2.0, true); + + EXPECT_EQ(aResult.NbPoints(), 1); + EXPECT_DOUBLE_EQ(aResult.Parameter(1), 2.0); + EXPECT_EQ(aResult.Type(1), LProp_MinCur); +} + +TEST(LProp_CurAndInfTest, AddExtCur_Maximum) +{ + LProp_CurAndInf aResult; + aResult.AddExtCur(3.0, false); + + EXPECT_EQ(aResult.NbPoints(), 1); + EXPECT_DOUBLE_EQ(aResult.Parameter(1), 3.0); + EXPECT_EQ(aResult.Type(1), LProp_MaxCur); +} + +TEST(LProp_CurAndInfTest, MultiplePoints_SortedByParameter) +{ + LProp_CurAndInf aResult; + // Add in non-sorted order + aResult.AddInflection(5.0); + aResult.AddExtCur(1.0, true); + aResult.AddExtCur(3.0, false); + aResult.AddInflection(7.0); + + EXPECT_EQ(aResult.NbPoints(), 4); + + // Verify sorted order + for (int i = 1; i < aResult.NbPoints(); ++i) + { + EXPECT_LE(aResult.Parameter(i), aResult.Parameter(i + 1)); + } +} + +TEST(LProp_CurAndInfTest, MultiplePoints_CorrectTypes) +{ + LProp_CurAndInf aResult; + aResult.AddExtCur(1.0, true); // MinCur + aResult.AddInflection(3.0); // Inflection + aResult.AddExtCur(5.0, false); // MaxCur + + EXPECT_EQ(aResult.NbPoints(), 3); + EXPECT_EQ(aResult.Type(1), LProp_MinCur); + EXPECT_EQ(aResult.Type(2), LProp_Inflection); + EXPECT_EQ(aResult.Type(3), LProp_MaxCur); +} + +TEST(LProp_CurAndInfTest, Clear) +{ + LProp_CurAndInf aResult; + aResult.AddInflection(1.0); + aResult.AddExtCur(2.0, true); + + EXPECT_EQ(aResult.NbPoints(), 2); + + aResult.Clear(); + EXPECT_TRUE(aResult.IsEmpty()); + EXPECT_EQ(aResult.NbPoints(), 0); +} + +TEST(LProp_CurAndInfTest, Clear_ThenRefill) +{ + LProp_CurAndInf aResult; + aResult.AddInflection(1.0); + aResult.Clear(); + + aResult.AddExtCur(5.0, false); + EXPECT_EQ(aResult.NbPoints(), 1); + EXPECT_DOUBLE_EQ(aResult.Parameter(1), 5.0); + EXPECT_EQ(aResult.Type(1), LProp_MaxCur); +} + +TEST(LProp_CurAndInfTest, Parameter_OutOfRange_Throws) +{ + LProp_CurAndInf aResult; + aResult.AddInflection(1.0); + + EXPECT_THROW(aResult.Parameter(0), Standard_OutOfRange); + EXPECT_THROW(aResult.Parameter(2), Standard_OutOfRange); +} + +TEST(LProp_CurAndInfTest, Type_OutOfRange_Throws) +{ + LProp_CurAndInf aResult; + aResult.AddInflection(1.0); + + EXPECT_THROW(aResult.Type(0), Standard_OutOfRange); + EXPECT_THROW(aResult.Type(2), Standard_OutOfRange); +} diff --git a/src/ModelingData/TKG2d/Geom2d/Geom2d_BSplineCurve_1.cxx b/src/ModelingData/TKG2d/Geom2d/Geom2d_BSplineCurve_1.cxx index c206ba5ed2..f02c282aaa 100644 --- a/src/ModelingData/TKG2d/Geom2d/Geom2d_BSplineCurve_1.cxx +++ b/src/ModelingData/TKG2d/Geom2d/Geom2d_BSplineCurve_1.cxx @@ -134,7 +134,7 @@ bool Geom2d_BSplineCurve::IsG1(const double theTf, const double theTl, const dou bool Geom2d_BSplineCurve::IsClosed() const { - return (StartPoint().Distance(EndPoint())) <= gp::Resolution(); + return StartPoint().SquareDistance(EndPoint()) <= Precision::Computational(); } //================================================================================================= diff --git a/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.cxx b/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.cxx index 07e263ada5..d56523f8a6 100644 --- a/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.cxx +++ b/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.cxx @@ -370,7 +370,7 @@ bool Geom2d_OffsetCurve::IsClosed() const gp_Pnt2d PF, PL; Geom2d_Curve::D0(FirstParameter(), PF); Geom2d_Curve::D0(LastParameter(), PL); - return (PF.Distance(PL) <= gp::Resolution()); + return PF.SquareDistance(PL) <= Precision::Computational(); } //================================================================================================== diff --git a/src/ModelingData/TKG2d/Geom2d/Geom2d_TrimmedCurve.cxx b/src/ModelingData/TKG2d/Geom2d/Geom2d_TrimmedCurve.cxx index b470bb5023..12ccdc6d19 100644 --- a/src/ModelingData/TKG2d/Geom2d/Geom2d_TrimmedCurve.cxx +++ b/src/ModelingData/TKG2d/Geom2d/Geom2d_TrimmedCurve.cxx @@ -181,15 +181,30 @@ double Geom2d_TrimmedCurve::FirstParameter() const bool Geom2d_TrimmedCurve::IsClosed() const { - double Dist = Value(FirstParameter()).Distance(Value(LastParameter())); - return (Dist <= gp::Resolution()); + if (basisCurve->IsPeriodic()) + { + const double aPeriod = basisCurve->Period(); + const double aLength = LastParameter() - FirstParameter(); + if (aLength > Precision::PConfusion() + && std::abs(std::remainder(aLength, aPeriod)) <= Precision::PConfusion()) + return true; + } + return Value(FirstParameter()).SquareDistance(Value(LastParameter())) + <= Precision::Computational(); } //================================================================================================= bool Geom2d_TrimmedCurve::IsPeriodic() const { - // return basisCurve->IsPeriodic(); + if (basisCurve->IsPeriodic()) + { + const double aPeriod = basisCurve->Period(); + const double aLength = LastParameter() - FirstParameter(); + if (aLength > Precision::PConfusion() + && std::abs(std::remainder(aLength, aPeriod)) <= Precision::PConfusion()) + return true; + } return false; } diff --git a/src/ModelingData/TKG2d/Geom2d/Geom2d_TrimmedCurve.hxx b/src/ModelingData/TKG2d/Geom2d/Geom2d_TrimmedCurve.hxx index a30b85d335..9c9b592b73 100644 --- a/src/ModelingData/TKG2d/Geom2d/Geom2d_TrimmedCurve.hxx +++ b/src/ModelingData/TKG2d/Geom2d/Geom2d_TrimmedCurve.hxx @@ -151,12 +151,13 @@ public: //! of the trimmed curve. Standard_EXPORT double FirstParameter() const final; - //! Returns True if the distance between the StartPoint and - //! the EndPoint is lower or equal to Resolution from package - //! gp. + //! Returns TRUE if the basis curve is periodic and the trim spans + //! exactly one full period, or if the distance between the StartPoint + //! and the EndPoint is within computational precision. Standard_EXPORT bool IsClosed() const final; - //! Always returns FALSE (independently of the type of basis curve). + //! Returns TRUE if the basis curve is periodic and the trim + //! spans exactly one full period. Returns FALSE otherwise. Standard_EXPORT bool IsPeriodic() const final; //! Returns the period of the basis curve of this trimmed curve. diff --git a/src/ModelingData/TKG3d/Geom/Geom_BSplineCurve_1.cxx b/src/ModelingData/TKG3d/Geom/Geom_BSplineCurve_1.cxx index 4e5b786cc8..0caa65e123 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_BSplineCurve_1.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_BSplineCurve_1.cxx @@ -132,9 +132,8 @@ bool Geom_BSplineCurve::IsG1(const double theTf, const double theTl, const doubl //================================================================================================= bool Geom_BSplineCurve::IsClosed() const -//-- { return (StartPoint().Distance (EndPoint())) <= gp::Resolution (); } { - return (StartPoint().SquareDistance(EndPoint())) <= 1e-16; + return StartPoint().SquareDistance(EndPoint()) <= Precision::Computational(); } //================================================================================================= diff --git a/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.cxx b/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.cxx index 38a36ddb90..e7fae70eb2 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.cxx @@ -424,7 +424,7 @@ bool Geom_OffsetCurve::IsClosed() const gp_Pnt PF, PL; Geom_Curve::D0(FirstParameter(), PF); Geom_Curve::D0(LastParameter(), PL); - return (PF.Distance(PL) <= gp::Resolution()); + return PF.SquareDistance(PL) <= Precision::Computational(); } //================================================================================================== diff --git a/src/ModelingData/TKG3d/Geom/Geom_RectangularTrimmedSurface.cxx b/src/ModelingData/TKG3d/Geom/Geom_RectangularTrimmedSurface.cxx index 8cada59d9f..44bbc78757 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_RectangularTrimmedSurface.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_RectangularTrimmedSurface.cxx @@ -476,15 +476,18 @@ void Geom_RectangularTrimmedSurface::Transform(const Trsf& T) basisSurf->TransformParameters(utrim2, vtrim2, T); } -//======================================================================= -// function : IsUPeriodic -// purpose : -// 24/11/98: pmn : Compare la periode a la longeur de l'intervalle -//======================================================================= +//================================================================================================= bool Geom_RectangularTrimmedSurface::IsUPeriodic() const { - return basisSurf->IsUPeriodic() && !isutrimmed; + if (!basisSurf->IsUPeriodic()) + return false; + if (!isutrimmed) + return true; + const double aPeriod = basisSurf->UPeriod(); + const double aLength = utrim2 - utrim1; + return aLength > Precision::PConfusion() + && std::abs(std::remainder(aLength, aPeriod)) <= Precision::PConfusion(); } //================================================================================================= @@ -498,7 +501,14 @@ double Geom_RectangularTrimmedSurface::UPeriod() const bool Geom_RectangularTrimmedSurface::IsVPeriodic() const { - return basisSurf->IsVPeriodic() && !isvtrimmed; + if (!basisSurf->IsVPeriodic()) + return false; + if (!isvtrimmed) + return true; + const double aPeriod = basisSurf->VPeriod(); + const double aLength = vtrim2 - vtrim1; + return aLength > Precision::PConfusion() + && std::abs(std::remainder(aLength, aPeriod)) <= Precision::PConfusion(); } //================================================================================================= @@ -512,22 +522,34 @@ double Geom_RectangularTrimmedSurface::VPeriod() const bool Geom_RectangularTrimmedSurface::IsUClosed() const { - - if (isutrimmed) - return false; - else + if (!isutrimmed) return basisSurf->IsUClosed(); + if (basisSurf->IsUPeriodic()) + { + const double aPeriod = basisSurf->UPeriod(); + const double aLength = utrim2 - utrim1; + if (aLength > Precision::PConfusion() + && std::abs(std::remainder(aLength, aPeriod)) <= Precision::PConfusion()) + return true; + } + return false; } //================================================================================================= bool Geom_RectangularTrimmedSurface::IsVClosed() const { - - if (isvtrimmed) - return false; - else + if (!isvtrimmed) return basisSurf->IsVClosed(); + if (basisSurf->IsVPeriodic()) + { + const double aPeriod = basisSurf->VPeriod(); + const double aLength = vtrim2 - vtrim1; + if (aLength > Precision::PConfusion() + && std::abs(std::remainder(aLength, aPeriod)) <= Precision::PConfusion()) + return true; + } + return false; } //================================================================================================= diff --git a/src/ModelingData/TKG3d/Geom/Geom_RectangularTrimmedSurface.hxx b/src/ModelingData/TKG3d/Geom/Geom_RectangularTrimmedSurface.hxx index 0c1d6bc377..c8b7a6e2dd 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_RectangularTrimmedSurface.hxx +++ b/src/ModelingData/TKG3d/Geom/Geom_RectangularTrimmedSurface.hxx @@ -186,10 +186,14 @@ public: //! CN : the order of continuity is infinite. Standard_EXPORT GeomAbs_Shape Continuity() const final; - //! Returns true if this patch is closed in the given parametric direction. + //! Returns true if this patch is closed in U: either not trimmed in U + //! and the basis surface is U-closed, or trimmed with a length that is + //! an integer multiple of the U period of a U-periodic basis surface. Standard_EXPORT bool IsUClosed() const final; - //! Returns true if this patch is closed in the given parametric direction. + //! Returns true if this patch is closed in V: either not trimmed in V + //! and the basis surface is V-closed, or trimmed with a length that is + //! an integer multiple of the V period of a V-periodic basis surface. Standard_EXPORT bool IsVClosed() const final; //! Returns true if the order of derivation in the U parametric @@ -202,8 +206,8 @@ public: //! Raised if N < 0. Standard_EXPORT bool IsCNv(const int N) const final; - //! Returns true if this patch is periodic and not trimmed in the given - //! parametric direction. + //! Returns true if the basis surface is U-periodic and either not trimmed + //! in U, or the trim spans an integer multiple of the U period. Standard_EXPORT bool IsUPeriodic() const final; //! Returns the period of this patch in the u @@ -211,8 +215,8 @@ public: //! raises if the surface is not uperiodic. Standard_EXPORT double UPeriod() const final; - //! Returns true if this patch is periodic and not trimmed in the given - //! parametric direction. + //! Returns true if the basis surface is V-periodic and either not trimmed + //! in V, or the trim spans an integer multiple of the V period. Standard_EXPORT bool IsVPeriodic() const final; //! Returns the period of this patch in the v diff --git a/src/ModelingData/TKG3d/Geom/Geom_TrimmedCurve.cxx b/src/ModelingData/TKG3d/Geom/Geom_TrimmedCurve.cxx index d5e79bdfed..0dc9a922ef 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_TrimmedCurve.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_TrimmedCurve.cxx @@ -144,14 +144,29 @@ void Geom_TrimmedCurve::SetTrim(const double U1, bool Geom_TrimmedCurve::IsClosed() const { - return (StartPoint().Distance(EndPoint()) <= gp::Resolution()); + if (basisCurve->IsPeriodic()) + { + const double aPeriod = basisCurve->Period(); + const double aLength = LastParameter() - FirstParameter(); + if (aLength > Precision::PConfusion() + && std::abs(std::remainder(aLength, aPeriod)) <= Precision::PConfusion()) + return true; + } + return StartPoint().SquareDistance(EndPoint()) <= Precision::Computational(); } //================================================================================================= bool Geom_TrimmedCurve::IsPeriodic() const { - // return basisCurve->IsPeriodic(); + if (basisCurve->IsPeriodic()) + { + const double aPeriod = basisCurve->Period(); + const double aLength = LastParameter() - FirstParameter(); + if (aLength > Precision::PConfusion() + && std::abs(std::remainder(aLength, aPeriod)) <= Precision::PConfusion()) + return true; + } return false; } diff --git a/src/ModelingData/TKG3d/Geom/Geom_TrimmedCurve.hxx b/src/ModelingData/TKG3d/Geom/Geom_TrimmedCurve.hxx index 1cd5a7a0b8..7b2148e91d 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_TrimmedCurve.hxx +++ b/src/ModelingData/TKG3d/Geom/Geom_TrimmedCurve.hxx @@ -156,11 +156,13 @@ public: //! of the trimmed curve. Standard_EXPORT double FirstParameter() const final; - //! Returns True if the distance between the StartPoint and - //! the EndPoint is lower or equal to Resolution from package gp. + //! Returns TRUE if the basis curve is periodic and the trim spans + //! exactly one full period, or if the distance between the StartPoint + //! and the EndPoint is within computational precision. Standard_EXPORT bool IsClosed() const final; - //! Always returns FALSE (independently of the type of basis curve). + //! Returns TRUE if the basis curve is periodic and the trim + //! spans exactly one full period. Returns FALSE otherwise. Standard_EXPORT bool IsPeriodic() const final; //! Returns the period of the basis curve of this trimmed curve.