From fcd88b933f47e2b9d94563f843632b10086b496b Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Fri, 13 Feb 2026 22:41:40 +0000 Subject: [PATCH] Modeling Data - Add Geom2dGridEval package for batch 2D curve evaluation (#1079) Add new Geom2dGridEval package in TKG2d providing batch evaluation of 2D curves at multiple parameter values, mirroring the existing 3D GeomGridEval package in TKG3d. Specialized evaluators use analytical formulas for conics and cache-based evaluation for BSpline/Bezier curves, with a unified std::variant dispatcher for automatic type-based dispatch. New classes: - Geom2dGridEval_Line (header-only), _Circle, _Ellipse, _Hyperbola, _Parabola, _BezierCurve, _BSplineCurve, _OffsetCurve, _OtherCurve - Geom2dGridEval_Curve: unified dispatcher with Initialize() from Adaptor2d_Curve2d or occ::handle - Geom2dGridEval.hxx: CurveD1/D2/D3 result structures BSplCLib_Cache changes: - Add D0Local/D1Local/D2Local/D3Local overloads for gp_Pnt2d/gp_Vec2d - Refactor existing 2D D0/D1/D2/D3 methods to delegate to D*Local, consistent with the existing 3D delegation pattern --- .../TKMath/BSplCLib/BSplCLib_Cache.cxx | 130 ++-- .../TKMath/BSplCLib/BSplCLib_Cache.hxx | 37 + src/ModelingData/TKG2d/GTests/FILES.cmake | 5 + .../Geom2dGridEval_BezierCurve_Test.cxx | 190 +++++ .../GTests/Geom2dGridEval_Curve_Test.cxx | 714 ++++++++++++++++++ .../GTests/Geom2dGridEval_Ellipse_Test.cxx | 157 ++++ .../GTests/Geom2dGridEval_Hyperbola_Test.cxx | 142 ++++ .../GTests/Geom2dGridEval_Parabola_Test.cxx | 160 ++++ .../TKG2d/Geom2dGridEval/FILES.cmake | 25 + .../TKG2d/Geom2dGridEval/Geom2dGridEval.hxx | 54 ++ .../Geom2dGridEval_BSplineCurve.cxx | 375 +++++++++ .../Geom2dGridEval_BSplineCurve.hxx | 101 +++ .../Geom2dGridEval_BezierCurve.cxx | 185 +++++ .../Geom2dGridEval_BezierCurve.hxx | 93 +++ .../Geom2dGridEval/Geom2dGridEval_Circle.cxx | 256 +++++++ .../Geom2dGridEval/Geom2dGridEval_Circle.hxx | 102 +++ .../Geom2dGridEval/Geom2dGridEval_Curve.cxx | 229 ++++++ .../Geom2dGridEval/Geom2dGridEval_Curve.hxx | 146 ++++ .../Geom2dGridEval/Geom2dGridEval_Ellipse.cxx | 263 +++++++ .../Geom2dGridEval/Geom2dGridEval_Ellipse.hxx | 100 +++ .../Geom2dGridEval_Hyperbola.cxx | 250 ++++++ .../Geom2dGridEval_Hyperbola.hxx | 95 +++ .../Geom2dGridEval/Geom2dGridEval_Line.hxx | 230 ++++++ .../Geom2dGridEval_OffsetCurve.cxx | 282 +++++++ .../Geom2dGridEval_OffsetCurve.hxx | 109 +++ .../Geom2dGridEval_OtherCurve.cxx | 185 +++++ .../Geom2dGridEval_OtherCurve.hxx | 101 +++ .../Geom2dGridEval_Parabola.cxx | 255 +++++++ .../Geom2dGridEval_Parabola.hxx | 95 +++ src/ModelingData/TKG2d/PACKAGES.cmake | 1 + 30 files changed, 5018 insertions(+), 49 deletions(-) create mode 100644 src/ModelingData/TKG2d/GTests/Geom2dGridEval_BezierCurve_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2dGridEval_Curve_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2dGridEval_Ellipse_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2dGridEval_Hyperbola_Test.cxx create mode 100644 src/ModelingData/TKG2d/GTests/Geom2dGridEval_Parabola_Test.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/FILES.cmake create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BSplineCurve.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BSplineCurve.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BezierCurve.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BezierCurve.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Circle.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Circle.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Curve.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Curve.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Ellipse.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Ellipse.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Hyperbola.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Hyperbola.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Line.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OffsetCurve.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OffsetCurve.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OtherCurve.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OtherCurve.hxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Parabola.cxx create mode 100644 src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Parabola.hxx diff --git a/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.cxx b/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.cxx index 8a28e93313..f30a7b3893 100644 --- a/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.cxx +++ b/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.cxx @@ -189,23 +189,9 @@ void BSplCLib_Cache::calculateDerivativeLocal(double theLocalParam, void BSplCLib_Cache::D0(const double& theParameter, gp_Pnt2d& thePoint) const { - double aNewParameter = myParams.PeriodicNormalization(theParameter); - aNewParameter = (aNewParameter - myParams.SpanStart) / myParams.SpanLength; - - double* aPolesArray = const_cast(myPolesWeightsBuffer); - double aPoint[4]; - const int aDimension = myRowLength; - - PLib::NoDerivativeEvalPolynomial(aNewParameter, - myParams.Degree, - aDimension, - myParams.Degree * aDimension, - aPolesArray[0], - aPoint[0]); - - thePoint.SetCoord(aPoint[0], aPoint[1]); - if (myIsRational) - thePoint.ChangeCoord().Divide(aPoint[2]); + double aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + D0Local(aLocalParam, thePoint); } //================================================================================================== @@ -240,15 +226,9 @@ void BSplCLib_Cache::D0Local(double theLocalParam, gp_Pnt& thePoint) const void BSplCLib_Cache::D1(const double& theParameter, gp_Pnt2d& thePoint, gp_Vec2d& theTangent) const { - int aDimension = myRowLength; - double aPntDeriv[8]; // result storage (point and derivative coordinates) - - calculateDerivative(theParameter, 1, aPntDeriv); - if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative - aDimension -= 1; - - thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1]); - theTangent.SetCoord(aPntDeriv[aDimension], aPntDeriv[aDimension + 1]); + double aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + D1Local(aLocalParam, thePoint, theTangent); } void BSplCLib_Cache::D1(const double& theParameter, gp_Pnt& thePoint, gp_Vec& theTangent) const @@ -275,16 +255,9 @@ void BSplCLib_Cache::D2(const double& theParameter, gp_Vec2d& theTangent, gp_Vec2d& theCurvature) const { - int aDimension = myRowLength; - double aPntDeriv[12]; // result storage (point and derivatives coordinates) - - calculateDerivative(theParameter, 2, aPntDeriv); - if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative - aDimension -= 1; - - thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1]); - theTangent.SetCoord(aPntDeriv[aDimension], aPntDeriv[aDimension + 1]); - theCurvature.SetCoord(aPntDeriv[aDimension << 1], aPntDeriv[(aDimension << 1) + 1]); + double aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + D2Local(aLocalParam, thePoint, theTangent, theCurvature); } void BSplCLib_Cache::D2(const double& theParameter, @@ -320,19 +293,9 @@ void BSplCLib_Cache::D3(const double& theParameter, gp_Vec2d& theCurvature, gp_Vec2d& theTorsion) const { - int aDimension = myRowLength; - double aPntDeriv[16]; // result storage (point and derivatives coordinates) - - calculateDerivative(theParameter, 3, aPntDeriv); - if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative - aDimension -= 1; - - thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1]); - theTangent.SetCoord(aPntDeriv[aDimension], aPntDeriv[aDimension + 1]); - int aShift = aDimension << 1; - theCurvature.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1]); - aShift += aDimension; - theTorsion.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1]); + double aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + D3Local(aLocalParam, thePoint, theTangent, theCurvature, theTorsion); } void BSplCLib_Cache::D3(const double& theParameter, @@ -365,3 +328,72 @@ void BSplCLib_Cache::D3Local(double theLocalParam, theCurvature.SetCoord(aDerivArray[aShift2], aDerivArray[aShift2 + 1], aDerivArray[aShift2 + 2]); theTorsion.SetCoord(aDerivArray[aShift3], aDerivArray[aShift3 + 1], aDerivArray[aShift3 + 2]); } + +//================================================================================================== + +void BSplCLib_Cache::D0Local(double theLocalParam, gp_Pnt2d& thePoint) const +{ + double aPoint[4]; + + PLib::NoDerivativeEvalPolynomial(theLocalParam, + myParams.Degree, + myRowLength, + myParams.Degree * myRowLength, + myPolesWeightsBuffer[0], + aPoint[0]); + + thePoint.SetCoord(aPoint[0], aPoint[1]); + if (myIsRational) + { + thePoint.ChangeCoord().Divide(aPoint[2]); + } +} + +//================================================================================================== + +void BSplCLib_Cache::D1Local(double theLocalParam, gp_Pnt2d& thePoint, gp_Vec2d& theTangent) const +{ + double aDerivArray[8]; + calculateDerivativeLocal(theLocalParam, 1, aDerivArray); + + const int aDim = myIsRational ? myRowLength - 1 : myRowLength; + thePoint.SetCoord(aDerivArray[0], aDerivArray[1]); + theTangent.SetCoord(aDerivArray[aDim], aDerivArray[aDim + 1]); +} + +//================================================================================================== + +void BSplCLib_Cache::D2Local(double theLocalParam, + gp_Pnt2d& thePoint, + gp_Vec2d& theTangent, + gp_Vec2d& theCurvature) const +{ + double aDerivArray[12]; + calculateDerivativeLocal(theLocalParam, 2, aDerivArray); + + const int aDim = myIsRational ? myRowLength - 1 : myRowLength; + const int aShift = aDim << 1; + thePoint.SetCoord(aDerivArray[0], aDerivArray[1]); + theTangent.SetCoord(aDerivArray[aDim], aDerivArray[aDim + 1]); + theCurvature.SetCoord(aDerivArray[aShift], aDerivArray[aShift + 1]); +} + +//================================================================================================== + +void BSplCLib_Cache::D3Local(double theLocalParam, + gp_Pnt2d& thePoint, + gp_Vec2d& theTangent, + gp_Vec2d& theCurvature, + gp_Vec2d& theTorsion) const +{ + double aDerivArray[16]; + calculateDerivativeLocal(theLocalParam, 3, aDerivArray); + + const int aDim = myIsRational ? myRowLength - 1 : myRowLength; + const int aShift2 = aDim << 1; + const int aShift3 = aShift2 + aDim; + thePoint.SetCoord(aDerivArray[0], aDerivArray[1]); + theTangent.SetCoord(aDerivArray[aDim], aDerivArray[aDim + 1]); + theCurvature.SetCoord(aDerivArray[aShift2], aDerivArray[aShift2 + 1]); + theTorsion.SetCoord(aDerivArray[aShift3], aDerivArray[aShift3 + 1]); +} diff --git a/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.hxx b/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.hxx index 87015b6e6f..56c95f38af 100644 --- a/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.hxx +++ b/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.hxx @@ -155,6 +155,43 @@ public: gp_Vec& theCurvature, gp_Vec& theTorsion) const; + //! Calculates the 2D point using pre-computed local parameter in [0, 1] range. + //! This bypasses periodic normalization and local parameter calculation. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[out] thePoint the result of calculation (the point on the curve) + Standard_EXPORT void D0Local(double theLocalParam, gp_Pnt2d& thePoint) const; + + //! Calculates the 2D point and first derivative using pre-computed local parameter. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[out] thePoint the point on the curve + //! @param[out] theTangent first derivative (tangent vector) + Standard_EXPORT void D1Local(double theLocalParam, + gp_Pnt2d& thePoint, + gp_Vec2d& theTangent) const; + + //! Calculates the 2D point, first and second derivatives using pre-computed local parameter. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[out] thePoint the point on the curve + //! @param[out] theTangent first derivative (tangent vector) + //! @param[out] theCurvature second derivative (curvature vector) + Standard_EXPORT void D2Local(double theLocalParam, + gp_Pnt2d& thePoint, + gp_Vec2d& theTangent, + gp_Vec2d& theCurvature) const; + + //! Calculates the 2D point, first, second and third derivatives using pre-computed local + //! parameter. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[out] thePoint the point on the curve + //! @param[out] theTangent first derivative (tangent vector) + //! @param[out] theCurvature second derivative (curvature vector) + //! @param[out] theTorsion third derivative (torsion vector) + Standard_EXPORT void D3Local(double theLocalParam, + gp_Pnt2d& thePoint, + gp_Vec2d& theTangent, + gp_Vec2d& theCurvature, + gp_Vec2d& theTorsion) const; + DEFINE_STANDARD_RTTIEXT(BSplCLib_Cache, Standard_Transient) protected: diff --git a/src/ModelingData/TKG2d/GTests/FILES.cmake b/src/ModelingData/TKG2d/GTests/FILES.cmake index daacfd752d..24c0e70e76 100644 --- a/src/ModelingData/TKG2d/GTests/FILES.cmake +++ b/src/ModelingData/TKG2d/GTests/FILES.cmake @@ -10,5 +10,10 @@ set(OCCT_TKG2d_GTests_FILES Geom2dAPI_InterCurveCurve_Test.cxx Geom2dGcc_Circ2d2TanOn_Test.cxx Geom2dGcc_Circ2d2TanRad_Test.cxx + Geom2dGridEval_BezierCurve_Test.cxx + Geom2dGridEval_Curve_Test.cxx + Geom2dGridEval_Ellipse_Test.cxx + Geom2dGridEval_Hyperbola_Test.cxx + Geom2dGridEval_Parabola_Test.cxx Geom2dHash_CurveHasher_Test.cxx ) diff --git a/src/ModelingData/TKG2d/GTests/Geom2dGridEval_BezierCurve_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_BezierCurve_Test.cxx new file mode 100644 index 0000000000..0d0d3dd95a --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_BezierCurve_Test.cxx @@ -0,0 +1,190 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include +#include + +#include + +namespace +{ +const double THE_TOLERANCE = 1e-10; + +NCollection_Array1 CreateUniformParams(double theFirst, double theLast, int theNbPoints) +{ + NCollection_Array1 aParams(1, theNbPoints); + const double aStep = (theLast - theFirst) / (theNbPoints - 1); + for (int i = 1; i <= theNbPoints; ++i) + { + aParams.SetValue(i, theFirst + (i - 1) * aStep); + } + return aParams; +} +} // namespace + +TEST(Geom2dGridEval_BezierCurveTest, BasicD0) +{ + NCollection_Array1 aPoles(1, 4); + aPoles.SetValue(1, gp_Pnt2d(0, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 2)); + aPoles.SetValue(3, gp_Pnt2d(3, 2)); + aPoles.SetValue(4, gp_Pnt2d(4, 0)); + occ::handle aBezier = new Geom2d_BezierCurve(aPoles); + + Geom2dGridEval_BezierCurve anEval(aBezier); + EXPECT_FALSE(anEval.Geometry().IsNull()); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + EXPECT_EQ(aGrid.Size(), 11); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aExpected = aBezier->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BezierCurveTest, D1) +{ + NCollection_Array1 aPoles(1, 4); + aPoles.SetValue(1, gp_Pnt2d(0, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 2)); + aPoles.SetValue(3, gp_Pnt2d(3, 2)); + aPoles.SetValue(4, gp_Pnt2d(4, 0)); + occ::handle aBezier = new Geom2d_BezierCurve(aPoles); + + Geom2dGridEval_BezierCurve anEval(aBezier); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD1(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1; + aBezier->D1(aParams.Value(i), aPnt, aD1); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BezierCurveTest, D2) +{ + NCollection_Array1 aPoles(1, 4); + aPoles.SetValue(1, gp_Pnt2d(0, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 2)); + aPoles.SetValue(3, gp_Pnt2d(3, 2)); + aPoles.SetValue(4, gp_Pnt2d(4, 0)); + occ::handle aBezier = new Geom2d_BezierCurve(aPoles); + + Geom2dGridEval_BezierCurve anEval(aBezier); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD2(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2; + aBezier->D2(aParams.Value(i), aPnt, aD1, aD2); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BezierCurveTest, D3) +{ + NCollection_Array1 aPoles(1, 4); + aPoles.SetValue(1, gp_Pnt2d(0, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 2)); + aPoles.SetValue(3, gp_Pnt2d(3, 2)); + aPoles.SetValue(4, gp_Pnt2d(4, 0)); + occ::handle aBezier = new Geom2d_BezierCurve(aPoles); + + Geom2dGridEval_BezierCurve anEval(aBezier); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2, aD3; + aBezier->D3(aParams.Value(i), aPnt, aD1, aD2, aD3); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D3 - aD3).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BezierCurveTest, DN_BeyondDegree) +{ + NCollection_Array1 aPoles(1, 4); + aPoles.SetValue(1, gp_Pnt2d(0, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 2)); + aPoles.SetValue(3, gp_Pnt2d(3, 2)); + aPoles.SetValue(4, gp_Pnt2d(4, 0)); + occ::handle aBezier = new Geom2d_BezierCurve(aPoles); + + Geom2dGridEval_BezierCurve anEval(aBezier); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + // Degree 3 Bezier, 4th derivative should be zero + NCollection_Array1 aGrid = anEval.EvaluateGridDN(aParams, 4); + + for (int i = 1; i <= 11; ++i) + { + EXPECT_NEAR(aGrid.Value(i).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BezierCurveTest, RationalBezier) +{ + NCollection_Array1 aPoles(1, 3); + NCollection_Array1 aWeights(1, 3); + + aPoles.SetValue(1, gp_Pnt2d(1, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 1)); + aPoles.SetValue(3, gp_Pnt2d(0, 1)); + + aWeights.SetValue(1, 1.0); + aWeights.SetValue(2, 1.0 / std::sqrt(2.0)); + aWeights.SetValue(3, 1.0); + + occ::handle aBezier = new Geom2d_BezierCurve(aPoles, aWeights); + + Geom2dGridEval_BezierCurve anEval(aBezier); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 21); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 21; ++i) + { + gp_Pnt2d aExpected = aBezier->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Curve_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Curve_Test.cxx new file mode 100644 index 0000000000..1b3154fb04 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Curve_Test.cxx @@ -0,0 +1,714 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +const double THE_TOLERANCE = 1e-10; + +//! Helper function to create uniform parameters +NCollection_Array1 CreateUniformParams(double theFirst, double theLast, int theNbPoints) +{ + NCollection_Array1 aParams(1, theNbPoints); + const double aStep = (theLast - theFirst) / (theNbPoints - 1); + for (int i = 1; i <= theNbPoints; ++i) + { + aParams.SetValue(i, theFirst + (i - 1) * aStep); + } + return aParams; +} + +//! Helper function to create a simple 2D B-spline curve +occ::handle CreateSimpleBSpline2d() +{ + NCollection_Array1 aPoles(1, 4); + aPoles.SetValue(1, gp_Pnt2d(0, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 2)); + aPoles.SetValue(3, gp_Pnt2d(3, 2)); + aPoles.SetValue(4, gp_Pnt2d(4, 0)); + + NCollection_Array1 aKnots(1, 2); + NCollection_Array1 aMults(1, 2); + aKnots.SetValue(1, 0.0); + aKnots.SetValue(2, 1.0); + aMults.SetValue(1, 4); + aMults.SetValue(2, 4); + + return new Geom2d_BSplineCurve(aPoles, aKnots, aMults, 3); +} +} // namespace + +//================================================================================================== +// Tests for Geom2dGridEval_Line +//================================================================================================== + +TEST(Geom2dGridEval_LineTest, BasicEvaluation) +{ + occ::handle aGeomLine = new Geom2d_Line(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)); + + Geom2dGridEval_Line anEval(aGeomLine); + EXPECT_FALSE(anEval.Geometry().IsNull()); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 10.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + EXPECT_EQ(aGrid.Size(), 11); + + for (int i = 1; i <= 11; ++i) + { + const double t = aParams.Value(i); + EXPECT_NEAR(aGrid.Value(i).X(), t, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(i).Y(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_LineTest, NonOriginLine) +{ + occ::handle aGeomLine = new Geom2d_Line(gp_Pnt2d(1, 2), gp_Dir2d(1, 1)); + + Geom2dGridEval_Line anEval(aGeomLine); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 5.0, 6); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + EXPECT_NEAR(aGrid.Value(1).Distance(gp_Pnt2d(1, 2)), 0.0, THE_TOLERANCE); + + const double d = 5.0 / std::sqrt(2.0); + EXPECT_NEAR(aGrid.Value(6).Distance(gp_Pnt2d(1 + d, 2 + d)), 0.0, THE_TOLERANCE); +} + +TEST(Geom2dGridEval_LineTest, DerivativeD1) +{ + occ::handle aGeomLine = new Geom2d_Line(gp_Pnt2d(0, 0), gp_Dir2d(1, 2)); + Geom2dGridEval_Line anEval(aGeomLine); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 5.0, 6); + + NCollection_Array1 aGrid = anEval.EvaluateGridD1(aParams); + EXPECT_EQ(aGrid.Size(), 6); + + gp_Dir2d aDir(1, 2); + for (int i = 1; i <= 6; ++i) + { + EXPECT_NEAR(aGrid.Value(i).D1.X(), aDir.X(), THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(i).D1.Y(), aDir.Y(), THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_LineTest, DerivativeD2D3) +{ + occ::handle aGeomLine = new Geom2d_Line(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)); + Geom2dGridEval_Line anEval(aGeomLine); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 5.0, 6); + + NCollection_Array1 aGridD2 = anEval.EvaluateGridD2(aParams); + NCollection_Array1 aGridD3 = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 6; ++i) + { + EXPECT_NEAR(aGridD2.Value(i).D2.Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR(aGridD3.Value(i).D2.Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR(aGridD3.Value(i).D3.Magnitude(), 0.0, THE_TOLERANCE); + } +} + +//================================================================================================== +// Tests for Geom2dGridEval_Circle +//================================================================================================== + +TEST(Geom2dGridEval_CircleTest, BasicEvaluation) +{ + occ::handle aGeomCircle = + new Geom2d_Circle(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 2.0); + + Geom2dGridEval_Circle anEval(aGeomCircle); + EXPECT_FALSE(anEval.Geometry().IsNull()); + + NCollection_Array1 aParams(1, 5); + aParams.SetValue(1, 0.0); + aParams.SetValue(2, M_PI / 2); + aParams.SetValue(3, M_PI); + aParams.SetValue(4, 3 * M_PI / 2); + aParams.SetValue(5, 2 * M_PI); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + EXPECT_NEAR(aGrid.Value(1).X(), 2.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(1).Y(), 0.0, THE_TOLERANCE); + + EXPECT_NEAR(aGrid.Value(2).X(), 0.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(2).Y(), 2.0, THE_TOLERANCE); + + EXPECT_NEAR(aGrid.Value(3).X(), -2.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(3).Y(), 0.0, THE_TOLERANCE); + + EXPECT_NEAR(aGrid.Value(4).X(), 0.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(4).Y(), -2.0, THE_TOLERANCE); + + EXPECT_NEAR(aGrid.Value(5).X(), 2.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(5).Y(), 0.0, THE_TOLERANCE); +} + +TEST(Geom2dGridEval_CircleTest, DerivativeD1) +{ + occ::handle aGeomCircle = + new Geom2d_Circle(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 2.0); + Geom2dGridEval_Circle anEval(aGeomCircle); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 9); + + NCollection_Array1 aGrid = anEval.EvaluateGridD1(aParams); + + for (int i = 1; i <= 9; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1; + aGeomCircle->D1(aParams.Value(i), aPnt, aD1); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CircleTest, DerivativeD2) +{ + occ::handle aGeomCircle = + new Geom2d_Circle(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 2.0); + Geom2dGridEval_Circle anEval(aGeomCircle); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 9); + + NCollection_Array1 aGrid = anEval.EvaluateGridD2(aParams); + + for (int i = 1; i <= 9; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2; + aGeomCircle->D2(aParams.Value(i), aPnt, aD1, aD2); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CircleTest, DerivativeD3) +{ + occ::handle aGeomCircle = + new Geom2d_Circle(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 2.0); + Geom2dGridEval_Circle anEval(aGeomCircle); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 9); + + NCollection_Array1 aGrid = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 9; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2, aD3; + aGeomCircle->D3(aParams.Value(i), aPnt, aD1, aD2, aD3); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D3 - aD3).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +//================================================================================================== +// Tests for Geom2dGridEval_BSplineCurve +//================================================================================================== + +TEST(Geom2dGridEval_BSplineCurveTest, BasicEvaluation) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + + Geom2dGridEval_BSplineCurve anEval(aCurve); + EXPECT_FALSE(anEval.Geometry().IsNull()); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + EXPECT_EQ(aGrid.Size(), 11); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aExpected = aCurve->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BSplineCurveTest, EndpointsMatch) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + + Geom2dGridEval_BSplineCurve anEval(aCurve); + + NCollection_Array1 aParams(1, 2); + aParams.SetValue(1, 0.0); + aParams.SetValue(2, 1.0); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + EXPECT_NEAR(aGrid.Value(1).Distance(gp_Pnt2d(0, 0)), 0.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(2).Distance(gp_Pnt2d(4, 0)), 0.0, THE_TOLERANCE); +} + +TEST(Geom2dGridEval_BSplineCurveTest, DerivativeD1) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + Geom2dGridEval_BSplineCurve anEval(aCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD1(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1; + aCurve->D1(aParams.Value(i), aPnt, aD1); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BSplineCurveTest, DerivativeD2) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + Geom2dGridEval_BSplineCurve anEval(aCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD2(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2; + aCurve->D2(aParams.Value(i), aPnt, aD1, aD2); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BSplineCurveTest, DerivativeD3) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + Geom2dGridEval_BSplineCurve anEval(aCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2, aD3; + aCurve->D3(aParams.Value(i), aPnt, aD1, aD2, aD3); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D3 - aD3).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BSplineCurveTest, DerivativeDN_BeyondDegree) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + Geom2dGridEval_BSplineCurve anEval(aCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridDN(aParams, 4); + + for (int i = 1; i <= 11; ++i) + { + EXPECT_NEAR(aGrid.Value(i).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_BSplineCurveTest, MultiSpanBSpline) +{ + NCollection_Array1 aPoles(1, 6); + aPoles.SetValue(1, gp_Pnt2d(0, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 2)); + aPoles.SetValue(3, gp_Pnt2d(2, 2)); + aPoles.SetValue(4, gp_Pnt2d(3, 0)); + aPoles.SetValue(5, gp_Pnt2d(4, -1)); + aPoles.SetValue(6, gp_Pnt2d(5, 0)); + + NCollection_Array1 aKnots(1, 3); + NCollection_Array1 aMults(1, 3); + aKnots.SetValue(1, 0.0); + aKnots.SetValue(2, 0.5); + aKnots.SetValue(3, 1.0); + aMults.SetValue(1, 4); + aMults.SetValue(2, 2); + aMults.SetValue(3, 4); + + occ::handle aCurve = new Geom2d_BSplineCurve(aPoles, aKnots, aMults, 3); + + Geom2dGridEval_BSplineCurve anEval(aCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 31); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 31; ++i) + { + gp_Pnt2d aExpected = aCurve->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +//================================================================================================== +// Tests for Geom2dGridEval_Curve (unified dispatcher) +//================================================================================================== + +TEST(Geom2dGridEval_CurveTest, LineDispatch) +{ + occ::handle aGeomLine = new Geom2d_Line(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)); + Geom2dAdaptor_Curve anAdaptor(aGeomLine); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_Line); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 10.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + EXPECT_EQ(aGrid.Size(), 11); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aExpected = aGeomLine->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, CircleDispatch) +{ + occ::handle aGeomCircle = + new Geom2d_Circle(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 2.0); + Geom2dAdaptor_Curve anAdaptor(aGeomCircle); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_Circle); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 17); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 17; ++i) + { + gp_Pnt2d aExpected = aGeomCircle->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, EllipseDispatch) +{ + occ::handle anEllipse = + new Geom2d_Ellipse(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + Geom2dAdaptor_Curve anAdaptor(anEllipse); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_Ellipse); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 13); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 13; ++i) + { + gp_Pnt2d aExpected = anEllipse->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, HyperbolaDispatch) +{ + occ::handle aHypr = + new Geom2d_Hyperbola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + Geom2dAdaptor_Curve anAdaptor(aHypr); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_Hyperbola); + + NCollection_Array1 aParams = CreateUniformParams(-2.0, 2.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aExpected = aHypr->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, ParabolaDispatch) +{ + occ::handle aParab = + new Geom2d_Parabola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 1.0); + Geom2dAdaptor_Curve anAdaptor(aParab); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_Parabola); + + NCollection_Array1 aParams = CreateUniformParams(-2.0, 2.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aExpected = aParab->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, BSplineDispatch) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + Geom2dAdaptor_Curve anAdaptor(aCurve); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_BSplineCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 21); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 21; ++i) + { + gp_Pnt2d aExpected = aCurve->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, BezierCurveDispatch) +{ + NCollection_Array1 aPoles(1, 4); + aPoles.SetValue(1, gp_Pnt2d(0, 0)); + aPoles.SetValue(2, gp_Pnt2d(1, 2)); + aPoles.SetValue(3, gp_Pnt2d(3, 2)); + aPoles.SetValue(4, gp_Pnt2d(4, 0)); + occ::handle aBezier = new Geom2d_BezierCurve(aPoles); + Geom2dAdaptor_Curve anAdaptor(aBezier); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_BezierCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aExpected = aBezier->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, OffsetCurveDispatch) +{ + occ::handle aLine = new Geom2d_Line(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)); + occ::handle anOffset = new Geom2d_OffsetCurve(aLine, 1.0); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anOffset); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_OffsetCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 5.0, 6); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + for (int i = 1; i <= 6; ++i) + { + gp_Pnt2d aExpected = anOffset->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, DirectHandleInit) +{ + occ::handle aGeomLine = new Geom2d_Line(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(aGeomLine); + EXPECT_TRUE(anEval.IsInitialized()); + EXPECT_EQ(anEval.GetType(), GeomAbs_Line); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 10.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + EXPECT_EQ(aGrid.Size(), 11); + EXPECT_NEAR(aGrid.Value(1).X(), 0.0, THE_TOLERANCE); +} + +TEST(Geom2dGridEval_CurveTest, UninitializedState) +{ + Geom2dGridEval_Curve anEval; + EXPECT_FALSE(anEval.IsInitialized()); + + NCollection_Array1 aEmptyParams; + NCollection_Array1 aGrid = anEval.EvaluateGrid(aEmptyParams); + EXPECT_TRUE(aGrid.IsEmpty()); +} + +TEST(Geom2dGridEval_CurveTest, EmptyParams) +{ + occ::handle aGeomLine = new Geom2d_Line(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)); + Geom2dAdaptor_Curve anAdaptor(aGeomLine); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + EXPECT_TRUE(anEval.IsInitialized()); + + NCollection_Array1 aEmptyParams; + NCollection_Array1 aGrid = anEval.EvaluateGrid(aEmptyParams); + EXPECT_TRUE(aGrid.IsEmpty()); +} + +TEST(Geom2dGridEval_CurveTest, UnifiedDerivativeD1) +{ + occ::handle aGeomCircle = + new Geom2d_Circle(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 2.0); + Geom2dAdaptor_Curve anAdaptor(aGeomCircle); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 9); + + NCollection_Array1 aGrid = anEval.EvaluateGridD1(aParams); + + for (int i = 1; i <= 9; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1; + aGeomCircle->D1(aParams.Value(i), aPnt, aD1); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, UnifiedDerivativeD2) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + Geom2dAdaptor_Curve anAdaptor(aCurve); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD2(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2; + aCurve->D2(aParams.Value(i), aPnt, aD1, aD2); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, UnifiedDerivativeD3) +{ + occ::handle aCurve = CreateSimpleBSpline2d(); + Geom2dAdaptor_Curve anAdaptor(aCurve); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anAdaptor); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 1.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2, aD3; + aCurve->D3(aParams.Value(i), aPnt, aD1, aD2, aD3); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D3 - aD3).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_CurveTest, OffsetCurveDerivativeD3) +{ + occ::handle aCircle = + new Geom2d_Circle(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 2.0); + occ::handle anOffset = new Geom2d_OffsetCurve(aCircle, 0.5); + + Geom2dGridEval_Curve anEval; + anEval.Initialize(anOffset); + EXPECT_EQ(anEval.GetType(), GeomAbs_OffsetCurve); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 9); + + NCollection_Array1 aGrid = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 9; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2, aD3; + anOffset->D3(aParams.Value(i), aPnt, aD1, aD2, aD3); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D3 - aD3).Magnitude(), 0.0, THE_TOLERANCE); + } +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Ellipse_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Ellipse_Test.cxx new file mode 100644 index 0000000000..8949098bde --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Ellipse_Test.cxx @@ -0,0 +1,157 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +const double THE_TOLERANCE = 1e-10; + +NCollection_Array1 CreateUniformParams(double theFirst, double theLast, int theNbPoints) +{ + NCollection_Array1 aParams(1, theNbPoints); + const double aStep = (theLast - theFirst) / (theNbPoints - 1); + for (int i = 1; i <= theNbPoints; ++i) + { + aParams.SetValue(i, theFirst + (i - 1) * aStep); + } + return aParams; +} +} // namespace + +TEST(Geom2dGridEval_EllipseTest, BasicD0) +{ + occ::handle anEllipse = + new Geom2d_Ellipse(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Ellipse anEval(anEllipse); + EXPECT_FALSE(anEval.Geometry().IsNull()); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 13); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + EXPECT_EQ(aGrid.Size(), 13); + + for (int i = 1; i <= 13; ++i) + { + gp_Pnt2d aExpected = anEllipse->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_EllipseTest, CardinalPoints) +{ + occ::handle anEllipse = + new Geom2d_Ellipse(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Ellipse anEval(anEllipse); + + NCollection_Array1 aParams(1, 4); + aParams.SetValue(1, 0.0); + aParams.SetValue(2, M_PI / 2); + aParams.SetValue(3, M_PI); + aParams.SetValue(4, 3 * M_PI / 2); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + // u=0: (3, 0) + EXPECT_NEAR(aGrid.Value(1).X(), 3.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(1).Y(), 0.0, THE_TOLERANCE); + + // u=PI/2: (0, 2) + EXPECT_NEAR(aGrid.Value(2).X(), 0.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(2).Y(), 2.0, THE_TOLERANCE); + + // u=PI: (-3, 0) + EXPECT_NEAR(aGrid.Value(3).X(), -3.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(3).Y(), 0.0, THE_TOLERANCE); + + // u=3PI/2: (0, -2) + EXPECT_NEAR(aGrid.Value(4).X(), 0.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(4).Y(), -2.0, THE_TOLERANCE); +} + +TEST(Geom2dGridEval_EllipseTest, D1) +{ + occ::handle anEllipse = + new Geom2d_Ellipse(gp_Ax22d(gp_Pnt2d(1, 2), gp_Dir2d(1, 0)), 5.0, 3.0); + + Geom2dGridEval_Ellipse anEval(anEllipse); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 13); + + NCollection_Array1 aGrid = anEval.EvaluateGridD1(aParams); + + for (int i = 1; i <= 13; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1; + anEllipse->D1(aParams.Value(i), aPnt, aD1); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_EllipseTest, D2) +{ + occ::handle anEllipse = + new Geom2d_Ellipse(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Ellipse anEval(anEllipse); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 9); + + NCollection_Array1 aGrid = anEval.EvaluateGridD2(aParams); + + for (int i = 1; i <= 9; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2; + anEllipse->D2(aParams.Value(i), aPnt, aD1, aD2); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_EllipseTest, D3) +{ + occ::handle anEllipse = + new Geom2d_Ellipse(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Ellipse anEval(anEllipse); + + NCollection_Array1 aParams = CreateUniformParams(0.0, 2 * M_PI, 9); + + NCollection_Array1 aGrid = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 9; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2, aD3; + anEllipse->D3(aParams.Value(i), aPnt, aD1, aD2, aD3); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D3 - aD3).Magnitude(), 0.0, THE_TOLERANCE); + } +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Hyperbola_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Hyperbola_Test.cxx new file mode 100644 index 0000000000..7514343e22 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Hyperbola_Test.cxx @@ -0,0 +1,142 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +const double THE_TOLERANCE = 1e-10; + +NCollection_Array1 CreateUniformParams(double theFirst, double theLast, int theNbPoints) +{ + NCollection_Array1 aParams(1, theNbPoints); + const double aStep = (theLast - theFirst) / (theNbPoints - 1); + for (int i = 1; i <= theNbPoints; ++i) + { + aParams.SetValue(i, theFirst + (i - 1) * aStep); + } + return aParams; +} +} // namespace + +TEST(Geom2dGridEval_HyperbolaTest, BasicD0) +{ + occ::handle aHypr = + new Geom2d_Hyperbola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Hyperbola anEval(aHypr); + EXPECT_FALSE(anEval.Geometry().IsNull()); + + NCollection_Array1 aParams = CreateUniformParams(-2.0, 2.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + EXPECT_EQ(aGrid.Size(), 11); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aExpected = aHypr->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_HyperbolaTest, AtOrigin) +{ + occ::handle aHypr = + new Geom2d_Hyperbola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Hyperbola anEval(aHypr); + + NCollection_Array1 aParams(1, 1); + aParams.SetValue(1, 0.0); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + // At u=0: P = Center + MajR*cosh(0)*X + MinR*sinh(0)*Y = Center + MajR*X = (3, 0) + EXPECT_NEAR(aGrid.Value(1).X(), 3.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(1).Y(), 0.0, THE_TOLERANCE); +} + +TEST(Geom2dGridEval_HyperbolaTest, D1) +{ + occ::handle aHypr = + new Geom2d_Hyperbola(gp_Ax22d(gp_Pnt2d(1, 2), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Hyperbola anEval(aHypr); + + NCollection_Array1 aParams = CreateUniformParams(-2.0, 2.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD1(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1; + aHypr->D1(aParams.Value(i), aPnt, aD1); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_HyperbolaTest, D2) +{ + occ::handle aHypr = + new Geom2d_Hyperbola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Hyperbola anEval(aHypr); + + NCollection_Array1 aParams = CreateUniformParams(-2.0, 2.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD2(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2; + aHypr->D2(aParams.Value(i), aPnt, aD1, aD2); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_HyperbolaTest, D3) +{ + occ::handle aHypr = + new Geom2d_Hyperbola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 3.0, 2.0); + + Geom2dGridEval_Hyperbola anEval(aHypr); + + NCollection_Array1 aParams = CreateUniformParams(-2.0, 2.0, 11); + + NCollection_Array1 aGrid = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 11; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2, aD3; + aHypr->D3(aParams.Value(i), aPnt, aD1, aD2, aD3); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D3 - aD3).Magnitude(), 0.0, THE_TOLERANCE); + } +} diff --git a/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Parabola_Test.cxx b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Parabola_Test.cxx new file mode 100644 index 0000000000..e276bcaf37 --- /dev/null +++ b/src/ModelingData/TKG2d/GTests/Geom2dGridEval_Parabola_Test.cxx @@ -0,0 +1,160 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +const double THE_TOLERANCE = 1e-10; + +NCollection_Array1 CreateUniformParams(double theFirst, double theLast, int theNbPoints) +{ + NCollection_Array1 aParams(1, theNbPoints); + const double aStep = (theLast - theFirst) / (theNbPoints - 1); + for (int i = 1; i <= theNbPoints; ++i) + { + aParams.SetValue(i, theFirst + (i - 1) * aStep); + } + return aParams; +} +} // namespace + +TEST(Geom2dGridEval_ParabolaTest, BasicD0) +{ + occ::handle aParab = + new Geom2d_Parabola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 1.0); + + Geom2dGridEval_Parabola anEval(aParab); + EXPECT_FALSE(anEval.Geometry().IsNull()); + + NCollection_Array1 aParams = CreateUniformParams(-3.0, 3.0, 13); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + EXPECT_EQ(aGrid.Size(), 13); + + for (int i = 1; i <= 13; ++i) + { + gp_Pnt2d aExpected = aParab->Value(aParams.Value(i)); + EXPECT_NEAR(aGrid.Value(i).Distance(aExpected), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_ParabolaTest, AtOrigin) +{ + occ::handle aParab = + new Geom2d_Parabola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 1.0); + + Geom2dGridEval_Parabola anEval(aParab); + + NCollection_Array1 aParams(1, 1); + aParams.SetValue(1, 0.0); + + NCollection_Array1 aGrid = anEval.EvaluateGrid(aParams); + + // At u=0: P = Center + 0*XDir + 0*YDir = (0, 0) + EXPECT_NEAR(aGrid.Value(1).X(), 0.0, THE_TOLERANCE); + EXPECT_NEAR(aGrid.Value(1).Y(), 0.0, THE_TOLERANCE); +} + +TEST(Geom2dGridEval_ParabolaTest, D1) +{ + occ::handle aParab = + new Geom2d_Parabola(gp_Ax22d(gp_Pnt2d(1, 2), gp_Dir2d(1, 0)), 2.0); + + Geom2dGridEval_Parabola anEval(aParab); + + NCollection_Array1 aParams = CreateUniformParams(-3.0, 3.0, 13); + + NCollection_Array1 aGrid = anEval.EvaluateGridD1(aParams); + + for (int i = 1; i <= 13; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1; + aParab->D1(aParams.Value(i), aPnt, aD1); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_ParabolaTest, D2) +{ + occ::handle aParab = + new Geom2d_Parabola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 1.0); + + Geom2dGridEval_Parabola anEval(aParab); + + NCollection_Array1 aParams = CreateUniformParams(-3.0, 3.0, 13); + + NCollection_Array1 aGrid = anEval.EvaluateGridD2(aParams); + + for (int i = 1; i <= 13; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2; + aParab->D2(aParams.Value(i), aPnt, aD1, aD2); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_ParabolaTest, D3) +{ + occ::handle aParab = + new Geom2d_Parabola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 1.0); + + Geom2dGridEval_Parabola anEval(aParab); + + NCollection_Array1 aParams = CreateUniformParams(-3.0, 3.0, 13); + + NCollection_Array1 aGrid = anEval.EvaluateGridD3(aParams); + + for (int i = 1; i <= 13; ++i) + { + gp_Pnt2d aPnt; + gp_Vec2d aD1, aD2, aD3; + aParab->D3(aParams.Value(i), aPnt, aD1, aD2, aD3); + EXPECT_NEAR(aGrid.Value(i).Point.Distance(aPnt), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D1 - aD1).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D2 - aD2).Magnitude(), 0.0, THE_TOLERANCE); + EXPECT_NEAR((aGrid.Value(i).D3 - aD3).Magnitude(), 0.0, THE_TOLERANCE); + } +} + +TEST(Geom2dGridEval_ParabolaTest, DN_HighOrder) +{ + occ::handle aParab = + new Geom2d_Parabola(gp_Ax22d(gp_Pnt2d(0, 0), gp_Dir2d(1, 0)), 1.0); + + Geom2dGridEval_Parabola anEval(aParab); + + NCollection_Array1 aParams = CreateUniformParams(-3.0, 3.0, 7); + + // D3 and higher should be zero for parabola + NCollection_Array1 aGrid = anEval.EvaluateGridDN(aParams, 3); + + for (int i = 1; i <= 7; ++i) + { + EXPECT_NEAR(aGrid.Value(i).Magnitude(), 0.0, THE_TOLERANCE); + } +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/FILES.cmake b/src/ModelingData/TKG2d/Geom2dGridEval/FILES.cmake new file mode 100644 index 0000000000..c26882f040 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/FILES.cmake @@ -0,0 +1,25 @@ +# Source files for Geom2dGridEval package +set(OCCT_Geom2dGridEval_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") + +set(OCCT_Geom2dGridEval_FILES + Geom2dGridEval.hxx + Geom2dGridEval_BezierCurve.cxx + Geom2dGridEval_BezierCurve.hxx + Geom2dGridEval_BSplineCurve.cxx + Geom2dGridEval_BSplineCurve.hxx + Geom2dGridEval_Circle.cxx + Geom2dGridEval_Circle.hxx + Geom2dGridEval_Curve.cxx + Geom2dGridEval_Curve.hxx + Geom2dGridEval_Ellipse.cxx + Geom2dGridEval_Ellipse.hxx + Geom2dGridEval_Hyperbola.cxx + Geom2dGridEval_Hyperbola.hxx + Geom2dGridEval_Line.hxx + Geom2dGridEval_OffsetCurve.cxx + Geom2dGridEval_OffsetCurve.hxx + Geom2dGridEval_OtherCurve.cxx + Geom2dGridEval_OtherCurve.hxx + Geom2dGridEval_Parabola.cxx + Geom2dGridEval_Parabola.hxx +) diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval.hxx new file mode 100644 index 0000000000..d2095edfa1 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval.hxx @@ -0,0 +1,54 @@ +// 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. + +#ifndef _Geom2dGridEval_HeaderFile +#define _Geom2dGridEval_HeaderFile + +#include +#include +#include + +//! @brief Namespace containing result structures for 2D curve grid evaluators. +//! +//! Provides lightweight POD structures for returning evaluation results +//! with derivatives from 2D curve grid evaluators. +namespace Geom2dGridEval +{ + +//! Result structure for curve D1 evaluation (point and first derivative). +struct CurveD1 +{ + gp_Pnt2d Point; + gp_Vec2d D1; +}; + +//! Result structure for curve D2 evaluation (point and first two derivatives). +struct CurveD2 +{ + gp_Pnt2d Point; + gp_Vec2d D1; + gp_Vec2d D2; +}; + +//! Result structure for curve D3 evaluation (point and first three derivatives). +struct CurveD3 +{ + gp_Pnt2d Point; + gp_Vec2d D1; + gp_Vec2d D2; + gp_Vec2d D3; +}; + +} // namespace Geom2dGridEval + +#endif // _Geom2dGridEval_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BSplineCurve.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BSplineCurve.cxx new file mode 100644 index 0000000000..122a7bef4e --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BSplineCurve.cxx @@ -0,0 +1,375 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include + +namespace +{ + +//! Threshold for using cache vs direct evaluation. +constexpr int THE_CACHE_THRESHOLD = 4; + +//! Helper structure holding extracted 2D B-spline curve data. +struct CurveData +{ + const NCollection_Array1* FlatKnots; + const NCollection_Array1* Poles; + const NCollection_Array1* Weights; + const NCollection_Array1* Knots; + const NCollection_Array1* Mults; + int Degree; + bool IsPeriodic; +}; + +//! Extract curve data from geometry. Returns false if data is invalid. +bool extractCurveData(const occ::handle& theGeom, CurveData& theData) +{ + if (theGeom.IsNull()) + return false; + + theData.FlatKnots = &theGeom->KnotSequence(); + theData.Poles = &theGeom->Poles(); + theData.Weights = theGeom->Weights(); + theData.Knots = &theGeom->Knots(); + theData.Mults = &theGeom->Multiplicities(); + theData.Degree = theGeom->Degree(); + theData.IsPeriodic = theGeom->IsPeriodic(); + return true; +} + +//! Locate parameter and compute span index. +inline void locateSpan(double theParam, + const NCollection_Array1& theFlatKnots, + int theDegree, + bool theIsPeriodic, + double& theAdjustedParam, + int& theSpanIdx) +{ + theAdjustedParam = theParam; + BSplCLib::LocateParameter(theDegree, + theFlatKnots, + BSplCLib::NoMults(), + theParam, + theIsPeriodic, + theSpanIdx, + theAdjustedParam); +} + +//! Count consecutive parameters in the same span (for cache threshold decision). +inline int countSpanSize(const NCollection_Array1& theParams, + const NCollection_Array1& theFlatKnots, + int theDegree, + bool theIsPeriodic, + int theStartIdx, + int theTargetSpan) +{ + const int aLower = theParams.Lower(); + const int aNb = theParams.Size(); + int aCount = 1; + + for (int i = theStartIdx + 1; i < aNb; ++i) + { + double aParam = theParams.Value(aLower + i); + double aAdjusted = aParam; + int aSpan = 0; + BSplCLib::LocateParameter(theDegree, + theFlatKnots, + BSplCLib::NoMults(), + aParam, + theIsPeriodic, + aSpan, + aAdjusted); + if (aSpan == theTargetSpan) + ++aCount; + else + break; + } + return aCount; +} + +//! Compute span local parameter coefficients (start and length). +//! Note: Curve cache uses [0,1] local parameter range (unlike surface which uses [-1,1]). +inline void computeSpanCoeffs(const NCollection_Array1& theFlatKnots, + int theSpan, + double& theStart, + double& theLen) +{ + theStart = theFlatKnots.Value(theSpan); + theLen = theFlatKnots.Value(theSpan + 1) - theStart; +} + +//! Template helper for cached grid evaluation. +//! @tparam ResultT result type (gp_Pnt2d, CurveD1, CurveD2, CurveD3) +//! @tparam CacheEvalF functor: (cache, localParam, result) -> void +//! @tparam DirectEvalF functor: (data, param, spanIdx, result) -> void +template +NCollection_Array1 evaluateGridCached(const CurveData& theData, + const NCollection_Array1& theParams, + CacheEvalF theCacheEval, + DirectEvalF theDirectEval) +{ + const int aNbParams = theParams.Size(); + const int aLow = theParams.Lower(); + + NCollection_Array1 aResults(1, aNbParams); + occ::handle aCache = new BSplCLib_Cache(theData.Degree, + theData.IsPeriodic, + *theData.FlatKnots, + *theData.Poles, + theData.Weights); + + int aPrevSpan = -1; + int aSpanSize = 0; + double aSpanStart = 0.0; + double aSpanLen = 1.0; + bool aUseCache = false; + + for (int i = 0; i < aNbParams; ++i) + { + const double aParam = theParams.Value(aLow + i); + double aAdjParam = aParam; + int aSpan = 0; + locateSpan(aParam, *theData.FlatKnots, theData.Degree, theData.IsPeriodic, aAdjParam, aSpan); + + // Update span info when span changes + if (aSpan != aPrevSpan) + { + aPrevSpan = aSpan; + aSpanSize = + countSpanSize(theParams, *theData.FlatKnots, theData.Degree, theData.IsPeriodic, i, aSpan); + computeSpanCoeffs(*theData.FlatKnots, aSpan, aSpanStart, aSpanLen); + + aUseCache = (aSpanSize >= THE_CACHE_THRESHOLD); + if (aUseCache) + { + aCache->BuildCache(aAdjParam, *theData.FlatKnots, *theData.Poles, theData.Weights); + } + } + + ResultT& aResult = aResults.ChangeValue(i + 1); + if (aUseCache) + { + // Curve cache uses [0,1] local parameter range + const double aLocalParam = (aSpanLen > 0.0) ? (aAdjParam - aSpanStart) / aSpanLen : 0.0; + theCacheEval(aCache, aLocalParam, aResult); + } + else + { + theDirectEval(theData, aAdjParam, aSpan, aResult); + } + } + + return aResults; +} + +} // namespace + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BSplineCurve::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + CurveData aData; + if (theParams.IsEmpty() || !extractCurveData(myGeom, aData)) + { + return NCollection_Array1(); + } + + return evaluateGridCached( + aData, + theParams, + [](const occ::handle& theCache, double theLocalParam, gp_Pnt2d& theResult) { + theCache->D0Local(theLocalParam, theResult); + }, + [](const CurveData& theData, double theParam, int theSpanIdx, gp_Pnt2d& theResult) { + BSplCLib::D0(theParam, + theSpanIdx, + theData.Degree, + theData.IsPeriodic, + *theData.Poles, + theData.Weights, + *theData.FlatKnots, + nullptr, + theResult); + }); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BSplineCurve::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + CurveData aData; + if (theParams.IsEmpty() || !extractCurveData(myGeom, aData)) + { + return NCollection_Array1(); + } + + return evaluateGridCached( + aData, + theParams, + [](const occ::handle& theCache, + double theLocalParam, + Geom2dGridEval::CurveD1& theResult) { + theCache->D1Local(theLocalParam, theResult.Point, theResult.D1); + }, + [](const CurveData& theData, + double theParam, + int theSpanIdx, + Geom2dGridEval::CurveD1& theResult) { + BSplCLib::D1(theParam, + theSpanIdx, + theData.Degree, + theData.IsPeriodic, + *theData.Poles, + theData.Weights, + *theData.FlatKnots, + nullptr, + theResult.Point, + theResult.D1); + }); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BSplineCurve::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + CurveData aData; + if (theParams.IsEmpty() || !extractCurveData(myGeom, aData)) + { + return NCollection_Array1(); + } + + return evaluateGridCached( + aData, + theParams, + [](const occ::handle& theCache, + double theLocalParam, + Geom2dGridEval::CurveD2& theResult) { + theCache->D2Local(theLocalParam, theResult.Point, theResult.D1, theResult.D2); + }, + [](const CurveData& theData, + double theParam, + int theSpanIdx, + Geom2dGridEval::CurveD2& theResult) { + BSplCLib::D2(theParam, + theSpanIdx, + theData.Degree, + theData.IsPeriodic, + *theData.Poles, + theData.Weights, + *theData.FlatKnots, + nullptr, + theResult.Point, + theResult.D1, + theResult.D2); + }); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BSplineCurve::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + CurveData aData; + if (theParams.IsEmpty() || !extractCurveData(myGeom, aData)) + { + return NCollection_Array1(); + } + + return evaluateGridCached( + aData, + theParams, + [](const occ::handle& theCache, + double theLocalParam, + Geom2dGridEval::CurveD3& theResult) { + theCache->D3Local(theLocalParam, theResult.Point, theResult.D1, theResult.D2, theResult.D3); + }, + [](const CurveData& theData, + double theParam, + int theSpanIdx, + Geom2dGridEval::CurveD3& theResult) { + BSplCLib::D3(theParam, + theSpanIdx, + theData.Degree, + theData.IsPeriodic, + *theData.Poles, + theData.Weights, + *theData.FlatKnots, + nullptr, + theResult.Point, + theResult.D1, + theResult.D2, + theResult.D3); + }); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BSplineCurve::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + CurveData aData; + if (theParams.IsEmpty() || theN < 1 || !extractCurveData(myGeom, aData)) + { + return NCollection_Array1(); + } + + const int aNbParams = theParams.Size(); + const int aLow = theParams.Lower(); + + // Derivatives beyond degree are zero + if (theN > aData.Degree) + { + NCollection_Array1 aResult(1, aNbParams); + const gp_Vec2d aZero(0.0, 0.0); + for (int i = 1; i <= aNbParams; ++i) + { + aResult.SetValue(i, aZero); + } + return aResult; + } + + NCollection_Array1 aResult(1, aNbParams); + + // DN uses direct evaluation (no cache available) + for (int i = 0; i < aNbParams; ++i) + { + const double aParam = theParams.Value(aLow + i); + double aAdjParam = aParam; + int aSpan = 0; + locateSpan(aParam, *aData.FlatKnots, aData.Degree, aData.IsPeriodic, aAdjParam, aSpan); + + gp_Vec2d aDN; + BSplCLib::DN(aAdjParam, + theN, + aSpan, + aData.Degree, + aData.IsPeriodic, + *aData.Poles, + aData.Weights, + *aData.Knots, + aData.Mults, + aDN); + aResult.SetValue(i + 1, aDN); + } + + return aResult; +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BSplineCurve.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BSplineCurve.hxx new file mode 100644 index 0000000000..6d9f2434b8 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BSplineCurve.hxx @@ -0,0 +1,101 @@ +// 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. + +#ifndef _Geom2dGridEval_BSplineCurve_HeaderFile +#define _Geom2dGridEval_BSplineCurve_HeaderFile + +#include +#include +#include +#include +#include +#include + +//! @brief Efficient batch evaluator for 2D B-spline curve grid points. +//! +//! Optimizes evaluation of multiple points on a 2D B-spline curve by: +//! - Pre-computing span indices for input parameters (no runtime binary search) +//! - Pre-grouping parameters by span for cache-optimal iteration +//! - Rebuilding cache only once per span block (not per point) +//! - Using KnotSequence() from Geom2d_BSplineCurve for direct flat knot access +//! +//! Usage: +//! @code +//! Geom2dGridEval_BSplineCurve anEvaluator(myBSplineCurve2d); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_BSplineCurve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with geometry. + //! @param theCurve the 2D B-spline curve to evaluate + Geom2dGridEval_BSplineCurve(const occ::handle& theCurve) + : myGeom(theCurve) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_BSplineCurve(const Geom2dGridEval_BSplineCurve&) = delete; + Geom2dGridEval_BSplineCurve& operator=(const Geom2dGridEval_BSplineCurve&) = delete; + Geom2dGridEval_BSplineCurve(Geom2dGridEval_BSplineCurve&&) = delete; + Geom2dGridEval_BSplineCurve& operator=(Geom2dGridEval_BSplineCurve&&) = delete; + + //! Returns the geometry handle. + const occ::handle& Geometry() const { return myGeom; } + + //! Evaluate all grid points. + //! Points are evaluated in span-grouped order to minimize cache rebuilds. + //! @param theParams array of parameter values + //! @return array of evaluated points (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first derivative. + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first and second derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first, second, and third derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! For orders 1-3, reuses EvaluateGridD1/D2/D3. + //! For orders > 3, uses BSplCLib::DN. + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + +private: + occ::handle myGeom; +}; + +#endif // _Geom2dGridEval_BSplineCurve_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BezierCurve.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BezierCurve.cxx new file mode 100644 index 0000000000..7938a977ad --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BezierCurve.cxx @@ -0,0 +1,185 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include + +namespace +{ +//! Creates a BSplCLib_Cache for a 2D Bezier curve. +//! @param theCurve the 2D Bezier curve geometry +//! @return initialized cache ready for evaluation +occ::handle CreateBezierCache2d(const occ::handle& theCurve) +{ + const NCollection_Array1& aKnotSequence = theCurve->KnotSequence(); + const NCollection_Array1& aPoles = theCurve->Poles(); + const NCollection_Array1* aWeights = theCurve->Weights(); + + occ::handle aCache = + new BSplCLib_Cache(theCurve->Degree(), false, aKnotSequence, aPoles, aWeights); + aCache->BuildCache(0.5, aKnotSequence, aPoles, aWeights); + return aCache; +} +} // namespace + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BezierCurve::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + occ::handle aCache = CreateBezierCache2d(myGeom); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + gp_Pnt2d aP; + aCache->D0(theParams.Value(i), aP); + aResult.SetValue(i - theParams.Lower() + 1, aP); + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BezierCurve::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + occ::handle aCache = CreateBezierCache2d(myGeom); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + gp_Pnt2d aP; + gp_Vec2d aD1; + aCache->D1(theParams.Value(i), aP, aD1); + aResult.ChangeValue(i - theParams.Lower() + 1) = {aP, aD1}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BezierCurve::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + occ::handle aCache = CreateBezierCache2d(myGeom); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + gp_Pnt2d aP; + gp_Vec2d aD1, aD2; + aCache->D2(theParams.Value(i), aP, aD1, aD2); + aResult.ChangeValue(i - theParams.Lower() + 1) = {aP, aD1, aD2}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BezierCurve::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + occ::handle aCache = CreateBezierCache2d(myGeom); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + gp_Pnt2d aP; + gp_Vec2d aD1, aD2, aD3; + aCache->D3(theParams.Value(i), aP, aD1, aD2, aD3); + aResult.ChangeValue(i - theParams.Lower() + 1) = {aP, aD1, aD2, aD3}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_BezierCurve::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + if (myGeom.IsNull() || theParams.IsEmpty() || theN < 1) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + // For Bezier curves, derivatives become zero when order exceeds degree + const int aDegree = myGeom->Degree(); + if (theN > aDegree) + { + const gp_Vec2d aZeroVec(0.0, 0.0); + for (int i = 1; i <= aNb; ++i) + { + aResult.SetValue(i, aZeroVec); + } + return aResult; + } + + // Get poles, weights, and flat knots from geometry + const NCollection_Array1& aPoles = myGeom->Poles(); + const NCollection_Array1* aWeights = myGeom->Weights(); + const NCollection_Array1& aKnotSequence = myGeom->KnotSequence(); + + // Bezier has a single span (index 0 with flat knots), non-periodic + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + gp_Vec2d aDN; + BSplCLib::DN(theParams.Value(i), + theN, + 0, // span index (single span for Bezier with flat knots) + aDegree, + false, // not periodic + aPoles, + aWeights, + aKnotSequence, + nullptr, // no multiplicities with flat knots + aDN); + aResult.SetValue(i - theParams.Lower() + 1, aDN); + } + return aResult; +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BezierCurve.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BezierCurve.hxx new file mode 100644 index 0000000000..cd05187117 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_BezierCurve.hxx @@ -0,0 +1,93 @@ +// 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. + +#ifndef _Geom2dGridEval_BezierCurve_HeaderFile +#define _Geom2dGridEval_BezierCurve_HeaderFile + +#include +#include +#include +#include +#include +#include + +//! @brief Efficient batch evaluator for 2D Bezier curve grid points. +//! +//! Uses BSplCLib to evaluate Bezier curves (treated as B-Splines). +//! +//! Usage: +//! @code +//! Geom2dGridEval_BezierCurve anEvaluator(myGeom2dBezier); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_BezierCurve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with geometry. + //! @param theBezier the 2D bezier curve geometry to evaluate + Geom2dGridEval_BezierCurve(const occ::handle& theBezier) + : myGeom(theBezier) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_BezierCurve(const Geom2dGridEval_BezierCurve&) = delete; + Geom2dGridEval_BezierCurve& operator=(const Geom2dGridEval_BezierCurve&) = delete; + Geom2dGridEval_BezierCurve(Geom2dGridEval_BezierCurve&&) = delete; + Geom2dGridEval_BezierCurve& operator=(Geom2dGridEval_BezierCurve&&) = delete; + + //! Returns the geometry handle. + const occ::handle& Geometry() const { return myGeom; } + + //! Evaluate all grid points. + //! @param theParams array of parameter values + //! @return array of evaluated points (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first derivative. + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first and second derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first, second, and third derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! For orders 1-3, reuses EvaluateGridD1/D2/D3. + //! For orders > 3, uses BSplCLib::DN. + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + +private: + occ::handle myGeom; +}; + +#endif // _Geom2dGridEval_BezierCurve_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Circle.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Circle.cxx new file mode 100644 index 0000000000..d45ca63ca3 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Circle.cxx @@ -0,0 +1,256 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include + +#include + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Circle::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + // Extract circle data from geometry + const gp_Circ2d& aCirc = myGeom->Circ2d(); + const gp_Pnt2d& aCenter = aCirc.Location(); + const gp_Dir2d& aXDir = aCirc.Position().XDirection(); + const gp_Dir2d& aYDir = aCirc.Position().YDirection(); + const double aRadius = aCirc.Radius(); + + // Pre-extract coordinates for performance + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + aResult.SetValue(i - theParams.Lower() + 1, + gp_Pnt2d(aCX + aRadius * (cosU * aXX + sinU * aYX), + aCY + aRadius * (cosU * aXY + sinU * aYY))); + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Circle::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Circ2d& aCirc = myGeom->Circ2d(); + const gp_Pnt2d& aCenter = aCirc.Location(); + const gp_Dir2d& aXDir = aCirc.Position().XDirection(); + const gp_Dir2d& aYDir = aCirc.Position().YDirection(); + const double aRadius = aCirc.Radius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + // P = C + R * (cos(u) * X + sin(u) * Y) + // D1 = R * (-sin(u) * X + cos(u) * Y) + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + aRadius * (cosU * aXX + sinU * aYX), + aCY + aRadius * (cosU * aXY + sinU * aYY)), + gp_Vec2d(aRadius * (-sinU * aXX + cosU * aYX), aRadius * (-sinU * aXY + cosU * aYY))}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Circle::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Circ2d& aCirc = myGeom->Circ2d(); + const gp_Pnt2d& aCenter = aCirc.Location(); + const gp_Dir2d& aXDir = aCirc.Position().XDirection(); + const gp_Dir2d& aYDir = aCirc.Position().YDirection(); + const double aRadius = aCirc.Radius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + // P = C + R * (cos(u) * X + sin(u) * Y) + // D1 = R * (-sin(u) * X + cos(u) * Y) + // D2 = R * (-cos(u) * X - sin(u) * Y) + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + aRadius * (cosU * aXX + sinU * aYX), + aCY + aRadius * (cosU * aXY + sinU * aYY)), + gp_Vec2d(aRadius * (-sinU * aXX + cosU * aYX), aRadius * (-sinU * aXY + cosU * aYY)), + gp_Vec2d(aRadius * (-cosU * aXX - sinU * aYX), aRadius * (-cosU * aXY - sinU * aYY))}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Circle::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Circ2d& aCirc = myGeom->Circ2d(); + const gp_Pnt2d& aCenter = aCirc.Location(); + const gp_Dir2d& aXDir = aCirc.Position().XDirection(); + const gp_Dir2d& aYDir = aCirc.Position().YDirection(); + const double aRadius = aCirc.Radius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + // P = C + R * (cos(u) * X + sin(u) * Y) + // D1 = R * (-sin(u) * X + cos(u) * Y) + // D2 = R * (-cos(u) * X - sin(u) * Y) + // D3 = R * (sin(u) * X - cos(u) * Y) + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + aRadius * (cosU * aXX + sinU * aYX), + aCY + aRadius * (cosU * aXY + sinU * aYY)), + gp_Vec2d(aRadius * (-sinU * aXX + cosU * aYX), aRadius * (-sinU * aXY + cosU * aYY)), + gp_Vec2d(aRadius * (-cosU * aXX - sinU * aYX), aRadius * (-cosU * aXY - sinU * aYY)), + gp_Vec2d(aRadius * (sinU * aXX - cosU * aYX), aRadius * (sinU * aXY - cosU * aYY))}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Circle::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + if (myGeom.IsNull() || theParams.IsEmpty() || theN < 1) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Circ2d& aCirc = myGeom->Circ2d(); + const gp_Dir2d& aXDir = aCirc.Position().XDirection(); + const gp_Dir2d& aYDir = aCirc.Position().YDirection(); + const double aRadius = aCirc.Radius(); + + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + // Circle derivatives are cyclic with period 4: + // D1 = R * (-sin(u) * X + cos(u) * Y) -> coefficients: (-sin, cos) + // D2 = R * (-cos(u) * X - sin(u) * Y) -> coefficients: (-cos, -sin) + // D3 = R * (sin(u) * X - cos(u) * Y) -> coefficients: (sin, -cos) + // D4 = R * (cos(u) * X + sin(u) * Y) -> coefficients: (cos, sin) = D0 + const int aPhase = (theN - 1) % 4; // 0->D1, 1->D2, 2->D3, 3->D4 + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + double aCoeffX, aCoeffY; + switch (aPhase) + { + case 0: // D1, D5, D9, ... + aCoeffX = -sinU; + aCoeffY = cosU; + break; + case 1: // D2, D6, D10, ... + aCoeffX = -cosU; + aCoeffY = -sinU; + break; + case 2: // D3, D7, D11, ... + aCoeffX = sinU; + aCoeffY = -cosU; + break; + default: // D4, D8, D12, ... (case 3) + aCoeffX = cosU; + aCoeffY = sinU; + break; + } + + aResult.SetValue(i - theParams.Lower() + 1, + gp_Vec2d(aRadius * (aCoeffX * aXX + aCoeffY * aYX), + aRadius * (aCoeffX * aXY + aCoeffY * aYY))); + } + return aResult; +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Circle.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Circle.hxx new file mode 100644 index 0000000000..af4d242423 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Circle.hxx @@ -0,0 +1,102 @@ +// 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. + +#ifndef _Geom2dGridEval_Circle_HeaderFile +#define _Geom2dGridEval_Circle_HeaderFile + +#include +#include +#include +#include +#include + +//! @brief Efficient batch evaluator for 2D circle grid points. +//! +//! Uses analytical formula: P(u) = Center + R * (cos(u) * XDir + sin(u) * YDir) +//! +//! Usage: +//! @code +//! Geom2dGridEval_Circle anEvaluator(myGeom2dCircle); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_Circle +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with geometry. + //! @param theCircle the 2D circle geometry to evaluate + Geom2dGridEval_Circle(const occ::handle& theCircle) + : myGeom(theCircle) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_Circle(const Geom2dGridEval_Circle&) = delete; + Geom2dGridEval_Circle& operator=(const Geom2dGridEval_Circle&) = delete; + Geom2dGridEval_Circle(Geom2dGridEval_Circle&&) = delete; + Geom2dGridEval_Circle& operator=(Geom2dGridEval_Circle&&) = delete; + + //! Returns the geometry handle. + const occ::handle& Geometry() const { return myGeom; } + + //! Evaluate all grid points. + //! @param theParams array of parameter values (angles in radians) + //! @return array of evaluated points (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first derivative. + //! D1 = R * (-sin(u) * XDir + cos(u) * YDir) + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first and second derivatives. + //! D2 = R * (-cos(u) * XDir - sin(u) * YDir) = -P (relative to center) + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first, second, and third derivatives. + //! D3 = R * (sin(u) * XDir - cos(u) * YDir) = -D1 + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! Circle has cyclic derivatives with period 4: + //! D1 = R * (-sin(u) * X + cos(u) * Y) + //! D2 = R * (-cos(u) * X - sin(u) * Y) + //! D3 = R * (sin(u) * X - cos(u) * Y) + //! D4 = R * (cos(u) * X + sin(u) * Y) = D0, then repeats + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + +private: + occ::handle myGeom; +}; + +#endif // _Geom2dGridEval_Circle_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Curve.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Curve.cxx new file mode 100644 index 0000000000..94abca89bd --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Curve.cxx @@ -0,0 +1,229 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +//! Extracts basis curve from potentially nested TrimmedCurve wrappers. +//! @param theCurve input curve (may be TrimmedCurve or any other) +//! @return the underlying basis curve, or theCurve if not a TrimmedCurve +occ::handle ExtractBasisCurve(const occ::handle& theCurve) +{ + occ::handle aResult = theCurve; + while (auto aTrimmed = occ::down_cast(aResult)) + { + aResult = aTrimmed->BasisCurve(); + } + return aResult; +} + +} // namespace + +//================================================================================================== + +void Geom2dGridEval_Curve::Initialize(const Adaptor2d_Curve2d& theCurve) +{ + if (theCurve.IsKind(STANDARD_TYPE(Geom2dAdaptor_Curve))) + { + Initialize(static_cast(theCurve).Curve()); + return; + } + + // For non-Geom2dAdaptor or when Geom2d_Curve is not available, + // use reference for the evaluator + myCurveType = theCurve.GetType(); + myEvaluator.emplace(theCurve); +} + +//================================================================================================== + +void Geom2dGridEval_Curve::Initialize(const occ::handle& theCurve) +{ + if (theCurve.IsNull()) + { + myEvaluator.emplace(); + myCurveType = GeomAbs_OtherCurve; + return; + } + + // Extract basis curve from potentially nested TrimmedCurve wrappers + occ::handle aBasisCurve = ExtractBasisCurve(theCurve); + + if (auto aLine = occ::down_cast(aBasisCurve)) + { + myCurveType = GeomAbs_Line; + myEvaluator.emplace(aLine); + } + else if (auto aCircle = occ::down_cast(aBasisCurve)) + { + myCurveType = GeomAbs_Circle; + myEvaluator.emplace(aCircle); + } + else if (auto anEllipse = occ::down_cast(aBasisCurve)) + { + myCurveType = GeomAbs_Ellipse; + myEvaluator.emplace(anEllipse); + } + else if (auto aHyperbola = occ::down_cast(aBasisCurve)) + { + myCurveType = GeomAbs_Hyperbola; + myEvaluator.emplace(aHyperbola); + } + else if (auto aParabola = occ::down_cast(aBasisCurve)) + { + myCurveType = GeomAbs_Parabola; + myEvaluator.emplace(aParabola); + } + else if (auto aBezier = occ::down_cast(aBasisCurve)) + { + myCurveType = GeomAbs_BezierCurve; + myEvaluator.emplace(aBezier); + } + else if (auto aBSpline = occ::down_cast(aBasisCurve)) + { + myCurveType = GeomAbs_BSplineCurve; + myEvaluator.emplace(aBSpline); + } + else if (auto anOffset = occ::down_cast(aBasisCurve)) + { + myCurveType = GeomAbs_OffsetCurve; + myEvaluator.emplace(anOffset); + } + else + { + // Unknown curve type - set uninitialized + myEvaluator.emplace(); + myCurveType = GeomAbs_OtherCurve; + } +} + +//================================================================================================== + +bool Geom2dGridEval_Curve::IsInitialized() const +{ + return !std::holds_alternative(myEvaluator); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Curve::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + return std::visit( + [&theParams](const auto& theEval) -> NCollection_Array1 { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return NCollection_Array1(); + } + else + { + return theEval.EvaluateGrid(theParams); + } + }, + myEvaluator); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Curve::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + return std::visit( + [&theParams](const auto& theEval) -> NCollection_Array1 { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return NCollection_Array1(); + } + else + { + return theEval.EvaluateGridD1(theParams); + } + }, + myEvaluator); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Curve::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + return std::visit( + [&theParams](const auto& theEval) -> NCollection_Array1 { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return NCollection_Array1(); + } + else + { + return theEval.EvaluateGridD2(theParams); + } + }, + myEvaluator); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Curve::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + return std::visit( + [&theParams](const auto& theEval) -> NCollection_Array1 { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return NCollection_Array1(); + } + else + { + return theEval.EvaluateGridD3(theParams); + } + }, + myEvaluator); +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Curve::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + return std::visit( + [&theParams, theN](const auto& theEval) -> NCollection_Array1 { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return NCollection_Array1(); + } + else + { + return theEval.EvaluateGridDN(theParams, theN); + } + }, + myEvaluator); +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Curve.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Curve.hxx new file mode 100644 index 0000000000..691fce4b09 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Curve.hxx @@ -0,0 +1,146 @@ +// 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. + +#ifndef _Geom2dGridEval_Curve_HeaderFile +#define _Geom2dGridEval_Curve_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//! @brief Unified grid evaluator for any 2D curve. +//! +//! Uses std::variant for compile-time type safety and zero heap allocation +//! for the evaluator itself. Automatically detects curve type from +//! Adaptor2d_Curve2d and dispatches to the appropriate specialized evaluator. +//! +//! Supported curve types with optimized evaluation: +//! - Line: Direct analytical formula +//! - Circle: Trigonometric formula +//! - Ellipse: Analytical formula +//! - Hyperbola: Analytical formula +//! - Parabola: Analytical formula +//! - BezierCurve: Optimized batch evaluation via BSplCLib +//! - BSplineCurve: Optimized batch evaluation via BSplCLib with span caching +//! - OffsetCurve: Composite evaluation using basis curve batch evaluator +//! - Other: Fallback using Adaptor2d_Curve2d::D0 +//! +//! Usage: +//! @code +//! Geom2dGridEval_Curve anEval; +//! anEval.Initialize(myAdaptorCurve2d); +//! // OR +//! anEval.Initialize(myGeom2dCurve); +//! NCollection_Array1 aGrid = anEval.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_Curve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Variant type holding all possible 2D curve evaluators. + using EvaluatorVariant = std::variant; // Fallback for other types + + //! Default constructor - uninitialized state. + Geom2dGridEval_Curve() + : myEvaluator(std::monostate{}), + myCurveType(GeomAbs_OtherCurve) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_Curve(const Geom2dGridEval_Curve&) = delete; + Geom2dGridEval_Curve& operator=(const Geom2dGridEval_Curve&) = delete; + Geom2dGridEval_Curve(Geom2dGridEval_Curve&&) = delete; + Geom2dGridEval_Curve& operator=(Geom2dGridEval_Curve&&) = delete; + + //! Initialize from 2D adaptor reference (auto-detects curve type). + //! For Geom2dAdaptor_Curve, extracts underlying Geom2d_Curve for optimized evaluation. + //! For other adaptors, stores reference for fallback evaluation. + //! @note The curve adaptor reference must remain valid during the lifetime + //! of this evaluator when using fallback evaluation. + //! @param theCurve 2D curve adaptor reference to evaluate + Standard_EXPORT void Initialize(const Adaptor2d_Curve2d& theCurve); + + //! Initialize from geometry handle (auto-detects curve type). + //! @param theCurve 2D geometry to evaluate + Standard_EXPORT void Initialize(const occ::handle& theCurve); + + //! Returns true if properly initialized. + Standard_EXPORT bool IsInitialized() const; + + //! Evaluate grid points at all parameters. + //! @param theParams array of parameter values + //! @return array of 2D points (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate grid points with first derivative. + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate grid points with first and second derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate grid points with first, second, and third derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + + //! Returns the detected curve type. + GeomAbs_CurveType GetType() const { return myCurveType; } + +private: + EvaluatorVariant myEvaluator; + GeomAbs_CurveType myCurveType; +}; + +#endif // _Geom2dGridEval_Curve_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Ellipse.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Ellipse.cxx new file mode 100644 index 0000000000..acfa490a69 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Ellipse.cxx @@ -0,0 +1,263 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include + +#include + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Ellipse::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Elips2d& anElips = myGeom->Elips2d(); + const gp_Pnt2d& aCenter = anElips.Location(); + const gp_Dir2d aXDir = anElips.XAxis().Direction(); + const gp_Dir2d aYDir = anElips.YAxis().Direction(); + const double aMajR = anElips.MajorRadius(); + const double aMinR = anElips.MinorRadius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + // P = Center + MajorR * cos(u) * XDir + MinorR * sin(u) * YDir + aResult.SetValue(i - theParams.Lower() + 1, + gp_Pnt2d(aCX + aMajR * cosU * aXX + aMinR * sinU * aYX, + aCY + aMajR * cosU * aXY + aMinR * sinU * aYY)); + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Ellipse::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Elips2d& anElips = myGeom->Elips2d(); + const gp_Pnt2d& aCenter = anElips.Location(); + const gp_Dir2d aXDir = anElips.XAxis().Direction(); + const gp_Dir2d aYDir = anElips.YAxis().Direction(); + const double aMajR = anElips.MajorRadius(); + const double aMinR = anElips.MinorRadius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + // P = Center + MajorR * cos(u) * XDir + MinorR * sin(u) * YDir + // D1 = -MajorR * sin(u) * XDir + MinorR * cos(u) * YDir + + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + aMajR * cosU * aXX + aMinR * sinU * aYX, + aCY + aMajR * cosU * aXY + aMinR * sinU * aYY), + gp_Vec2d(-aMajR * sinU * aXX + aMinR * cosU * aYX, -aMajR * sinU * aXY + aMinR * cosU * aYY)}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Ellipse::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Elips2d& anElips = myGeom->Elips2d(); + const gp_Pnt2d& aCenter = anElips.Location(); + const gp_Dir2d aXDir = anElips.XAxis().Direction(); + const gp_Dir2d aYDir = anElips.YAxis().Direction(); + const double aMajR = anElips.MajorRadius(); + const double aMinR = anElips.MinorRadius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + // P = Center + MajorR * cos(u) * XDir + MinorR * sin(u) * YDir + // D1 = -MajorR * sin(u) * XDir + MinorR * cos(u) * YDir + // D2 = -MajorR * cos(u) * XDir - MinorR * sin(u) * YDir = -(P - Center) + + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + aMajR * cosU * aXX + aMinR * sinU * aYX, + aCY + aMajR * cosU * aXY + aMinR * sinU * aYY), + gp_Vec2d(-aMajR * sinU * aXX + aMinR * cosU * aYX, -aMajR * sinU * aXY + aMinR * cosU * aYY), + gp_Vec2d(-aMajR * cosU * aXX - aMinR * sinU * aYX, -aMajR * cosU * aXY - aMinR * sinU * aYY)}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Ellipse::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Elips2d& anElips = myGeom->Elips2d(); + const gp_Pnt2d& aCenter = anElips.Location(); + const gp_Dir2d aXDir = anElips.XAxis().Direction(); + const gp_Dir2d aYDir = anElips.YAxis().Direction(); + const double aMajR = anElips.MajorRadius(); + const double aMinR = anElips.MinorRadius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + // P = Center + MajorR * cos(u) * XDir + MinorR * sin(u) * YDir + // D1 = -MajorR * sin(u) * XDir + MinorR * cos(u) * YDir + // D2 = -MajorR * cos(u) * XDir - MinorR * sin(u) * YDir + // D3 = MajorR * sin(u) * XDir - MinorR * cos(u) * YDir = -D1 + + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + aMajR * cosU * aXX + aMinR * sinU * aYX, + aCY + aMajR * cosU * aXY + aMinR * sinU * aYY), + gp_Vec2d(-aMajR * sinU * aXX + aMinR * cosU * aYX, -aMajR * sinU * aXY + aMinR * cosU * aYY), + gp_Vec2d(-aMajR * cosU * aXX - aMinR * sinU * aYX, -aMajR * cosU * aXY - aMinR * sinU * aYY), + gp_Vec2d(aMajR * sinU * aXX - aMinR * cosU * aYX, aMajR * sinU * aXY - aMinR * cosU * aYY)}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Ellipse::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + if (myGeom.IsNull() || theParams.IsEmpty() || theN < 1) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Elips2d& anElips = myGeom->Elips2d(); + const gp_Dir2d aXDir = anElips.XAxis().Direction(); + const gp_Dir2d aYDir = anElips.YAxis().Direction(); + const double aMajR = anElips.MajorRadius(); + const double aMinR = anElips.MinorRadius(); + + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + // Ellipse derivatives are cyclic with period 4: + // D1 = -MajR * sin(u) * X + MinR * cos(u) * Y -> coefficients: (-sin, cos) + // D2 = -MajR * cos(u) * X - MinR * sin(u) * Y -> coefficients: (-cos, -sin) + // D3 = MajR * sin(u) * X - MinR * cos(u) * Y -> coefficients: (sin, -cos) + // D4 = MajR * cos(u) * X + MinR * sin(u) * Y -> coefficients: (cos, sin) = D0 + const int aPhase = (theN - 1) % 4; + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double cosU = std::cos(u); + const double sinU = std::sin(u); + + double aCoeffMajR, aCoeffMinR; + switch (aPhase) + { + case 0: // D1, D5, D9, ... + aCoeffMajR = -sinU; + aCoeffMinR = cosU; + break; + case 1: // D2, D6, D10, ... + aCoeffMajR = -cosU; + aCoeffMinR = -sinU; + break; + case 2: // D3, D7, D11, ... + aCoeffMajR = sinU; + aCoeffMinR = -cosU; + break; + default: // D4, D8, D12, ... (case 3) + aCoeffMajR = cosU; + aCoeffMinR = sinU; + break; + } + + aResult.SetValue(i - theParams.Lower() + 1, + gp_Vec2d(aMajR * aCoeffMajR * aXX + aMinR * aCoeffMinR * aYX, + aMajR * aCoeffMajR * aXY + aMinR * aCoeffMinR * aYY)); + } + return aResult; +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Ellipse.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Ellipse.hxx new file mode 100644 index 0000000000..1d0561ca78 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Ellipse.hxx @@ -0,0 +1,100 @@ +// 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. + +#ifndef _Geom2dGridEval_Ellipse_HeaderFile +#define _Geom2dGridEval_Ellipse_HeaderFile + +#include +#include +#include +#include +#include + +//! @brief Efficient batch evaluator for 2D ellipse grid points. +//! +//! Uses analytical formula: +//! P(u) = Center + MajorRadius * cos(u) * XDir + MinorRadius * sin(u) * YDir +//! +//! Usage: +//! @code +//! Geom2dGridEval_Ellipse anEvaluator(myGeom2dEllipse); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_Ellipse +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with geometry. + //! @param theEllipse the 2D ellipse geometry to evaluate + Geom2dGridEval_Ellipse(const occ::handle& theEllipse) + : myGeom(theEllipse) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_Ellipse(const Geom2dGridEval_Ellipse&) = delete; + Geom2dGridEval_Ellipse& operator=(const Geom2dGridEval_Ellipse&) = delete; + Geom2dGridEval_Ellipse(Geom2dGridEval_Ellipse&&) = delete; + Geom2dGridEval_Ellipse& operator=(Geom2dGridEval_Ellipse&&) = delete; + + //! Returns the geometry handle. + const occ::handle& Geometry() const { return myGeom; } + + //! Evaluate all grid points. + //! @param theParams array of parameter values + //! @return array of evaluated points (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first derivative. + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first and second derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first, second, and third derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! Ellipse has cyclic derivatives with period 4: + //! D1 = -MajR * sin(u) * X + MinR * cos(u) * Y + //! D2 = -MajR * cos(u) * X - MinR * sin(u) * Y + //! D3 = MajR * sin(u) * X - MinR * cos(u) * Y + //! D4 = MajR * cos(u) * X + MinR * sin(u) * Y = D0, then repeats + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + +private: + occ::handle myGeom; +}; + +#endif // _Geom2dGridEval_Ellipse_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Hyperbola.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Hyperbola.cxx new file mode 100644 index 0000000000..d57ee0bd89 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Hyperbola.cxx @@ -0,0 +1,250 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include + +#include + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Hyperbola::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Hypr2d& aHypr = myGeom->Hypr2d(); + const gp_Pnt2d& aCenter = aHypr.Location(); + const gp_Dir2d aXDir = aHypr.XAxis().Direction(); + const gp_Dir2d aYDir = aHypr.YAxis().Direction(); + const double aMajR = aHypr.MajorRadius(); + const double aMinR = aHypr.MinorRadius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double coshU = std::cosh(u); + const double sinhU = std::sinh(u); + + aResult.SetValue(i - theParams.Lower() + 1, + gp_Pnt2d(aCX + aMajR * coshU * aXX + aMinR * sinhU * aYX, + aCY + aMajR * coshU * aXY + aMinR * sinhU * aYY)); + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Hyperbola::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Hypr2d& aHypr = myGeom->Hypr2d(); + const gp_Pnt2d& aCenter = aHypr.Location(); + const gp_Dir2d aXDir = aHypr.XAxis().Direction(); + const gp_Dir2d aYDir = aHypr.YAxis().Direction(); + const double aMajR = aHypr.MajorRadius(); + const double aMinR = aHypr.MinorRadius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double coshU = std::cosh(u); + const double sinhU = std::sinh(u); + + // P = Center + MajorR * cosh(u) * XDir + MinorR * sinh(u) * YDir + // D1 = MajorR * sinh(u) * XDir + MinorR * cosh(u) * YDir + + aResult.ChangeValue(i - theParams.Lower() + + 1) = {gp_Pnt2d(aCX + aMajR * coshU * aXX + aMinR * sinhU * aYX, + aCY + aMajR * coshU * aXY + aMinR * sinhU * aYY), + gp_Vec2d(aMajR * sinhU * aXX + aMinR * coshU * aYX, + aMajR * sinhU * aXY + aMinR * coshU * aYY)}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Hyperbola::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Hypr2d& aHypr = myGeom->Hypr2d(); + const gp_Pnt2d& aCenter = aHypr.Location(); + const gp_Dir2d aXDir = aHypr.XAxis().Direction(); + const gp_Dir2d aYDir = aHypr.YAxis().Direction(); + const double aMajR = aHypr.MajorRadius(); + const double aMinR = aHypr.MinorRadius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double coshU = std::cosh(u); + const double sinhU = std::sinh(u); + + // D2 = MajorR * cosh(u) * XDir + MinorR * sinh(u) * YDir = (P - Center) + + aResult.ChangeValue(i - theParams.Lower() + + 1) = {gp_Pnt2d(aCX + aMajR * coshU * aXX + aMinR * sinhU * aYX, + aCY + aMajR * coshU * aXY + aMinR * sinhU * aYY), + gp_Vec2d(aMajR * sinhU * aXX + aMinR * coshU * aYX, + aMajR * sinhU * aXY + aMinR * coshU * aYY), + gp_Vec2d(aMajR * coshU * aXX + aMinR * sinhU * aYX, + aMajR * coshU * aXY + aMinR * sinhU * aYY)}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Hyperbola::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Hypr2d& aHypr = myGeom->Hypr2d(); + const gp_Pnt2d& aCenter = aHypr.Location(); + const gp_Dir2d aXDir = aHypr.XAxis().Direction(); + const gp_Dir2d aYDir = aHypr.YAxis().Direction(); + const double aMajR = aHypr.MajorRadius(); + const double aMinR = aHypr.MinorRadius(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double coshU = std::cosh(u); + const double sinhU = std::sinh(u); + + // D3 = MajorR * sinh(u) * XDir + MinorR * cosh(u) * YDir = D1 + + aResult.ChangeValue(i - theParams.Lower() + + 1) = {gp_Pnt2d(aCX + aMajR * coshU * aXX + aMinR * sinhU * aYX, + aCY + aMajR * coshU * aXY + aMinR * sinhU * aYY), + gp_Vec2d(aMajR * sinhU * aXX + aMinR * coshU * aYX, + aMajR * sinhU * aXY + aMinR * coshU * aYY), + gp_Vec2d(aMajR * coshU * aXX + aMinR * sinhU * aYX, + aMajR * coshU * aXY + aMinR * sinhU * aYY), + gp_Vec2d(aMajR * sinhU * aXX + aMinR * coshU * aYX, + aMajR * sinhU * aXY + aMinR * coshU * aYY)}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Hyperbola::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + if (myGeom.IsNull() || theParams.IsEmpty() || theN < 1) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Hypr2d& aHypr = myGeom->Hypr2d(); + const gp_Dir2d aXDir = aHypr.XAxis().Direction(); + const gp_Dir2d aYDir = aHypr.YAxis().Direction(); + const double aMajR = aHypr.MajorRadius(); + const double aMinR = aHypr.MinorRadius(); + + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + // Hyperbola derivatives are cyclic with period 2: + // D0 = MajR * cosh(u) * X + MinR * sinh(u) * Y -> coefficients: (cosh, sinh) + // D1 = MajR * sinh(u) * X + MinR * cosh(u) * Y -> coefficients: (sinh, cosh) + // D2 = D0, D3 = D1, etc. + const bool isOdd = (theN % 2) == 1; + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double coshU = std::cosh(u); + const double sinhU = std::sinh(u); + + if (isOdd) + { + aResult.SetValue(i - theParams.Lower() + 1, + gp_Vec2d(aMajR * sinhU * aXX + aMinR * coshU * aYX, + aMajR * sinhU * aXY + aMinR * coshU * aYY)); + } + else + { + aResult.SetValue(i - theParams.Lower() + 1, + gp_Vec2d(aMajR * coshU * aXX + aMinR * sinhU * aYX, + aMajR * coshU * aXY + aMinR * sinhU * aYY)); + } + } + return aResult; +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Hyperbola.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Hyperbola.hxx new file mode 100644 index 0000000000..1a879e0a37 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Hyperbola.hxx @@ -0,0 +1,95 @@ +// 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. + +#ifndef _Geom2dGridEval_Hyperbola_HeaderFile +#define _Geom2dGridEval_Hyperbola_HeaderFile + +#include +#include +#include +#include +#include + +//! @brief Efficient batch evaluator for 2D hyperbola grid points. +//! +//! Uses analytical formula: +//! P(u) = Center + MajorRadius * cosh(u) * XDir + MinorRadius * sinh(u) * YDir +//! +//! Usage: +//! @code +//! Geom2dGridEval_Hyperbola anEvaluator(myGeom2dHyperbola); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_Hyperbola +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with geometry. + //! @param theHyperbola the 2D hyperbola geometry to evaluate + Geom2dGridEval_Hyperbola(const occ::handle& theHyperbola) + : myGeom(theHyperbola) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_Hyperbola(const Geom2dGridEval_Hyperbola&) = delete; + Geom2dGridEval_Hyperbola& operator=(const Geom2dGridEval_Hyperbola&) = delete; + Geom2dGridEval_Hyperbola(Geom2dGridEval_Hyperbola&&) = delete; + Geom2dGridEval_Hyperbola& operator=(Geom2dGridEval_Hyperbola&&) = delete; + + //! Returns the geometry handle. + const occ::handle& Geometry() const { return myGeom; } + + //! Evaluate all grid points. + //! @param theParams array of parameter values + //! @return array of evaluated points (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first derivative. + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first and second derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first, second, and third derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! Hyperbola has cyclic derivatives with period 2: + //! D1 = MajR * sinh(u) * X + MinR * cosh(u) * Y + //! D2 = MajR * cosh(u) * X + MinR * sinh(u) * Y = D0 + //! D3 = D1, D4 = D0, etc. + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + +private: + occ::handle myGeom; +}; + +#endif // _Geom2dGridEval_Hyperbola_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Line.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Line.hxx new file mode 100644 index 0000000000..67bcb46133 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Line.hxx @@ -0,0 +1,230 @@ +// 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. + +#ifndef _Geom2dGridEval_Line_HeaderFile +#define _Geom2dGridEval_Line_HeaderFile + +#include +#include +#include +#include + +//! @brief Efficient batch evaluator for 2D line grid points. +//! +//! Uses direct analytical formula: P(t) = Location + t * Direction +//! +//! Usage: +//! @code +//! Geom2dGridEval_Line anEvaluator(myGeom2dLine); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_Line +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with geometry. + //! @param theLine the 2D line geometry to evaluate + Geom2dGridEval_Line(const occ::handle& theLine) + : myGeom(theLine) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_Line(const Geom2dGridEval_Line&) = delete; + Geom2dGridEval_Line& operator=(const Geom2dGridEval_Line&) = delete; + Geom2dGridEval_Line(Geom2dGridEval_Line&&) = delete; + Geom2dGridEval_Line& operator=(Geom2dGridEval_Line&&) = delete; + + //! Returns the geometry handle. + const occ::handle& Geometry() const { return myGeom; } + + //! Evaluate all grid points. + //! @param theParams array of parameter values + //! @return array of evaluated points (1-based indexing), + //! or empty array if geometry is null or no parameters + NCollection_Array1 EvaluateGrid(const NCollection_Array1& theParams) const + { + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + NCollection_Array1 aResult(1, theParams.Size()); + + const gp_Lin2d& aLin = myGeom->Lin2d(); + const gp_Pnt2d& aLoc = aLin.Location(); + const gp_Dir2d& aDir = aLin.Direction(); + + const double aLocX = aLoc.X(); + const double aLocY = aLoc.Y(); + const double aDirX = aDir.X(); + const double aDirY = aDir.Y(); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double t = theParams.Value(i); + aResult.SetValue(i - theParams.Lower() + 1, gp_Pnt2d(aLocX + t * aDirX, aLocY + t * aDirY)); + } + return aResult; + } + + //! Evaluate all grid points with first derivative. + //! For a line, D1 is constant (the direction vector). + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing), + //! or empty array if geometry is null or no parameters + NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const + { + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + NCollection_Array1 aResult(1, theParams.Size()); + + const gp_Lin2d& aLin = myGeom->Lin2d(); + const gp_Pnt2d& aLoc = aLin.Location(); + const gp_Dir2d& aDir = aLin.Direction(); + + const double aLocX = aLoc.X(); + const double aLocY = aLoc.Y(); + const double aDirX = aDir.X(); + const double aDirY = aDir.Y(); + + const gp_Vec2d aD1(aDirX, aDirY); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double t = theParams.Value(i); + aResult.ChangeValue(i - theParams.Lower() + + 1) = {gp_Pnt2d(aLocX + t * aDirX, aLocY + t * aDirY), aD1}; + } + return aResult; + } + + //! Evaluate all grid points with first and second derivatives. + //! For a line, D1 is constant and D2 is zero. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing), + //! or empty array if geometry is null or no parameters + NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const + { + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + NCollection_Array1 aResult(1, theParams.Size()); + + const gp_Lin2d& aLin = myGeom->Lin2d(); + const gp_Pnt2d& aLoc = aLin.Location(); + const gp_Dir2d& aDir = aLin.Direction(); + + const double aLocX = aLoc.X(); + const double aLocY = aLoc.Y(); + const double aDirX = aDir.X(); + const double aDirY = aDir.Y(); + + const gp_Vec2d aD1(aDirX, aDirY); + const gp_Vec2d aD2(0, 0); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double t = theParams.Value(i); + aResult.ChangeValue(i - theParams.Lower() + + 1) = {gp_Pnt2d(aLocX + t * aDirX, aLocY + t * aDirY), aD1, aD2}; + } + return aResult; + } + + //! Evaluate all grid points with first, second, and third derivatives. + //! For a line, D1 is constant, D2 and D3 are zero. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing), + //! or empty array if geometry is null or no parameters + NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const + { + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + NCollection_Array1 aResult(1, theParams.Size()); + + const gp_Lin2d& aLin = myGeom->Lin2d(); + const gp_Pnt2d& aLoc = aLin.Location(); + const gp_Dir2d& aDir = aLin.Direction(); + + const double aLocX = aLoc.X(); + const double aLocY = aLoc.Y(); + const double aDirX = aDir.X(); + const double aDirY = aDir.Y(); + + const gp_Vec2d aD1(aDirX, aDirY); + const gp_Vec2d aZero(0, 0); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double t = theParams.Value(i); + aResult.ChangeValue( + i - theParams.Lower() + + 1) = {gp_Pnt2d(aLocX + t * aDirX, aLocY + t * aDirY), aD1, aZero, aZero}; + } + return aResult; + } + + //! Evaluate Nth derivative at all grid points. + //! For a line: D1 = Direction, DN = 0 for N > 1. + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing), + //! or empty array if geometry is null or no parameters + NCollection_Array1 EvaluateGridDN(const NCollection_Array1& theParams, + int theN) const + { + if (myGeom.IsNull() || theParams.IsEmpty() || theN < 1) + { + return NCollection_Array1(); + } + + NCollection_Array1 aResult(1, theParams.Size()); + + if (theN == 1) + { + const gp_Dir2d aDir = myGeom->Lin2d().Direction(); + const gp_Vec2d aD1(aDir.X(), aDir.Y()); + for (int i = 1; i <= theParams.Size(); ++i) + { + aResult.SetValue(i, aD1); + } + } + else + { + const gp_Vec2d aZero(0, 0); + for (int i = 1; i <= theParams.Size(); ++i) + { + aResult.SetValue(i, aZero); + } + } + return aResult; + } + +private: + occ::handle myGeom; +}; + +#endif // _Geom2dGridEval_Line_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OffsetCurve.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OffsetCurve.cxx new file mode 100644 index 0000000000..ec145b4506 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OffsetCurve.cxx @@ -0,0 +1,282 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OffsetCurve::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + if (myBasis.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + // Offset D0 requires basis D1 to compute offset normal direction + Geom2dGridEval_Curve aBasisEval; + aBasisEval.Initialize(myBasis); + + NCollection_Array1 aBasisD1 = aBasisEval.EvaluateGridD1(theParams); + if (aBasisD1.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNbParams = theParams.Size(); + NCollection_Array1 aResult(1, aNbParams); + + for (int i = 1; i <= aNbParams; ++i) + { + const Geom2dGridEval::CurveD1& aBasis = aBasisD1.Value(i); + gp_Pnt2d aP = aBasis.Point; + if (!Geom2d_OffsetCurveUtils::CalculateD0(aP, aBasis.D1, myOffset)) + { + return NCollection_Array1(); + } + aResult.SetValue(i, aP); + } + + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OffsetCurve::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + if (myBasis.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + // Offset D1 requires basis D2 + Geom2dGridEval_Curve aBasisEval; + aBasisEval.Initialize(myBasis); + + NCollection_Array1 aBasisD2 = aBasisEval.EvaluateGridD2(theParams); + if (aBasisD2.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNbParams = theParams.Size(); + NCollection_Array1 aResult(1, aNbParams); + + for (int i = 1; i <= aNbParams; ++i) + { + const Geom2dGridEval::CurveD2& aBasis = aBasisD2.Value(i); + gp_Pnt2d aP = aBasis.Point; + gp_Vec2d aD1 = aBasis.D1; + if (!Geom2d_OffsetCurveUtils::CalculateD1(aP, aD1, aBasis.D2, myOffset)) + { + return NCollection_Array1(); + } + aResult.ChangeValue(i) = {aP, aD1}; + } + + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OffsetCurve::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + if (myBasis.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + // Offset D2 requires basis D3 + Geom2dGridEval_Curve aBasisEval; + aBasisEval.Initialize(myBasis); + + NCollection_Array1 aBasisD3 = aBasisEval.EvaluateGridD3(theParams); + if (aBasisD3.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNbParams = theParams.Size(); + NCollection_Array1 aResult(1, aNbParams); + + for (int i = 1; i <= aNbParams; ++i) + { + const Geom2dGridEval::CurveD3& aBasis = aBasisD3.Value(i); + gp_Pnt2d aP = aBasis.Point; + gp_Vec2d aD1 = aBasis.D1; + gp_Vec2d aD2 = aBasis.D2; + gp_Vec2d aD3 = aBasis.D3; + + // Check for direction change at singular points + bool isDirectionChange = false; + if (aD1.SquareMagnitude() <= gp::Resolution()) + { + gp_Vec2d aDummyD4; + if (!Geom2d_OffsetCurveUtils::AdjustDerivative(*myBasis, + 3, + theParams.Value(theParams.Lower() + i - 1), + aD1, + aD2, + aD3, + aDummyD4, + isDirectionChange)) + { + return NCollection_Array1(); + } + } + + if (!Geom2d_OffsetCurveUtils::CalculateD2(aP, aD1, aD2, aD3, isDirectionChange, myOffset)) + { + return NCollection_Array1(); + } + aResult.ChangeValue(i) = {aP, aD1, aD2}; + } + + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OffsetCurve::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + if (myBasis.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + // Offset D3 requires basis D3 + D4 + Geom2dGridEval_Curve aBasisEval; + aBasisEval.Initialize(myBasis); + + NCollection_Array1 aBasisD3 = aBasisEval.EvaluateGridD3(theParams); + if (aBasisD3.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNbParams = theParams.Size(); + NCollection_Array1 aResult(1, aNbParams); + + for (int i = 1; i <= aNbParams; ++i) + { + const double aParam = theParams.Value(theParams.Lower() + i - 1); + + const Geom2dGridEval::CurveD3& aBasis = aBasisD3.Value(i); + gp_Pnt2d aP = aBasis.Point; + gp_Vec2d aD1 = aBasis.D1; + gp_Vec2d aD2 = aBasis.D2; + gp_Vec2d aD3 = aBasis.D3; + std::optional aD4Opt = myBasis->EvalDN(aParam, 4); + if (!aD4Opt) + { + return NCollection_Array1(); + } + gp_Vec2d aD4 = *aD4Opt; + + // Check for direction change at singular points + bool isDirectionChange = false; + if (aD1.SquareMagnitude() <= gp::Resolution()) + { + if (!Geom2d_OffsetCurveUtils::AdjustDerivative(*myBasis, + 4, + aParam, + aD1, + aD2, + aD3, + aD4, + isDirectionChange)) + { + return NCollection_Array1(); + } + } + + if (!Geom2d_OffsetCurveUtils::CalculateD3(aP, aD1, aD2, aD3, aD4, isDirectionChange, myOffset)) + { + return NCollection_Array1(); + } + aResult.ChangeValue(i) = {aP, aD1, aD2, aD3}; + } + + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OffsetCurve::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + if (myBasis.IsNull() || theParams.IsEmpty() || theN < 1) + { + return NCollection_Array1(); + } + + const int aNbParams = theParams.Size(); + + // Reuse optimized grid evaluators for orders 1-3 + if (theN == 1) + { + NCollection_Array1 aD1Grid = EvaluateGridD1(theParams); + if (aD1Grid.IsEmpty()) + { + return NCollection_Array1(); + } + NCollection_Array1 aResult(1, aNbParams); + for (int i = 1; i <= aNbParams; ++i) + { + aResult.SetValue(i, aD1Grid.Value(i).D1); + } + return aResult; + } + else if (theN == 2) + { + NCollection_Array1 aD2Grid = EvaluateGridD2(theParams); + if (aD2Grid.IsEmpty()) + { + return NCollection_Array1(); + } + NCollection_Array1 aResult(1, aNbParams); + for (int i = 1; i <= aNbParams; ++i) + { + aResult.SetValue(i, aD2Grid.Value(i).D2); + } + return aResult; + } + else if (theN == 3) + { + NCollection_Array1 aD3Grid = EvaluateGridD3(theParams); + if (aD3Grid.IsEmpty()) + { + return NCollection_Array1(); + } + NCollection_Array1 aResult(1, aNbParams); + for (int i = 1; i <= aNbParams; ++i) + { + aResult.SetValue(i, aD3Grid.Value(i).D3); + } + return aResult; + } + else + { + // For orders > 3, batch evaluate basis curve DN + Geom2dGridEval_Curve aBasisEval; + aBasisEval.Initialize(myBasis); + return aBasisEval.EvaluateGridDN(theParams, theN); + } +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OffsetCurve.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OffsetCurve.hxx new file mode 100644 index 0000000000..0bf5a9be3e --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OffsetCurve.hxx @@ -0,0 +1,109 @@ +// 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. + +#ifndef _Geom2dGridEval_OffsetCurve_HeaderFile +#define _Geom2dGridEval_OffsetCurve_HeaderFile + +#include +#include +#include +#include +#include +#include + +//! @brief Batch evaluator for 2D offset curve grid points. +//! +//! Evaluates the 2D offset curve formula: +//! P(u) = C(u) + Offset * N / ||N|| +//! where N = (D1.Y, -D1.X) is the normal (tangent rotated 90 degrees). +//! +//! Uses Geom2dGridEval_Curve for batch evaluation of the basis curve, +//! then applies offset transformation. +//! +//! Usage: +//! @code +//! Geom2dGridEval_OffsetCurve anEvaluator(myGeom2dOffset); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_OffsetCurve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with geometry. + //! @param theOffset the 2D offset curve geometry to evaluate + Geom2dGridEval_OffsetCurve(const occ::handle& theOffset) + : myGeom(theOffset), + myOffset(0.0) + { + if (!myGeom.IsNull()) + { + myOffset = myGeom->Offset(); + myBasis = myGeom->BasisCurve(); + } + } + + //! Non-copyable and non-movable. + Geom2dGridEval_OffsetCurve(const Geom2dGridEval_OffsetCurve&) = delete; + Geom2dGridEval_OffsetCurve& operator=(const Geom2dGridEval_OffsetCurve&) = delete; + Geom2dGridEval_OffsetCurve(Geom2dGridEval_OffsetCurve&&) = delete; + Geom2dGridEval_OffsetCurve& operator=(Geom2dGridEval_OffsetCurve&&) = delete; + + //! Returns the geometry handle. + const occ::handle& Geometry() const { return myGeom; } + + //! Evaluate all grid points. + //! @param theParams array of parameter values + //! @return array of evaluated points (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first derivative. + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first and second derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with derivatives up to third order. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! For orders 1-3, reuses EvaluateGridD1/D2/D3. + //! For orders > 3, uses basis curve DN method. + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + +private: + occ::handle myGeom; + occ::handle myBasis; + double myOffset; +}; + +#endif // _Geom2dGridEval_OffsetCurve_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OtherCurve.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OtherCurve.cxx new file mode 100644 index 0000000000..779692c0c9 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OtherCurve.cxx @@ -0,0 +1,185 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OtherCurve::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + if (theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + std::optional aP = myCurve.get().EvalD0(theParams.Value(i)); + if (!aP) + { + return NCollection_Array1(); + } + aResult.SetValue(i - theParams.Lower() + 1, *aP); + } + + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OtherCurve::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + if (theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + std::optional aD1 = myCurve.get().EvalD1(theParams.Value(i)); + if (!aD1) + { + return NCollection_Array1(); + } + aResult.ChangeValue(i - theParams.Lower() + 1) = {aD1->Point, aD1->D1}; + } + + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OtherCurve::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + if (theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + std::optional aD2 = myCurve.get().EvalD2(theParams.Value(i)); + if (!aD2) + { + return NCollection_Array1(); + } + aResult.ChangeValue(i - theParams.Lower() + 1) = {aD2->Point, aD2->D1, aD2->D2}; + } + + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OtherCurve::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + if (theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + std::optional aD3 = myCurve.get().EvalD3(theParams.Value(i)); + if (!aD3) + { + return NCollection_Array1(); + } + aResult.ChangeValue(i - theParams.Lower() + 1) = {aD3->Point, aD3->D1, aD3->D2, aD3->D3}; + } + + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_OtherCurve::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + if (theParams.IsEmpty() || theN < 1) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + // Reuse existing grid evaluators for orders 1-3 + if (theN == 1) + { + NCollection_Array1 aD1Grid = EvaluateGridD1(theParams); + if (aD1Grid.IsEmpty()) + { + return NCollection_Array1(); + } + for (int i = 1; i <= aNb; ++i) + { + aResult.SetValue(i, aD1Grid.Value(i).D1); + } + } + else if (theN == 2) + { + NCollection_Array1 aD2Grid = EvaluateGridD2(theParams); + if (aD2Grid.IsEmpty()) + { + return NCollection_Array1(); + } + for (int i = 1; i <= aNb; ++i) + { + aResult.SetValue(i, aD2Grid.Value(i).D2); + } + } + else if (theN == 3) + { + NCollection_Array1 aD3Grid = EvaluateGridD3(theParams); + if (aD3Grid.IsEmpty()) + { + return NCollection_Array1(); + } + for (int i = 1; i <= aNb; ++i) + { + aResult.SetValue(i, aD3Grid.Value(i).D3); + } + } + else + { + // For orders > 3, use adaptor EvalDN method + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + std::optional aDN = myCurve.get().EvalDN(theParams.Value(i), theN); + if (!aDN) + { + return NCollection_Array1(); + } + aResult.SetValue(i - theParams.Lower() + 1, *aDN); + } + } + return aResult; +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OtherCurve.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OtherCurve.hxx new file mode 100644 index 0000000000..4ff0aad800 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_OtherCurve.hxx @@ -0,0 +1,101 @@ +// 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. + +#ifndef _Geom2dGridEval_OtherCurve_HeaderFile +#define _Geom2dGridEval_OtherCurve_HeaderFile + +#include +#include +#include +#include +#include + +#include + +//! @brief Fallback evaluator for unknown 2D curve types. +//! +//! Uses Adaptor2d_Curve2d::D0 for point-by-point evaluation. +//! This is the slowest evaluator but handles any 2D curve type. +//! +//! @note The curve adaptor reference must remain valid during the lifetime +//! of this evaluator. The evaluator does not take ownership. +//! +//! Usage: +//! @code +//! Geom2dGridEval_OtherCurve anEvaluator(myCurveAdaptor2d); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_OtherCurve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with curve adaptor reference. + //! @param theCurve reference to 2D curve adaptor (must remain valid) + Geom2dGridEval_OtherCurve(const Adaptor2d_Curve2d& theCurve) + : myCurve(theCurve) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_OtherCurve(const Geom2dGridEval_OtherCurve&) = delete; + Geom2dGridEval_OtherCurve& operator=(const Geom2dGridEval_OtherCurve&) = delete; + Geom2dGridEval_OtherCurve(Geom2dGridEval_OtherCurve&&) = delete; + Geom2dGridEval_OtherCurve& operator=(Geom2dGridEval_OtherCurve&&) = delete; + + //! Returns the curve adaptor reference. + const Adaptor2d_Curve2d& Curve() const { return myCurve.get(); } + + //! Evaluate all grid points. + //! @param theParams array of parameter values + //! @return array of evaluated points (1-based indexing), + //! or empty array if no parameters + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first derivative. + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing), + //! or empty array if no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first and second derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing), + //! or empty array if no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first, second, and third derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing), + //! or empty array if no parameters + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! For orders 1-3, reuses EvaluateGridD1/D2/D3. + //! For orders > 3, uses adaptor DN method. + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + +private: + std::reference_wrapper myCurve; +}; + +#endif // _Geom2dGridEval_OtherCurve_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Parabola.cxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Parabola.cxx new file mode 100644 index 0000000000..1509722853 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Parabola.cxx @@ -0,0 +1,255 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Parabola::EvaluateGrid( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Parab2d& aParab = myGeom->Parab2d(); + const gp_Pnt2d& aCenter = aParab.Location(); + const gp_Dir2d aXDir = aParab.MirrorAxis().Direction(); + const gp_Dir2d aYDir(-aXDir.Y(), aXDir.X()); + const double aFocal = aParab.Focal(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + const double aCoeff = 1.0 / (4.0 * aFocal); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + + // P = Center + (u^2 / 4F) * XDir + u * YDir + const double u2Term = u * u * aCoeff; + + aResult.SetValue(i - theParams.Lower() + 1, + gp_Pnt2d(aCX + u2Term * aXX + u * aYX, aCY + u2Term * aXY + u * aYY)); + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Parabola::EvaluateGridD1( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Parab2d& aParab = myGeom->Parab2d(); + const gp_Pnt2d& aCenter = aParab.Location(); + const gp_Dir2d aXDir = aParab.MirrorAxis().Direction(); + const gp_Dir2d aYDir(-aXDir.Y(), aXDir.X()); + const double aFocal = aParab.Focal(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + const double aCoeff = 1.0 / (4.0 * aFocal); + const double aCoeff2 = 1.0 / (2.0 * aFocal); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + + // P = Center + (u^2 / 4F) * XDir + u * YDir + // D1 = (u / 2F) * XDir + YDir + + const double u2Term = u * u * aCoeff; + const double d1Term = u * aCoeff2; + + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + u2Term * aXX + u * aYX, aCY + u2Term * aXY + u * aYY), + gp_Vec2d(d1Term * aXX + aYX, d1Term * aXY + aYY)}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Parabola::EvaluateGridD2( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Parab2d& aParab = myGeom->Parab2d(); + const gp_Pnt2d& aCenter = aParab.Location(); + const gp_Dir2d aXDir = aParab.MirrorAxis().Direction(); + const gp_Dir2d aYDir(-aXDir.Y(), aXDir.X()); + const double aFocal = aParab.Focal(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + const double aCoeff = 1.0 / (4.0 * aFocal); + const double aCoeff2 = 1.0 / (2.0 * aFocal); + + // D2 is constant for parabola: (1/2F) * XDir + const gp_Vec2d aD2(aCoeff2 * aXX, aCoeff2 * aXY); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + + const double u2Term = u * u * aCoeff; + const double d1Term = u * aCoeff2; + + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + u2Term * aXX + u * aYX, aCY + u2Term * aXY + u * aYY), + gp_Vec2d(d1Term * aXX + aYX, d1Term * aXY + aYY), + aD2}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Parabola::EvaluateGridD3( + const NCollection_Array1& theParams) const +{ + if (myGeom.IsNull() || theParams.IsEmpty()) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Parab2d& aParab = myGeom->Parab2d(); + const gp_Pnt2d& aCenter = aParab.Location(); + const gp_Dir2d aXDir = aParab.MirrorAxis().Direction(); + const gp_Dir2d aYDir(-aXDir.Y(), aXDir.X()); + const double aFocal = aParab.Focal(); + + const double aCX = aCenter.X(); + const double aCY = aCenter.Y(); + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + const double aCoeff = 1.0 / (4.0 * aFocal); + const double aCoeff2 = 1.0 / (2.0 * aFocal); + + const gp_Vec2d aD2(aCoeff2 * aXX, aCoeff2 * aXY); + const gp_Vec2d aD3(0.0, 0.0); + + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + + const double u2Term = u * u * aCoeff; + const double d1Term = u * aCoeff2; + + aResult.ChangeValue(i - theParams.Lower() + 1) = { + gp_Pnt2d(aCX + u2Term * aXX + u * aYX, aCY + u2Term * aXY + u * aYY), + gp_Vec2d(d1Term * aXX + aYX, d1Term * aXY + aYY), + aD2, + aD3}; + } + return aResult; +} + +//================================================================================================== + +NCollection_Array1 Geom2dGridEval_Parabola::EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const +{ + if (myGeom.IsNull() || theParams.IsEmpty() || theN < 1) + { + return NCollection_Array1(); + } + + const int aNb = theParams.Size(); + NCollection_Array1 aResult(1, aNb); + + const gp_Parab2d& aParab = myGeom->Parab2d(); + const gp_Dir2d aXDir = aParab.MirrorAxis().Direction(); + const gp_Dir2d aYDir(-aXDir.Y(), aXDir.X()); + const double aFocal = aParab.Focal(); + + const double aXX = aXDir.X(); + const double aXY = aXDir.Y(); + const double aYX = aYDir.X(); + const double aYY = aYDir.Y(); + + const double aCoeff2 = 1.0 / (2.0 * aFocal); + + if (theN == 1) + { + // D1 = (u/2F) * X + Y (depends on u) + for (int i = theParams.Lower(); i <= theParams.Upper(); ++i) + { + const double u = theParams.Value(i); + const double d1Term = u * aCoeff2; + aResult.SetValue(i - theParams.Lower() + 1, gp_Vec2d(d1Term * aXX + aYX, d1Term * aXY + aYY)); + } + } + else if (theN == 2) + { + // D2 = (1/2F) * X (constant) + const gp_Vec2d aD2(aCoeff2 * aXX, aCoeff2 * aXY); + for (int i = 1; i <= aNb; ++i) + { + aResult.SetValue(i, aD2); + } + } + else + { + // DN = 0 for N >= 3 + const gp_Vec2d aZero(0.0, 0.0); + for (int i = 1; i <= aNb; ++i) + { + aResult.SetValue(i, aZero); + } + } + return aResult; +} diff --git a/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Parabola.hxx b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Parabola.hxx new file mode 100644 index 0000000000..d021368a84 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2dGridEval/Geom2dGridEval_Parabola.hxx @@ -0,0 +1,95 @@ +// 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. + +#ifndef _Geom2dGridEval_Parabola_HeaderFile +#define _Geom2dGridEval_Parabola_HeaderFile + +#include +#include +#include +#include +#include + +//! @brief Efficient batch evaluator for 2D parabola grid points. +//! +//! Uses analytical formula: +//! P(u) = Center + (u^2 / (4*Focal)) * XDir + u * YDir +//! +//! Usage: +//! @code +//! Geom2dGridEval_Parabola anEvaluator(myGeom2dParabola); +//! NCollection_Array1 aGrid = anEvaluator.EvaluateGrid(myParams); +//! @endcode +class Geom2dGridEval_Parabola +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with geometry. + //! @param theParabola the 2D parabola geometry to evaluate + Geom2dGridEval_Parabola(const occ::handle& theParabola) + : myGeom(theParabola) + { + } + + //! Non-copyable and non-movable. + Geom2dGridEval_Parabola(const Geom2dGridEval_Parabola&) = delete; + Geom2dGridEval_Parabola& operator=(const Geom2dGridEval_Parabola&) = delete; + Geom2dGridEval_Parabola(Geom2dGridEval_Parabola&&) = delete; + Geom2dGridEval_Parabola& operator=(Geom2dGridEval_Parabola&&) = delete; + + //! Returns the geometry handle. + const occ::handle& Geometry() const { return myGeom; } + + //! Evaluate all grid points. + //! @param theParams array of parameter values + //! @return array of evaluated points (1-based indexing), + //! or empty array if geometry is null or no parameters + Standard_EXPORT NCollection_Array1 EvaluateGrid( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first derivative. + //! @param theParams array of parameter values + //! @return array of CurveD1 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD1( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first and second derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD2 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD2( + const NCollection_Array1& theParams) const; + + //! Evaluate all grid points with first, second, and third derivatives. + //! @param theParams array of parameter values + //! @return array of CurveD3 (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridD3( + const NCollection_Array1& theParams) const; + + //! Evaluate Nth derivative at all grid points. + //! Parabola: P = Center + (u^2/4F) * X + u * Y + //! D1 = (u/2F) * X + Y (depends on u) + //! D2 = (1/2F) * X (constant) + //! DN = 0 for N >= 3 + //! @param theParams array of parameter values + //! @param theN derivative order (N >= 1) + //! @return array of derivative vectors (1-based indexing) + Standard_EXPORT NCollection_Array1 EvaluateGridDN( + const NCollection_Array1& theParams, + int theN) const; + +private: + occ::handle myGeom; +}; + +#endif // _Geom2dGridEval_Parabola_HeaderFile diff --git a/src/ModelingData/TKG2d/PACKAGES.cmake b/src/ModelingData/TKG2d/PACKAGES.cmake index feea4dbfd0..f6f24a3528 100644 --- a/src/ModelingData/TKG2d/PACKAGES.cmake +++ b/src/ModelingData/TKG2d/PACKAGES.cmake @@ -7,4 +7,5 @@ set(OCCT_TKG2d_LIST_OF_PACKAGES Geom2dLProp Geom2dAdaptor Geom2dHash + Geom2dGridEval )