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)
This commit is contained in:
Pasukhin Dmitry
2026-02-24 15:34:22 +00:00
committed by GitHub
parent 95b2c6ee3e
commit 9ee9dc02a4
28 changed files with 3219 additions and 38 deletions

View File

@@ -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 <Adaptor2d_Line2d.hxx>
#include <GeomAbs_CurveType.hxx>
#include <GeomAbs_Shape.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Lin2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec2d.hxx>
#include <NCollection_Array1.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
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<Adaptor2d_Line2d> myLine;
occ::handle<Adaptor2d_Line2d> 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<double> 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<Adaptor2d_Curve2d> 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<Adaptor2d_Curve2d> 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());
}

View File

@@ -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 <Adaptor2d_Line2d.hxx>
#include <Adaptor2d_OffsetCurve.hxx>
#include <GeomAbs_CurveType.hxx>
#include <GeomAbs_Shape.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec2d.hxx>
#include <NCollection_Array1.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
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<Adaptor2d_Line2d> 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<Adaptor2d_Line2d> 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<Adaptor2d_Curve2d>& 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<Adaptor2d_Curve2d> 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<Adaptor2d_Curve2d> 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);
}

View File

@@ -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
)

View File

@@ -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 <Geom2d_Circle.hxx>
#include <Geom2d_Ellipse.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2dLProp_CLProps2d.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Circ2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Elips2d.hxx>
#include <gp_Lin2d.hxx>
#include <gp_Pnt2d.hxx>
#include <LProp_NotDefined.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
#include <cmath>
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<Geom2d_Circle> myCircle;
occ::handle<Geom2d_Line> myLine;
occ::handle<Geom2d_Ellipse> 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());
}

View File

@@ -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 <Geom2d_Circle.hxx>
#include <Geom2d_Ellipse.hxx>
#include <Geom2dLProp_CurAndInf2d.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Circ2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Elips2d.hxx>
#include <gp_Pnt2d.hxx>
#include <LProp_CIType.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
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<Geom2d_Circle> myCircle;
occ::handle<Geom2d_Ellipse> 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);
}
}

View File

@@ -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 <Geom2d_AxisPlacement.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Vec2d.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
#include <cmath>
TEST(Geom2d_AxisPlacementTest, ConstructFromAx2d)
{
gp_Ax2d anAxis(gp_Pnt2d(1.0, 2.0), gp_Dir2d(1.0, 0.0));
occ::handle<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> anAP1 =
new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
occ::handle<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> anAP1 =
new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
occ::handle<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> anAP1 =
new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
occ::handle<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> anAP =
new Geom2d_AxisPlacement(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
occ::handle<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> 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<Geom2d_AxisPlacement> anAP =
new Geom2d_AxisPlacement(gp_Pnt2d(1.0, 2.0), gp_Dir2d(0.0, 1.0));
occ::handle<Geom2d_Geometry> aCopyGeom = anAP->Copy();
occ::handle<Geom2d_AxisPlacement> aCopy = occ::down_cast<Geom2d_AxisPlacement>(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());
}

View File

@@ -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 <Geom2d_CartesianPoint.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Vec2d.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
#include <cmath>
TEST(Geom2d_CartesianPointTest, ConstructFromCoords)
{
occ::handle<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> aP1 = new Geom2d_CartesianPoint(0.0, 0.0);
occ::handle<Geom2d_CartesianPoint> aP2 = new Geom2d_CartesianPoint(3.0, 4.0);
EXPECT_NEAR(aP1->Distance(aP2), 5.0, Precision::Confusion());
}
TEST(Geom2d_CartesianPointTest, SquareDistance)
{
occ::handle<Geom2d_CartesianPoint> aP1 = new Geom2d_CartesianPoint(0.0, 0.0);
occ::handle<Geom2d_CartesianPoint> aP2 = new Geom2d_CartesianPoint(3.0, 4.0);
EXPECT_NEAR(aP1->SquareDistance(aP2), 25.0, Precision::Confusion());
}
TEST(Geom2d_CartesianPointTest, Distance_SamePoint)
{
occ::handle<Geom2d_CartesianPoint> aPnt = new Geom2d_CartesianPoint(5.0, 5.0);
EXPECT_NEAR(aPnt->Distance(aPnt), 0.0, Precision::Confusion());
}
TEST(Geom2d_CartesianPointTest, Transform_Translation)
{
occ::handle<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> 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<Geom2d_CartesianPoint> aPnt = new Geom2d_CartesianPoint(3.0, 4.0);
occ::handle<Geom2d_Geometry> aCopyGeom = aPnt->Copy();
occ::handle<Geom2d_CartesianPoint> aCopy = occ::down_cast<Geom2d_CartesianPoint>(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());
}

View File

@@ -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 <Geom2d_Circle.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Ax22d.hxx>
#include <gp_Circ2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
#include <cmath>
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<Geom2d_Circle> myCircle;
};
TEST_F(Geom2d_CircleTest, ConstructFromCirc2d)
{
EXPECT_NEAR(myCircle->Radius(), 5.0, Precision::Confusion());
}
TEST_F(Geom2d_CircleTest, ConstructFromAxisAndRadius)
{
occ::handle<Geom2d_Circle> 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<Geom2d_Circle> 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<Geom2d_Geometry> aCopyGeom = myCircle->Copy();
occ::handle<Geom2d_Circle> aCopy = occ::down_cast<Geom2d_Circle>(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());
}
}

