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 )