View File

@@ -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 <Geom2d_Direction.hxx>
#include <Geom2d_VectorWithMagnitude.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Trsf2d.hxx>
#include <Precision.hxx>
#include <Standard_ConstructionError.hxx>
#include <gtest/gtest.h>
#include <cmath>
TEST(Geom2d_DirectionTest, ConstructFromCoords)
{
occ::handle<Geom2d_Direction> 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<Geom2d_Direction> 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<Geom2d_Direction> 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<Geom2d_Direction> 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<Geom2d_Direction> 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<Geom2d_Direction> 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<Geom2d_Direction> aDirX = new Geom2d_Direction(1.0, 0.0);
occ::handle<Geom2d_Direction> 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<Geom2d_Direction> aDirX = new Geom2d_Direction(1.0, 0.0);
occ::handle<Geom2d_Direction> 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<Geom2d_Direction> aDirPos = new Geom2d_Direction(1.0, 0.0);
occ::handle<Geom2d_Direction> 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<Geom2d_Direction> aDirX = new Geom2d_Direction(1.0, 0.0);
occ::handle<Geom2d_Direction> 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<Geom2d_Direction> 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<Geom2d_Direction> aDir = new Geom2d_Direction(1.0, 0.0);
occ::handle<Geom2d_VectorWithMagnitude> aVec = new Geom2d_VectorWithMagnitude(0.0, 5.0);
EXPECT_NEAR(aDir->Crossed(aVec), 5.0, Precision::Confusion());
}
TEST(Geom2d_DirectionTest, Copy)
{
occ::handle<Geom2d_Direction> aDir = new Geom2d_Direction(1.0, 0.0);
occ::handle<Geom2d_Geometry> aCopyGeom = aDir->Copy();
occ::handle<Geom2d_Direction> aCopy = occ::down_cast<Geom2d_Direction>(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<Geom2d_Direction> 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());
}

View File

@@ -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 <Geom2d_Ellipse.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Elips2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Vec2d.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
#include <cmath>
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<Geom2d_Ellipse> 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<Geom2d_Geometry> aCopyGeom = myEllipse->Copy();
occ::handle<Geom2d_Ellipse> aCopy = occ::down_cast<Geom2d_Ellipse>(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());
}

View File

@@ -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 <Geom2d_Hyperbola.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Hypr2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Vec2d.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
#include <cmath>
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<Geom2d_Hyperbola> 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<Geom2d_Geometry> aCopyGeom = myHyperbola->Copy();
occ::handle<Geom2d_Hyperbola> aCopy = occ::down_cast<Geom2d_Hyperbola>(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());
}

View File

@@ -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 <Geom2d_Line.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Lin2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Vec2d.hxx>
#include <GeomAbs_Shape.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
#include <cmath>
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<Geom2d_Line> 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<Geom2d_Line> 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<Geom2d_Line> 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<Geom2d_Geometry> aCopyGeom = myLine->Copy();
occ::handle<Geom2d_Line> aCopy = occ::down_cast<Geom2d_Line>(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<Geom2d_Line> 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());
}

View File

@@ -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 <Geom2d_Parabola.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Lin2d.hxx>
#include <gp_Parab2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Vec2d.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
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<Geom2d_Parabola> 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<Geom2d_Geometry> aCopyGeom = myParabola->Copy();
occ::handle<Geom2d_Parabola> aCopy = occ::down_cast<Geom2d_Parabola>(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());
}

View File

@@ -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 <Geom2d_Transformation.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_TrsfForm.hxx>
#include <gp_Vec2d.hxx>
#include <Precision.hxx>
#include <gtest/gtest.h>
#include <cmath>
TEST(Geom2d_TransformationTest, DefaultConstructor_Identity)
{
occ::handle<Geom2d_Transformation> 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<Geom2d_Transformation> aTrsf = new Geom2d_Transformation(aGpTrsf);
EXPECT_EQ(aTrsf->Form(), gp_Translation);
}
TEST(Geom2d_TransformationTest, SetTranslation_Vector)
{
occ::handle<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> aTrsf = new Geom2d_Transformation();
aTrsf->SetTranslation(gp_Vec2d(5.0, 10.0));
occ::handle<Geom2d_Transformation> 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<Geom2d_Transformation> aTrans = new Geom2d_Transformation();
aTrans->SetTranslation(gp_Vec2d(1.0, 0.0));
occ::handle<Geom2d_Transformation> aRot = new Geom2d_Transformation();
aRot->SetRotation(gp_Pnt2d(0.0, 0.0), M_PI / 2.0);
// aRot * aTrans: first translate, then rotate
occ::handle<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> aTrsf = new Geom2d_Transformation();
aTrsf->SetScale(gp_Pnt2d(0.0, 0.0), 3.0);
occ::handle<Geom2d_Transformation> 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<Geom2d_Transformation> 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<Geom2d_Transformation> 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());
}

View File

@@ -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 <Geom2d_Circle.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <gp_Circ2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Vec2d.hxx>
#include <GeomAbs_Shape.hxx>
#include <Precision.hxx>
#include <Standard_ConstructionError.hxx>
#include <gtest/gtest.h>
#include <cmath>
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<Geom2d_Line> myLine;
occ::handle<Geom2d_Circle> myCircle;
};
TEST_F(Geom2d_TrimmedCurveTest, ConstructFromLine)
{
occ::handle<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI);
occ::handle<Geom2d_Curve> aBasis = aTrim->BasisCurve();
EXPECT_FALSE(aBasis.IsNull());
}
TEST_F(Geom2d_TrimmedCurveTest, EvalD0_MatchesBasisCurve)
{
occ::handle<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0);
EXPECT_EQ(aTrim->Continuity(), GeomAbs_CN);
}
TEST_F(Geom2d_TrimmedCurveTest, IsCN)
{
occ::handle<Geom2d_TrimmedCurve> aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0);
EXPECT_TRUE(aTrim->IsCN(10));
}
TEST_F(Geom2d_TrimmedCurveTest, IsClosed_OpenArc)
{
occ::handle<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, 2.0 * M_PI);
EXPECT_TRUE(aTrim->IsClosed());
}
TEST_F(Geom2d_TrimmedCurveTest, IsPeriodic_PartialArc)
{
occ::handle<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> aTrim = new Geom2d_TrimmedCurve(myLine, 0.0, 10.0);
EXPECT_FALSE(aTrim->IsPeriodic());
}
TEST_F(Geom2d_TrimmedCurveTest, SetTrim)
{
occ::handle<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> aTrim = new Geom2d_TrimmedCurve(myCircle, 0.0, M_PI);
occ::handle<Geom2d_Geometry> aCopyGeom = aTrim->Copy();
occ::handle<Geom2d_TrimmedCurve> aCopy = occ::down_cast<Geom2d_TrimmedCurve>(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<Geom2d_TrimmedCurve> 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<Geom2d_TrimmedCurve> 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());
}

View File

@@ -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 <Geom2d_Direction.hxx>
#include <Geom2d_VectorWithMagnitude.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Vec2d.hxx>
#include <Precision.hxx>
#include <Standard_ConstructionError.hxx>
#include <gtest/gtest.h>
#include <cmath>
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<Geom2d_VectorWithMagnitude> myVec1;
occ::handle<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_VectorWithMagnitude> 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<Geom2d_Geometry> aCopyGeom = myVec1->Copy();
occ::handle<Geom2d_VectorWithMagnitude> aCopy =
occ::down_cast<Geom2d_VectorWithMagnitude>(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<Geom2d_VectorWithMagnitude> 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());
}

View File

@@ -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 <GeomAbs_CurveType.hxx>
#include <LProp_AnalyticCurInf.hxx>
#include <LProp_CIType.hxx>
#include <LProp_CurAndInf.hxx>
#include <gtest/gtest.h>
#include <cmath>
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));
}
}

View File

@@ -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 <LProp_CIType.hxx>
#include <LProp_CurAndInf.hxx>
#include <gtest/gtest.h>
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);
}

View File

@@ -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();
}
//=================================================================================================

View File

@@ -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();
}
//==================================================================================================

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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();
}
//=================================================================================================

View File

@@ -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();
}
//==================================================================================================

View File

@@ -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;
}
//=================================================================================================

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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.