diff --git a/src/ModelingData/TKG3d/GTests/FILES.cmake b/src/ModelingData/TKG3d/GTests/FILES.cmake index f5b23d3567..4ecb9ed1ac 100644 --- a/src/ModelingData/TKG3d/GTests/FILES.cmake +++ b/src/ModelingData/TKG3d/GTests/FILES.cmake @@ -44,4 +44,7 @@ set(OCCT_TKG3d_GTests_FILES GeomGridEval_Torus_Test.cxx GeomHash_CurveHasher_Test.cxx GeomHash_SurfaceHasher_Test.cxx + GeomProp_Test.cxx + GeomProp_VsCLProps_Test.cxx + GeomProp_VsSLProps_Test.cxx ) diff --git a/src/ModelingData/TKG3d/GTests/GeomProp_Test.cxx b/src/ModelingData/TKG3d/GTests/GeomProp_Test.cxx new file mode 100644 index 0000000000..8e5268b633 --- /dev/null +++ b/src/ModelingData/TKG3d/GTests/GeomProp_Test.cxx @@ -0,0 +1,523 @@ +// 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. + +// Unit tests for GeomProp free functions and result structures. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +// ============================================================================ +// Free function tests - ComputeTangent +// ============================================================================ + +TEST(GeomPropTest, ComputeTangent_D1NonZero) +{ + const gp_Vec aD1(1.0, 0.0, 0.0); + const gp_Vec aD2(0.0, 1.0, 0.0); + const gp_Vec aD3(0.0, 0.0, 1.0); + const GeomProp::TangentResult aRes = + GeomProp::ComputeTangent(aD1, aD2, aD3, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_NEAR(aRes.Direction.X(), 1.0, Precision::Confusion()); + EXPECT_NEAR(aRes.Direction.Y(), 0.0, Precision::Confusion()); + EXPECT_NEAR(aRes.Direction.Z(), 0.0, Precision::Confusion()); +} + +TEST(GeomPropTest, ComputeTangent_D1Zero_D2NonZero) +{ + const gp_Vec aD1(0.0, 0.0, 0.0); + const gp_Vec aD2(0.0, 1.0, 0.0); + const gp_Vec aD3(0.0, 0.0, 1.0); + const GeomProp::TangentResult aRes = + GeomProp::ComputeTangent(aD1, aD2, aD3, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_NEAR(aRes.Direction.Y(), 1.0, Precision::Confusion()); +} + +TEST(GeomPropTest, ComputeTangent_AllZero) +{ + const gp_Vec aD1(0.0, 0.0, 0.0); + const gp_Vec aD2(0.0, 0.0, 0.0); + const gp_Vec aD3(0.0, 0.0, 0.0); + const GeomProp::TangentResult aRes = + GeomProp::ComputeTangent(aD1, aD2, aD3, Precision::Confusion()); + EXPECT_FALSE(aRes.IsDefined); +} + +// ============================================================================ +// Free function tests - ComputeCurvature +// ============================================================================ + +TEST(GeomPropTest, ComputeCurvature_CircularArc) +{ + // At (1,0,0) on unit circle in XY plane: D1=(0,1,0), D2=(-1,0,0) + const gp_Vec aD1(0.0, 1.0, 0.0); + const gp_Vec aD2(-1.0, 0.0, 0.0); + const GeomProp::CurvatureResult aRes = + GeomProp::ComputeCurvature(aD1, aD2, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_FALSE(aRes.IsInfinite); + EXPECT_NEAR(aRes.Value, 1.0, 1.0e-10); +} + +TEST(GeomPropTest, ComputeCurvature_ZeroD1) +{ + const gp_Vec aD1(0.0, 0.0, 0.0); + const gp_Vec aD2(1.0, 0.0, 0.0); + const GeomProp::CurvatureResult aRes = + GeomProp::ComputeCurvature(aD1, aD2, Precision::Confusion()); + EXPECT_TRUE(aRes.IsDefined); + EXPECT_TRUE(aRes.IsInfinite); +} + +TEST(GeomPropTest, ComputeCurvature_StraightLine) +{ + const gp_Vec aD1(1.0, 0.0, 0.0); + const gp_Vec aD2(0.0, 0.0, 0.0); + const GeomProp::CurvatureResult aRes = + GeomProp::ComputeCurvature(aD1, aD2, Precision::Confusion()); + EXPECT_TRUE(aRes.IsDefined); + EXPECT_NEAR(aRes.Value, 0.0, Precision::Confusion()); +} + +// ============================================================================ +// Free function tests - ComputeNormal +// ============================================================================ + +TEST(GeomPropTest, ComputeNormal_CircularArc) +{ + const gp_Vec aD1(0.0, 1.0, 0.0); + const gp_Vec aD2(-1.0, 0.0, 0.0); + const GeomProp::NormalResult aRes = GeomProp::ComputeNormal(aD1, aD2, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_NEAR(aRes.Direction.X(), -1.0, Precision::Confusion()); +} + +// ============================================================================ +// Free function tests - ComputeSurfaceNormal +// ============================================================================ + +TEST(GeomPropTest, ComputeSurfaceNormal_XYPlane) +{ + const gp_Vec aD1U(1.0, 0.0, 0.0); + const gp_Vec aD1V(0.0, 1.0, 0.0); + const GeomProp::SurfaceNormalResult aRes = + GeomProp::ComputeSurfaceNormal(aD1U, aD1V, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_NEAR(std::abs(aRes.Direction.Z()), 1.0, Precision::Confusion()); +} + +TEST(GeomPropTest, ComputeSurfaceNormal_DegeneratePoint) +{ + const gp_Vec aD1U(0.0, 0.0, 0.0); + const gp_Vec aD1V(0.0, 1.0, 0.0); + const GeomProp::SurfaceNormalResult aRes = + GeomProp::ComputeSurfaceNormal(aD1U, aD1V, Precision::Confusion()); + EXPECT_FALSE(aRes.IsDefined); +} + +// ============================================================================ +// Free function tests - ComputeMeanGaussian +// ============================================================================ + +TEST(GeomPropTest, ComputeMeanGaussian_Sphere) +{ + // At north pole of unit sphere, D1U and D1V are orthogonal unit vectors + const gp_Vec aD1U(1.0, 0.0, 0.0); + const gp_Vec aD1V(0.0, 1.0, 0.0); + const gp_Vec aD2U(0.0, 0.0, -1.0); + const gp_Vec aD2V(0.0, 0.0, -1.0); + const gp_Vec aDUV(0.0, 0.0, 0.0); + const GeomProp::MeanGaussianResult aRes = + GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aDUV, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_NEAR(aRes.MeanCurvature, -1.0, 1.0e-10); + EXPECT_NEAR(aRes.GaussianCurvature, 1.0, 1.0e-10); +} + +// ============================================================================ +// GeomProp_Curve - initialization and basic queries +// ============================================================================ + +TEST(GeomPropCurveTest, UninitializedState) +{ + GeomProp_Curve aProp; + EXPECT_FALSE(aProp.IsInitialized()); +} + +TEST(GeomPropCurveTest, InitializeFromNullHandle) +{ + GeomProp_Curve aProp; + occ::handle aNullCurve; + aProp.Initialize(aNullCurve); + EXPECT_FALSE(aProp.IsInitialized()); +} + +TEST(GeomPropCurveTest, Line_ZeroCurvature) +{ + occ::handle aLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)); + GeomProp_Curve aProp; + aProp.Initialize(aLine); + ASSERT_TRUE(aProp.IsInitialized()); + EXPECT_EQ(aProp.GetType(), GeomAbs_Line); + + const GeomProp::CurvatureResult aCurv = aProp.Curvature(0.5, Precision::Confusion()); + EXPECT_TRUE(aCurv.IsDefined); + EXPECT_NEAR(aCurv.Value, 0.0, Precision::Confusion()); +} + +TEST(GeomPropCurveTest, Circle_ConstantCurvature) +{ + const double aRadius = 5.0; + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aRadius); + occ::handle aCircle = new Geom_Circle(aCirc); + + GeomProp_Curve aProp; + aProp.Initialize(aCircle); + ASSERT_TRUE(aProp.IsInitialized()); + EXPECT_EQ(aProp.GetType(), GeomAbs_Circle); + + const GeomProp::CurvatureResult aCurv = aProp.Curvature(1.0, Precision::Confusion()); + ASSERT_TRUE(aCurv.IsDefined); + EXPECT_NEAR(aCurv.Value, 1.0 / aRadius, 1.0e-10); +} + +TEST(GeomPropCurveTest, Ellipse_CurvatureExtrema) +{ + const double aMajor = 10.0; + const double aMinor = 5.0; + gp_Elips anElips(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aMajor, aMinor); + occ::handle anEllipse = new Geom_Ellipse(anElips); + + GeomProp_Curve aProp; + aProp.Initialize(anEllipse); + ASSERT_TRUE(aProp.IsInitialized()); + + const GeomProp::CurveAnalysis aResult = aProp.FindCurvatureExtrema(); + ASSERT_TRUE(aResult.IsDone); + EXPECT_EQ(aResult.Points.Length(), 4); +} + +TEST(GeomPropCurveTest, Hyperbola_SingleExtremum) +{ + gp_Hypr anHypr(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 6.0, 3.0); + occ::handle aHyperbola = new Geom_Hyperbola(anHypr); + + GeomProp_Curve aProp; + aProp.Initialize(aHyperbola); + ASSERT_TRUE(aProp.IsInitialized()); + + const GeomProp::CurveAnalysis aResult = aProp.FindCurvatureExtrema(); + ASSERT_TRUE(aResult.IsDone); + EXPECT_EQ(aResult.Points.Length(), 1); + EXPECT_NEAR(aResult.Points[0].Parameter, 0.0, Precision::Confusion()); +} + +TEST(GeomPropCurveTest, Parabola_SingleExtremum) +{ + gp_Parab aParab(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 2.0); + occ::handle aParabola = new Geom_Parabola(aParab); + + GeomProp_Curve aProp; + aProp.Initialize(aParabola); + ASSERT_TRUE(aProp.IsInitialized()); + + const GeomProp::CurveAnalysis aResult = aProp.FindCurvatureExtrema(); + ASSERT_TRUE(aResult.IsDone); + EXPECT_EQ(aResult.Points.Length(), 1); + EXPECT_NEAR(aResult.Points[0].Parameter, 0.0, Precision::Confusion()); +} + +TEST(GeomPropCurveTest, Circle_NoExtrema) +{ + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0); + occ::handle aCircle = new Geom_Circle(aCirc); + + GeomProp_Curve aProp; + aProp.Initialize(aCircle); + const GeomProp::CurveAnalysis aResult = aProp.FindCurvatureExtrema(); + ASSERT_TRUE(aResult.IsDone); + EXPECT_EQ(aResult.Points.Length(), 0); +} + +TEST(GeomPropCurveTest, Circle_NoInflections) +{ + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0); + occ::handle aCircle = new Geom_Circle(aCirc); + + GeomProp_Curve aProp; + aProp.Initialize(aCircle); + const GeomProp::CurveAnalysis aResult = aProp.FindInflections(); + ASSERT_TRUE(aResult.IsDone); + EXPECT_EQ(aResult.Points.Length(), 0); +} + +TEST(GeomPropCurveTest, BezierCurve_Inflections) +{ + NCollection_Array1 aPoles(1, 4); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(1, 2, 0); + aPoles(3) = gp_Pnt(3, -1, 0); + aPoles(4) = gp_Pnt(4, 1, 0); + occ::handle aBezier = new Geom_BezierCurve(aPoles); + + GeomProp_Curve aProp; + aProp.Initialize(aBezier); + ASSERT_TRUE(aProp.IsInitialized()); + EXPECT_EQ(aProp.GetType(), GeomAbs_BezierCurve); + + const GeomProp::CurveAnalysis aResult = aProp.FindInflections(); + ASSERT_TRUE(aResult.IsDone); + EXPECT_GE(aResult.Points.Length(), 1); +} + +TEST(GeomPropCurveTest, Line_TangentDirection) +{ + occ::handle aLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)); + GeomProp_Curve aProp; + aProp.Initialize(aLine); + + const GeomProp::TangentResult aTan = aProp.Tangent(5.0, Precision::Confusion()); + ASSERT_TRUE(aTan.IsDefined); + EXPECT_NEAR(aTan.Direction.Y(), 1.0, Precision::Confusion()); +} + +TEST(GeomPropCurveTest, Circle_Normal) +{ + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0); + occ::handle aCircle = new Geom_Circle(aCirc); + + GeomProp_Curve aProp; + aProp.Initialize(aCircle); + + // At param=0, point is (5,0,0), normal should point toward center (-1,0,0) + const GeomProp::NormalResult aNorm = aProp.Normal(0.0, Precision::Confusion()); + ASSERT_TRUE(aNorm.IsDefined); + EXPECT_NEAR(aNorm.Direction.X(), -1.0, 1.0e-6); +} + +TEST(GeomPropCurveTest, Circle_CentreOfCurvature) +{ + gp_Circ aCirc(gp_Ax2(gp_Pnt(1, 2, 3), gp_Dir(0, 0, 1)), 5.0); + occ::handle aCircle = new Geom_Circle(aCirc); + + GeomProp_Curve aProp; + aProp.Initialize(aCircle); + + const GeomProp::CentreResult aCentre = aProp.CentreOfCurvature(0.0, Precision::Confusion()); + ASSERT_TRUE(aCentre.IsDefined); + EXPECT_NEAR(aCentre.Centre.X(), 1.0, 1.0e-6); + EXPECT_NEAR(aCentre.Centre.Y(), 2.0, 1.0e-6); + EXPECT_NEAR(aCentre.Centre.Z(), 3.0, 1.0e-6); +} + +// ============================================================================ +// GeomProp_Surface - initialization and basic queries +// ============================================================================ + +TEST(GeomPropSurfaceTest, UninitializedState) +{ + GeomProp_Surface aProp; + EXPECT_FALSE(aProp.IsInitialized()); +} + +TEST(GeomPropSurfaceTest, InitializeFromNullHandle) +{ + GeomProp_Surface aProp; + occ::handle aNullSurf; + aProp.Initialize(aNullSurf); + EXPECT_FALSE(aProp.IsInitialized()); +} + +TEST(GeomPropSurfaceTest, Plane_ZeroCurvatures) +{ + occ::handle aPlane = new Geom_Plane(gp_Ax3()); + + GeomProp_Surface aProp; + aProp.Initialize(aPlane); + ASSERT_TRUE(aProp.IsInitialized()); + EXPECT_EQ(aProp.GetType(), GeomAbs_Plane); + + const GeomProp::SurfaceCurvatureResult aCurv = aProp.Curvatures(0.0, 0.0, Precision::Confusion()); + ASSERT_TRUE(aCurv.IsDefined); + EXPECT_TRUE(aCurv.IsUmbilic); + EXPECT_NEAR(aCurv.MinCurvature, 0.0, Precision::Confusion()); + EXPECT_NEAR(aCurv.MaxCurvature, 0.0, Precision::Confusion()); +} + +TEST(GeomPropSurfaceTest, Plane_Normal) +{ + occ::handle aPlane = new Geom_Plane(gp_Ax3()); + + GeomProp_Surface aProp; + aProp.Initialize(aPlane); + + const GeomProp::SurfaceNormalResult aNorm = aProp.Normal(0.0, 0.0, Precision::Confusion()); + ASSERT_TRUE(aNorm.IsDefined); + EXPECT_NEAR(std::abs(aNorm.Direction.Z()), 1.0, Precision::Confusion()); +} + +TEST(GeomPropSurfaceTest, Plane_MeanGaussian) +{ + occ::handle aPlane = new Geom_Plane(gp_Ax3()); + + GeomProp_Surface aProp; + aProp.Initialize(aPlane); + + const GeomProp::MeanGaussianResult aRes = aProp.MeanGaussian(0.0, 0.0, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_NEAR(aRes.MeanCurvature, 0.0, Precision::Confusion()); + EXPECT_NEAR(aRes.GaussianCurvature, 0.0, Precision::Confusion()); +} + +TEST(GeomPropSurfaceTest, Sphere_ConstantCurvature) +{ + const double aRadius = 5.0; + occ::handle aSphere = new Geom_SphericalSurface(gp_Ax3(), aRadius); + + GeomProp_Surface aProp; + aProp.Initialize(aSphere); + ASSERT_TRUE(aProp.IsInitialized()); + EXPECT_EQ(aProp.GetType(), GeomAbs_Sphere); + + // Curvature sign depends on normal orientation. For outward-pointing normal, + // convex surfaces have negative curvature (center on opposite side of normal). + const GeomProp::SurfaceCurvatureResult aCurv = aProp.Curvatures(0.5, 0.5, Precision::Confusion()); + ASSERT_TRUE(aCurv.IsDefined); + EXPECT_NEAR(std::abs(aCurv.MinCurvature), 1.0 / aRadius, 1.0e-10); + EXPECT_NEAR(std::abs(aCurv.MaxCurvature), 1.0 / aRadius, 1.0e-10); +} + +TEST(GeomPropSurfaceTest, Sphere_MeanGaussian) +{ + const double aRadius = 5.0; + occ::handle aSphere = new Geom_SphericalSurface(gp_Ax3(), aRadius); + + GeomProp_Surface aProp; + aProp.Initialize(aSphere); + + const GeomProp::MeanGaussianResult aRes = aProp.MeanGaussian(0.5, 0.5, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_NEAR(std::abs(aRes.MeanCurvature), 1.0 / aRadius, 1.0e-10); + EXPECT_NEAR(aRes.GaussianCurvature, 1.0 / (aRadius * aRadius), 1.0e-10); +} + +TEST(GeomPropSurfaceTest, Cylinder_Curvatures) +{ + const double aRadius = 3.0; + occ::handle aCyl = new Geom_CylindricalSurface(gp_Ax3(), aRadius); + + GeomProp_Surface aProp; + aProp.Initialize(aCyl); + ASSERT_TRUE(aProp.IsInitialized()); + EXPECT_EQ(aProp.GetType(), GeomAbs_Cylinder); + + const GeomProp::SurfaceCurvatureResult aCurv = aProp.Curvatures(0.5, 1.0, Precision::Confusion()); + ASSERT_TRUE(aCurv.IsDefined); + // One curvature is zero (along axis), the other is non-zero (1/R with sign from normal). + const double aAbsMin = std::abs(aCurv.MinCurvature); + const double aAbsMax = std::abs(aCurv.MaxCurvature); + EXPECT_TRUE(aAbsMin < 1.0e-10 || aAbsMax < 1.0e-10); + EXPECT_NEAR(std::max(aAbsMin, aAbsMax), 1.0 / aRadius, 1.0e-10); +} + +TEST(GeomPropSurfaceTest, Cylinder_MeanGaussian) +{ + const double aRadius = 3.0; + occ::handle aCyl = new Geom_CylindricalSurface(gp_Ax3(), aRadius); + + GeomProp_Surface aProp; + aProp.Initialize(aCyl); + + const GeomProp::MeanGaussianResult aRes = aProp.MeanGaussian(0.5, 1.0, Precision::Confusion()); + ASSERT_TRUE(aRes.IsDefined); + EXPECT_NEAR(std::abs(aRes.MeanCurvature), 1.0 / (2.0 * aRadius), 1.0e-10); + EXPECT_NEAR(aRes.GaussianCurvature, 0.0, Precision::Confusion()); +} + +TEST(GeomPropSurfaceTest, Cone_CurvaturesVaryAlongV) +{ + gp_Ax3 anAx3; + occ::handle aCone = new Geom_ConicalSurface(anAx3, M_PI / 6.0, 5.0); + + GeomProp_Surface aProp; + aProp.Initialize(aCone); + ASSERT_TRUE(aProp.IsInitialized()); + EXPECT_EQ(aProp.GetType(), GeomAbs_Cone); + + const GeomProp::SurfaceCurvatureResult aCurv1 = + aProp.Curvatures(0.5, 0.0, Precision::Confusion()); + const GeomProp::SurfaceCurvatureResult aCurv2 = + aProp.Curvatures(0.5, 5.0, Precision::Confusion()); + ASSERT_TRUE(aCurv1.IsDefined); + ASSERT_TRUE(aCurv2.IsDefined); + // One curvature is zero (along ruling), the other varies with V. + // The magnitude of the non-zero curvature should decrease with V (larger radius). + const double aNonZero1 = std::max(std::abs(aCurv1.MinCurvature), std::abs(aCurv1.MaxCurvature)); + const double aNonZero2 = std::max(std::abs(aCurv2.MinCurvature), std::abs(aCurv2.MaxCurvature)); + EXPECT_GT(aNonZero1, aNonZero2); +} + +TEST(GeomPropSurfaceTest, Torus_CurvaturesVaryAlongV) +{ + const double aMajor = 10.0; + const double aMinor = 3.0; + occ::handle aTorus = new Geom_ToroidalSurface(gp_Ax3(), aMajor, aMinor); + + GeomProp_Surface aProp; + aProp.Initialize(aTorus); + ASSERT_TRUE(aProp.IsInitialized()); + EXPECT_EQ(aProp.GetType(), GeomAbs_Torus); + + // At V=0 (outer edge): k1=1/r, k2=1/(R+r) + const GeomProp::MeanGaussianResult aRes0 = aProp.MeanGaussian(0.0, 0.0, Precision::Confusion()); + ASSERT_TRUE(aRes0.IsDefined); + EXPECT_GT(aRes0.GaussianCurvature, 0.0); // Both curvatures positive at outer edge + + // At V=PI (inner edge): k1=1/r, k2=-1/(R-r) (negative) + const GeomProp::MeanGaussianResult aResPI = aProp.MeanGaussian(0.0, M_PI, Precision::Confusion()); + ASSERT_TRUE(aResPI.IsDefined); + EXPECT_LT(aResPI.GaussianCurvature, 0.0); // Negative Gaussian curvature at inner edge +} diff --git a/src/ModelingData/TKG3d/GTests/GeomProp_VsCLProps_Test.cxx b/src/ModelingData/TKG3d/GTests/GeomProp_VsCLProps_Test.cxx new file mode 100644 index 0000000000..cb2419612a --- /dev/null +++ b/src/ModelingData/TKG3d/GTests/GeomProp_VsCLProps_Test.cxx @@ -0,0 +1,248 @@ +// 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. + +// Cross-validation tests comparing GeomProp_Curve against GeomLProp_CLProps +// for local curve differential properties (tangent, curvature, normal, centre). + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace +{ +constexpr double THE_LIN_TOL = Precision::PConfusion(); +constexpr double THE_CURV_TOL = 1.0e-8; +constexpr double THE_DIR_TOL = 1.0e-6; +constexpr double THE_POINT_TOL = 1.0e-6; + +//! Compare tangent from new GeomProp_Curve vs old GeomLProp_CLProps. +void compareTangent(const occ::handle& theCurve, const double theParam) +{ + GeomProp_Curve aProp; + aProp.Initialize(theCurve); + const GeomProp::TangentResult aNew = aProp.Tangent(theParam, THE_LIN_TOL); + + GeomLProp_CLProps anOld(theCurve, theParam, 2, THE_LIN_TOL); + if (anOld.IsTangentDefined()) + { + ASSERT_TRUE(aNew.IsDefined) << "New tangent undefined but old is defined at param=" << theParam; + gp_Dir anOldDir; + anOld.Tangent(anOldDir); + // Tangent directions may differ by sign + const double aDot = aNew.Direction.Dot(anOldDir); + EXPECT_NEAR(std::abs(aDot), 1.0, THE_DIR_TOL) + << "Tangent direction mismatch at param=" << theParam; + } +} + +//! Compare curvature from new GeomProp_Curve vs old GeomLProp_CLProps. +void compareCurvature(const occ::handle& theCurve, const double theParam) +{ + GeomProp_Curve aProp; + aProp.Initialize(theCurve); + const GeomProp::CurvatureResult aNew = aProp.Curvature(theParam, THE_LIN_TOL); + + GeomLProp_CLProps anOld(theCurve, theParam, 2, THE_LIN_TOL); + if (anOld.IsTangentDefined()) + { + ASSERT_TRUE(aNew.IsDefined) << "New curvature undefined at param=" << theParam; + EXPECT_NEAR(aNew.Value, anOld.Curvature(), THE_CURV_TOL) + << "Curvature mismatch at param=" << theParam; + } +} + +//! Compare normal from new GeomProp_Curve vs old GeomLProp_CLProps. +void compareNormal(const occ::handle& theCurve, const double theParam) +{ + GeomProp_Curve aProp; + aProp.Initialize(theCurve); + const GeomProp::NormalResult aNew = aProp.Normal(theParam, THE_LIN_TOL); + + GeomLProp_CLProps anOld(theCurve, theParam, 2, THE_LIN_TOL); + if (anOld.IsTangentDefined() && std::abs(anOld.Curvature()) > THE_LIN_TOL) + { + ASSERT_TRUE(aNew.IsDefined) << "New normal undefined at param=" << theParam; + gp_Dir anOldNorm; + anOld.Normal(anOldNorm); + const double aDot = aNew.Direction.Dot(anOldNorm); + EXPECT_NEAR(std::abs(aDot), 1.0, THE_DIR_TOL) + << "Normal direction mismatch at param=" << theParam; + } +} + +//! Compare centre of curvature from new vs old. +void compareCentre(const occ::handle& theCurve, const double theParam) +{ + GeomProp_Curve aProp; + aProp.Initialize(theCurve); + const GeomProp::CentreResult aNew = aProp.CentreOfCurvature(theParam, THE_LIN_TOL); + + GeomLProp_CLProps anOld(theCurve, theParam, 2, THE_LIN_TOL); + if (anOld.IsTangentDefined() && std::abs(anOld.Curvature()) > THE_LIN_TOL) + { + ASSERT_TRUE(aNew.IsDefined) << "New centre undefined at param=" << theParam; + gp_Pnt anOldCentre; + anOld.CentreOfCurvature(anOldCentre); + EXPECT_NEAR(aNew.Centre.Distance(anOldCentre), 0.0, THE_POINT_TOL) + << "Centre mismatch at param=" << theParam; + } +} + +//! Run all comparisons at several parameter values. +void compareAll(const occ::handle& theCurve, + const double theFirst, + const double theLast, + const int theNbSamples = 10) +{ + const double aStep = (theLast - theFirst) / theNbSamples; + for (int i = 0; i <= theNbSamples; ++i) + { + const double aParam = theFirst + i * aStep; + compareTangent(theCurve, aParam); + compareCurvature(theCurve, aParam); + compareNormal(theCurve, aParam); + compareCentre(theCurve, aParam); + } +} +} // namespace + +// ============================================================================ +// Line +// ============================================================================ + +TEST(GeomProp_VsCLPropsTest, Line) +{ + occ::handle aLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 1, 0)); + compareAll(aLine, -5.0, 5.0); +} + +// ============================================================================ +// Circle +// ============================================================================ + +TEST(GeomProp_VsCLPropsTest, Circle) +{ + gp_Circ aCirc(gp_Ax2(gp_Pnt(1, 2, 3), gp_Dir(0, 0, 1)), 5.0); + occ::handle aCircle = new Geom_Circle(aCirc); + compareAll(aCircle, 0.0, 2.0 * M_PI); +} + +// ============================================================================ +// Ellipse +// ============================================================================ + +TEST(GeomProp_VsCLPropsTest, Ellipse) +{ + gp_Elips anElips(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0, 5.0); + occ::handle anEllipse = new Geom_Ellipse(anElips); + compareAll(anEllipse, 0.0, 2.0 * M_PI); +} + +// ============================================================================ +// Hyperbola +// ============================================================================ + +TEST(GeomProp_VsCLPropsTest, Hyperbola) +{ + gp_Hypr anHypr(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 6.0, 3.0); + occ::handle aHyperbola = new Geom_Hyperbola(anHypr); + compareAll(aHyperbola, -2.0, 2.0); +} + +// ============================================================================ +// Parabola +// ============================================================================ + +TEST(GeomProp_VsCLPropsTest, Parabola) +{ + gp_Parab aParab(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 2.0); + occ::handle aParabola = new Geom_Parabola(aParab); + compareAll(aParabola, -5.0, 5.0); +} + +// ============================================================================ +// Bezier +// ============================================================================ + +TEST(GeomProp_VsCLPropsTest, BezierCubic) +{ + NCollection_Array1 aPoles(1, 4); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(1, 2, 1); + aPoles(3) = gp_Pnt(3, -1, 0); + aPoles(4) = gp_Pnt(4, 1, 1); + occ::handle aBezier = new Geom_BezierCurve(aPoles); + compareAll(aBezier, 0.0, 1.0); +} + +// ============================================================================ +// BSpline +// ============================================================================ + +TEST(GeomProp_VsCLPropsTest, BSplineCubic) +{ + NCollection_Array1 aPoles(1, 6); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(1, 3, 0); + aPoles(3) = gp_Pnt(2, 1, 1); + aPoles(4) = gp_Pnt(3, 4, 0); + aPoles(5) = gp_Pnt(4, 2, 1); + aPoles(6) = gp_Pnt(5, 0, 0); + + NCollection_Array1 aKnots(1, 4); + aKnots(1) = 0.0; + aKnots(2) = 0.33; + aKnots(3) = 0.66; + aKnots(4) = 1.0; + + NCollection_Array1 aMults(1, 4); + aMults(1) = 4; + aMults(2) = 1; + aMults(3) = 1; + aMults(4) = 4; + + occ::handle aBSpline = new Geom_BSplineCurve(aPoles, aKnots, aMults, 3); + compareAll(aBSpline, 0.0, 1.0); +} + +// ============================================================================ +// Offset curve +// ============================================================================ + +TEST(GeomProp_VsCLPropsTest, OffsetCircle) +{ + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0); + occ::handle aCircle = new Geom_Circle(aCirc); + occ::handle anOffset = new Geom_OffsetCurve(aCircle, 2.0, gp_Dir(0, 0, 1)); + compareAll(anOffset, 0.0, 2.0 * M_PI); +} diff --git a/src/ModelingData/TKG3d/GTests/GeomProp_VsSLProps_Test.cxx b/src/ModelingData/TKG3d/GTests/GeomProp_VsSLProps_Test.cxx new file mode 100644 index 0000000000..d9cb02fbb5 --- /dev/null +++ b/src/ModelingData/TKG3d/GTests/GeomProp_VsSLProps_Test.cxx @@ -0,0 +1,213 @@ +// 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. + +// Cross-validation tests comparing GeomProp_Surface against GeomLProp_SLProps +// for local surface differential properties (normal, curvatures). + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace +{ +constexpr double THE_LIN_TOL = Precision::PConfusion(); +constexpr double THE_CURV_TOL = 1.0e-6; +constexpr double THE_DIR_TOL = 1.0e-4; + +//! Compare surface normal from new GeomProp_Surface vs old GeomLProp_SLProps. +void compareNormal(const occ::handle& theSurf, const double theU, const double theV) +{ + GeomProp_Surface aProp; + aProp.Initialize(theSurf); + const GeomProp::SurfaceNormalResult aNew = aProp.Normal(theU, theV, THE_LIN_TOL); + + GeomLProp_SLProps anOld(theSurf, theU, theV, 2, THE_LIN_TOL); + if (anOld.IsNormalDefined()) + { + ASSERT_TRUE(aNew.IsDefined) << "New normal undefined at (" << theU << "," << theV << ")"; + const gp_Dir anOldNorm = anOld.Normal(); + const double aDot = aNew.Direction.Dot(anOldNorm); + EXPECT_NEAR(std::abs(aDot), 1.0, THE_DIR_TOL) + << "Normal mismatch at (" << theU << "," << theV << ")"; + } +} + +//! Compare curvatures from new GeomProp_Surface vs old GeomLProp_SLProps. +void compareCurvatures(const occ::handle& theSurf, + const double theU, + const double theV) +{ + GeomProp_Surface aProp; + aProp.Initialize(theSurf); + const GeomProp::SurfaceCurvatureResult aNew = aProp.Curvatures(theU, theV, THE_LIN_TOL); + + GeomLProp_SLProps anOld(theSurf, theU, theV, 2, THE_LIN_TOL); + if (anOld.IsCurvatureDefined()) + { + ASSERT_TRUE(aNew.IsDefined) << "New curvatures undefined at (" << theU << "," << theV << ")"; + EXPECT_NEAR(aNew.MinCurvature, anOld.MinCurvature(), THE_CURV_TOL) + << "MinCurvature mismatch at (" << theU << "," << theV << ")"; + EXPECT_NEAR(aNew.MaxCurvature, anOld.MaxCurvature(), THE_CURV_TOL) + << "MaxCurvature mismatch at (" << theU << "," << theV << ")"; + // Note: IsUmbilic comparison intentionally omitted - the flag is tolerance-dependent + // and may differ between implementations while curvature values agree. + } +} + +//! Compare mean and Gaussian curvatures. +void compareMeanGaussian(const occ::handle& theSurf, + const double theU, + const double theV) +{ + GeomProp_Surface aProp; + aProp.Initialize(theSurf); + const GeomProp::MeanGaussianResult aNew = aProp.MeanGaussian(theU, theV, THE_LIN_TOL); + + GeomLProp_SLProps anOld(theSurf, theU, theV, 2, THE_LIN_TOL); + if (anOld.IsCurvatureDefined()) + { + ASSERT_TRUE(aNew.IsDefined) << "New MeanGaussian undefined at (" << theU << "," << theV << ")"; + EXPECT_NEAR(aNew.MeanCurvature, anOld.MeanCurvature(), THE_CURV_TOL) + << "Mean curvature mismatch at (" << theU << "," << theV << ")"; + EXPECT_NEAR(aNew.GaussianCurvature, anOld.GaussianCurvature(), THE_CURV_TOL) + << "Gaussian curvature mismatch at (" << theU << "," << theV << ")"; + } +} + +//! Run all surface comparisons at a grid of parameter values. +void compareAllSurface(const occ::handle& theSurf, + const double theUMin, + const double theUMax, + const double theVMin, + const double theVMax, + const int theNbU = 5, + const int theNbV = 5) +{ + const double aUStep = (theUMax - theUMin) / theNbU; + const double aVStep = (theVMax - theVMin) / theNbV; + for (int i = 0; i <= theNbU; ++i) + { + for (int j = 0; j <= theNbV; ++j) + { + const double aU = theUMin + i * aUStep; + const double aV = theVMin + j * aVStep; + compareNormal(theSurf, aU, aV); + compareCurvatures(theSurf, aU, aV); + compareMeanGaussian(theSurf, aU, aV); + } + } +} +} // namespace + +// ============================================================================ +// Plane +// ============================================================================ + +TEST(GeomProp_VsSLPropsTest, Plane) +{ + occ::handle aPlane = new Geom_Plane(gp_Ax3()); + compareAllSurface(aPlane, -5.0, 5.0, -5.0, 5.0); +} + +// ============================================================================ +// Sphere +// ============================================================================ + +TEST(GeomProp_VsSLPropsTest, Sphere) +{ + occ::handle aSphere = new Geom_SphericalSurface(gp_Ax3(), 5.0); + // Avoid poles where D1U degenerates + compareAllSurface(aSphere, 0.0, 2.0 * M_PI, -M_PI / 3.0, M_PI / 3.0); +} + +// ============================================================================ +// Cylinder +// ============================================================================ + +TEST(GeomProp_VsSLPropsTest, Cylinder) +{ + occ::handle aCyl = new Geom_CylindricalSurface(gp_Ax3(), 3.0); + compareAllSurface(aCyl, 0.0, 2.0 * M_PI, -5.0, 5.0); +} + +// ============================================================================ +// Cone +// ============================================================================ + +TEST(GeomProp_VsSLPropsTest, Cone) +{ + occ::handle aCone = new Geom_ConicalSurface(gp_Ax3(), M_PI / 6.0, 5.0); + // Stay away from the apex (at V = -R/sin(alpha) = -10) + compareAllSurface(aCone, 0.0, 2.0 * M_PI, 0.0, 10.0); +} + +// ============================================================================ +// Torus +// ============================================================================ + +TEST(GeomProp_VsSLPropsTest, Torus) +{ + occ::handle aTorus = new Geom_ToroidalSurface(gp_Ax3(), 10.0, 3.0); + compareAllSurface(aTorus, 0.0, 2.0 * M_PI, 0.0, 2.0 * M_PI); +} + +// ============================================================================ +// BSpline Surface +// ============================================================================ + +TEST(GeomProp_VsSLPropsTest, BSplineSurface) +{ + // Simple 4x4 bicubic patch + NCollection_Array2 aPoles(1, 4, 1, 4); + NCollection_Array1 aUKnots(1, 2), aVKnots(1, 2); + NCollection_Array1 aUMults(1, 2), aVMults(1, 2); + + aUKnots(1) = 0.0; + aUKnots(2) = 1.0; + aVKnots(1) = 0.0; + aVKnots(2) = 1.0; + aUMults(1) = 4; + aUMults(2) = 4; + aVMults(1) = 4; + aVMults(2) = 4; + + for (int i = 1; i <= 4; ++i) + { + for (int j = 1; j <= 4; ++j) + { + const double aX = i - 1; + const double aY = j - 1; + const double aZ = std::sin((i - 1) * 0.5) * std::cos((j - 1) * 0.5); + aPoles.SetValue(i, j, gp_Pnt(aX, aY, aZ)); + } + } + + occ::handle aSurf = + new Geom_BSplineSurface(aPoles, aUKnots, aVKnots, aUMults, aVMults, 3, 3); + + compareAllSurface(aSurf, 0.0, 1.0, 0.0, 1.0, 4, 4); +} diff --git a/src/ModelingData/TKG3d/GeomProp/FILES.cmake b/src/ModelingData/TKG3d/GeomProp/FILES.cmake new file mode 100644 index 0000000000..50c2f3cbef --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/FILES.cmake @@ -0,0 +1,46 @@ +# Source files for GeomProp package +set(OCCT_GeomProp_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") + +set(OCCT_GeomProp_FILES + GeomProp.hxx + GeomProp.cxx + GeomProp_BezierCurve.hxx + GeomProp_BezierCurve.cxx + GeomProp_BezierSurface.hxx + GeomProp_BezierSurface.cxx + GeomProp_BSplineCurve.hxx + GeomProp_BSplineCurve.cxx + GeomProp_BSplineSurface.hxx + GeomProp_BSplineSurface.cxx + GeomProp_Circle.hxx + GeomProp_Cone.hxx + GeomProp_Cone.cxx + GeomProp_Curve.hxx + GeomProp_Curve.cxx + GeomProp_Cylinder.hxx + GeomProp_Ellipse.hxx + GeomProp_Ellipse.cxx + GeomProp_Hyperbola.hxx + GeomProp_Hyperbola.cxx + GeomProp_Line.hxx + GeomProp_OffsetCurve.hxx + GeomProp_OffsetCurve.cxx + GeomProp_OffsetSurface.hxx + GeomProp_OffsetSurface.cxx + GeomProp_OtherCurve.hxx + GeomProp_OtherCurve.cxx + GeomProp_OtherSurface.hxx + GeomProp_OtherSurface.cxx + GeomProp_Parabola.hxx + GeomProp_Parabola.cxx + GeomProp_Plane.hxx + GeomProp_Sphere.hxx + GeomProp_Surface.hxx + GeomProp_Surface.cxx + GeomProp_SurfaceOfExtrusion.hxx + GeomProp_SurfaceOfExtrusion.cxx + GeomProp_SurfaceOfRevolution.hxx + GeomProp_SurfaceOfRevolution.cxx + GeomProp_Torus.hxx + GeomProp_Torus.cxx +) diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp.cxx new file mode 100644 index 0000000000..260fab45da --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp.cxx @@ -0,0 +1,315 @@ +// 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 + +//================================================================================================== + +GeomProp::TangentResult GeomProp::ComputeTangent(const gp_Vec& theD1, + const gp_Vec& theD2, + const gp_Vec& theD3, + const double theTol) +{ + const double aTol2 = theTol * theTol; + + // Try first derivative + if (theD1.SquareMagnitude() > aTol2) + { + return {gp_Dir(theD1), true}; + } + + // Try second derivative + if (theD2.SquareMagnitude() > aTol2) + { + return {gp_Dir(theD2), true}; + } + + // Try third derivative + if (theD3.SquareMagnitude() > aTol2) + { + return {gp_Dir(theD3), true}; + } + + return {{}, false}; +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp::ComputeCurvature(const gp_Vec& theD1, + const gp_Vec& theD2, + const double theTol) +{ + const double aTol2 = theTol * theTol; + const double aDD1 = theD1.SquareMagnitude(); + + // If first derivative is null, curvature is infinite (singular point). + if (aDD1 <= aTol2) + { + return {0.0, true, true}; + } + + const double aDD2 = theD2.SquareMagnitude(); + + // If second derivative is null, curvature is zero. + if (aDD2 <= aTol2) + { + return {0.0, true, false}; + } + + // Cross product magnitude squared: |D1 x D2|^2 + const gp_Vec aCross = theD1.Crossed(theD2); + const double aN = aCross.SquareMagnitude(); + + // If D1 and D2 are collinear, curvature is zero. + const double aT = aN / aDD1 / aDD2; + if (aT <= aTol2) + { + return {0.0, true, false}; + } + + // Curvature = |D1 x D2| / |D1|^3 + const double aCurvature = std::sqrt(aN) / aDD1 / std::sqrt(aDD1); + return {aCurvature, true, false}; +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp::ComputeNormal(const gp_Vec& theD1, + const gp_Vec& theD2, + const double theTol) +{ + // First compute curvature to check if normal is defined. + const CurvatureResult aCurvRes = ComputeCurvature(theD1, theD2, theTol); + if (!aCurvRes.IsDefined || aCurvRes.IsInfinite || std::abs(aCurvRes.Value) <= theTol) + { + return {{}, false}; + } + + // Normal = D2 * (D1.D1) - D1 * (D1.D2) + // This is equivalent to (D1 x D2) x D1 using the vector triple product identity. + const gp_Vec aNorm = theD2 * theD1.Dot(theD1) - theD1 * theD1.Dot(theD2); + if (aNorm.SquareMagnitude() <= theTol * theTol) + { + return {{}, false}; + } + return {gp_Dir(aNorm), true}; +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp::ComputeCentreOfCurvature(const gp_Pnt& thePnt, + const gp_Vec& theD1, + const gp_Vec& theD2, + const double theTol) +{ + const CurvatureResult aCurvRes = ComputeCurvature(theD1, theD2, theTol); + if (!aCurvRes.IsDefined || aCurvRes.IsInfinite || std::abs(aCurvRes.Value) <= theTol) + { + return {{}, false}; + } + + // Normal vector (unnormalized) = D2 * (D1.D1) - D1 * (D1.D2) + gp_Vec aNorm = theD2 * theD1.Dot(theD1) - theD1 * theD1.Dot(theD2); + aNorm.Normalize(); + aNorm.Divide(aCurvRes.Value); + + return {thePnt.Translated(aNorm), true}; +} + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp::ComputeSurfaceNormal(const gp_Vec& theD1U, + const gp_Vec& theD1V, + const double theTol) +{ + const gp_Vec aCross = theD1U.Crossed(theD1V); + if (aCross.SquareMagnitude() <= theTol * theTol) + { + return {{}, false}; + } + return {gp_Dir(aCross), true}; +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp::ComputeSurfaceCurvatures(const gp_Vec& theD1U, + const gp_Vec& theD1V, + const gp_Vec& theD2U, + const gp_Vec& theD2V, + const gp_Vec& theDUV, + const double theTol) +{ + // Compute surface normal. + const SurfaceNormalResult aNormRes = ComputeSurfaceNormal(theD1U, theD1V, theTol); + if (!aNormRes.IsDefined) + { + return {}; + } + + const gp_Vec aNormal(aNormRes.Direction); + + // First fundamental form coefficients. + const double aE = theD1U.Dot(theD1U); + const double aF = theD1U.Dot(theD1V); + const double aG = theD1V.Dot(theD1V); + + // Second fundamental form coefficients. + const double aL = aNormal.Dot(theD2U); + const double aM = aNormal.Dot(theDUV); + const double aN_ = aNormal.Dot(theD2V); + + // Discriminant of first fundamental form. + const double aDet = aE * aG - aF * aF; + if (std::abs(aDet) <= theTol * theTol) + { + return {}; + } + + // Mean curvature: H = (EN - 2FM + GL) / (2 * det) + const double aH = (aE * aN_ - 2.0 * aF * aM + aG * aL) / (2.0 * aDet); + + // Gaussian curvature: K = (LN - M^2) / det + const double aK = (aL * aN_ - aM * aM) / aDet; + + // Principal curvatures from: k^2 - 2Hk + K = 0 + const double aDiscriminant = aH * aH - aK; + + SurfaceCurvatureResult aResult; + aResult.IsDefined = true; + + if (aDiscriminant <= theTol * theTol) + { + // Umbilic point: both principal curvatures are equal. + aResult.MinCurvature = aH; + aResult.MaxCurvature = aH; + aResult.IsUmbilic = true; + // At umbilic points, directions are undefined - use U and V directions. + if (theD1U.SquareMagnitude() > theTol * theTol) + { + aResult.MinDirection = gp_Dir(theD1U); + } + if (theD1V.SquareMagnitude() > theTol * theTol) + { + aResult.MaxDirection = gp_Dir(theD1V); + } + return aResult; + } + + const double aSqrtDisc = std::sqrt(std::max(aDiscriminant, 0.0)); + const double aK1 = aH - aSqrtDisc; // min curvature + const double aK2 = aH + aSqrtDisc; // max curvature + + aResult.MinCurvature = aK1; + aResult.MaxCurvature = aK2; + aResult.IsUmbilic = false; + + // Compute principal directions from the shape operator (Weingarten map). + // For each principal curvature k, the principal direction (a, b) satisfies: + // (L - kE)*a + (M - kF)*b = 0 + // (M - kF)*a + (N - kG)*b = 0 + // We pick the equation with the largest coefficient to avoid division by near-zero. + for (int i = 0; i < 2; ++i) + { + const double aKi = (i == 0) ? aK1 : aK2; + const double aCoeffA = aL - aKi * aE; + const double aCoeffB = aM - aKi * aF; + // TODO: aCoeffC is always equal to aCoeffB (symmetric shape operator matrix). + // Consider removing the redundant variable and using aCoeffB directly. + const double aCoeffC = aM - aKi * aF; + const double aCoeffD = aN_ - aKi * aG; + + gp_Vec aDir; + if (std::abs(aCoeffA) > std::abs(aCoeffD)) + { + // From first equation: a*coeff_a + b*coeff_b = 0 => b/a = -coeff_a/coeff_b + if (std::abs(aCoeffB) > theTol) + { + aDir = theD1U * (-aCoeffB) + theD1V * aCoeffA; + } + else + { + aDir = theD1V; + } + } + else + { + // From second equation: a*coeff_c + b*coeff_d = 0 => a/b = -coeff_d/coeff_c + if (std::abs(aCoeffC) > theTol) + { + aDir = theD1U * aCoeffD + theD1V * (-aCoeffC); + } + else + { + aDir = theD1U; + } + } + + if (aDir.SquareMagnitude() > theTol * theTol) + { + if (i == 0) + { + aResult.MinDirection = gp_Dir(aDir); + } + else + { + aResult.MaxDirection = gp_Dir(aDir); + } + } + } + + return aResult; +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp::ComputeMeanGaussian(const gp_Vec& theD1U, + const gp_Vec& theD1V, + const gp_Vec& theD2U, + const gp_Vec& theD2V, + const gp_Vec& theDUV, + const double theTol) +{ + // Compute surface normal. + const SurfaceNormalResult aNormRes = ComputeSurfaceNormal(theD1U, theD1V, theTol); + if (!aNormRes.IsDefined) + { + return {}; + } + + const gp_Vec aNormal(aNormRes.Direction); + + // First fundamental form coefficients. + const double aE = theD1U.Dot(theD1U); + const double aF = theD1U.Dot(theD1V); + const double aG = theD1V.Dot(theD1V); + + // Second fundamental form coefficients. + const double aL = aNormal.Dot(theD2U); + const double aM = aNormal.Dot(theDUV); + const double aN_ = aNormal.Dot(theD2V); + + // Discriminant of first fundamental form. + const double aDet = aE * aG - aF * aF; + if (std::abs(aDet) <= theTol * theTol) + { + return {}; + } + + MeanGaussianResult aResult; + aResult.IsDefined = true; + aResult.MeanCurvature = (aE * aN_ - 2.0 * aF * aM + aG * aL) / (2.0 * aDet); + aResult.GaussianCurvature = (aL * aN_ - aM * aM) / aDet; + return aResult; +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp.hxx new file mode 100644 index 0000000000..f11936181a --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp.hxx @@ -0,0 +1,212 @@ +// 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 _GeomProp_HeaderFile +#define _GeomProp_HeaderFile + +#include +#include +#include +#include +#include + +//! @brief Namespace containing result structures and free functions for 3D curve +//! and surface differential property computation. +//! +//! Provides lightweight result structures with explicit validity flags instead of +//! exception-based APIs, and geometry-agnostic free functions that compute local +//! differential properties from derivative vectors. +namespace GeomProp +{ + +// ============================================================================ +// Curve result structures +// ============================================================================ + +//! Result of tangent direction computation. +struct TangentResult +{ + gp_Dir Direction; //!< Tangent direction (valid only when IsDefined is true) + bool IsDefined = false; //!< True if the tangent is well-defined +}; + +//! Result of curvature computation. +struct CurvatureResult +{ + double Value = 0.0; //!< Curvature value (valid only when IsDefined is true) + bool IsDefined = false; //!< True if curvature could be computed + bool IsInfinite = false; //!< True if first derivative is null (singular point) +}; + +//! Result of normal direction computation. +struct NormalResult +{ + gp_Dir Direction; //!< Normal direction (valid only when IsDefined is true) + bool IsDefined = false; //!< True if the normal is well-defined +}; + +//! Result of centre of curvature computation. +struct CentreResult +{ + gp_Pnt Centre; //!< Centre of curvature (valid only when IsDefined is true) + bool IsDefined = false; //!< True if the centre is well-defined +}; + +//! Type of a special curve point (curvature extremum or inflection). +enum class CIType +{ + Inflection, //!< Inflection point (curvature changes sign) + MinCurvature, //!< Local minimum of the radius of curvature (maximum of |curvature|) + MaxCurvature //!< Local maximum of the radius of curvature (minimum of |curvature|) +}; + +//! A special point on a curve with its parameter and type. +struct CurveSpecialPoint +{ + double Parameter = 0.0; //!< Curve parameter + CIType Type = CIType::Inflection; //!< Point type +}; + +//! Result of global curve analysis (curvature extrema and inflection points). +struct CurveAnalysis +{ + NCollection_DynamicArray Points; //!< Special points sorted by parameter + bool IsDone = false; //!< True if analysis completed +}; + +// ============================================================================ +// Surface result structures +// ============================================================================ + +//! Result of surface normal computation. +struct SurfaceNormalResult +{ + gp_Dir Direction; //!< Surface normal direction (valid only when IsDefined is true) + bool IsDefined = false; //!< True if the normal is well-defined +}; + +//! Result of surface principal curvature computation. +struct SurfaceCurvatureResult +{ + double MinCurvature = 0.0; //!< Minimum principal curvature (valid only when IsDefined is true) + double MaxCurvature = 0.0; //!< Maximum principal curvature (valid only when IsDefined is true) + gp_Dir MinDirection; //!< Direction of minimum curvature (valid only when IsDefined is true) + gp_Dir MaxDirection; //!< Direction of maximum curvature (valid only when IsDefined is true) + bool IsDefined = false; //!< True if curvatures could be computed + bool IsUmbilic = false; //!< True if the point is umbilic (all curvatures equal) +}; + +//! Result of mean and Gaussian curvature computation. +struct MeanGaussianResult +{ + double MeanCurvature = 0.0; //!< Mean curvature H = (k1 + k2) / 2 + double GaussianCurvature = 0.0; //!< Gaussian curvature K = k1 * k2 + bool IsDefined = false; //!< True if curvatures could be computed +}; + +// ============================================================================ +// Curve free functions +// ============================================================================ + +//! Compute tangent direction from derivative vectors. +//! Tries D1 first; if D1 magnitude^2 <= theTol^2, tries D2, then D3. +//! @param[in] theD1 first derivative vector +//! @param[in] theD2 second derivative vector +//! @param[in] theD3 third derivative vector +//! @param[in] theTol linear tolerance for zero-vector detection +//! @return tangent result with validity flag +Standard_EXPORT TangentResult ComputeTangent(const gp_Vec& theD1, + const gp_Vec& theD2, + const gp_Vec& theD3, + double theTol); + +//! Compute curvature from first and second derivative vectors. +//! Curvature = |D1 x D2| / |D1|^3 +//! @param[in] theD1 first derivative vector +//! @param[in] theD2 second derivative vector +//! @param[in] theTol linear tolerance for zero-vector detection +//! @return curvature result with validity and infinity flags +Standard_EXPORT CurvatureResult ComputeCurvature(const gp_Vec& theD1, + const gp_Vec& theD2, + double theTol); + +//! Compute normal direction from first and second derivative vectors. +//! Normal = D1 x (D2 x D1) (normalized), perpendicular to tangent pointing toward center. +//! @param[in] theD1 first derivative vector +//! @param[in] theD2 second derivative vector +//! @param[in] theTol linear tolerance for zero-vector detection +//! @return normal result with validity flag +Standard_EXPORT NormalResult ComputeNormal(const gp_Vec& theD1, const gp_Vec& theD2, double theTol); + +//! Compute centre of curvature from point and derivative vectors. +//! Centre = Point + Normal / Curvature +//! @param[in] thePnt point on the curve +//! @param[in] theD1 first derivative vector +//! @param[in] theD2 second derivative vector +//! @param[in] theTol linear tolerance for zero-vector detection +//! @return centre result with validity flag +Standard_EXPORT CentreResult ComputeCentreOfCurvature(const gp_Pnt& thePnt, + const gp_Vec& theD1, + const gp_Vec& theD2, + double theTol); + +// ============================================================================ +// Surface free functions +// ============================================================================ + +//! Compute surface normal from first partial derivatives. +//! Normal = D1U x D1V (normalized). +//! @param[in] theD1U first partial derivative in U direction +//! @param[in] theD1V first partial derivative in V direction +//! @param[in] theTol linear tolerance for zero-vector detection +//! @return surface normal result with validity flag +Standard_EXPORT SurfaceNormalResult ComputeSurfaceNormal(const gp_Vec& theD1U, + const gp_Vec& theD1V, + double theTol); + +//! Compute principal curvatures and directions from surface derivatives. +//! Uses first and second fundamental forms to compute principal curvatures. +//! @param[in] theD1U first partial derivative in U direction +//! @param[in] theD1V first partial derivative in V direction +//! @param[in] theD2U second partial derivative in U direction +//! @param[in] theD2V second partial derivative in V direction +//! @param[in] theDUV mixed partial derivative +//! @param[in] theTol linear tolerance for zero-vector detection +//! @return surface curvature result with principal curvatures and directions +Standard_EXPORT SurfaceCurvatureResult ComputeSurfaceCurvatures(const gp_Vec& theD1U, + const gp_Vec& theD1V, + const gp_Vec& theD2U, + const gp_Vec& theD2V, + const gp_Vec& theDUV, + double theTol); + +//! Compute mean and Gaussian curvatures from surface derivatives. +//! Mean curvature H = (EN - 2FM + GL) / (2(EG - F^2)) +//! Gaussian curvature K = (LN - M^2) / (EG - F^2) +//! @param[in] theD1U first partial derivative in U direction +//! @param[in] theD1V first partial derivative in V direction +//! @param[in] theD2U second partial derivative in U direction +//! @param[in] theD2V second partial derivative in V direction +//! @param[in] theDUV mixed partial derivative +//! @param[in] theTol linear tolerance for zero-vector detection +//! @return mean and Gaussian curvature result +Standard_EXPORT MeanGaussianResult ComputeMeanGaussian(const gp_Vec& theD1U, + const gp_Vec& theD1V, + const gp_Vec& theD2U, + const gp_Vec& theD2V, + const gp_Vec& theDUV, + double theTol); + +} // namespace GeomProp + +#endif // _GeomProp_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineCurve.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineCurve.cxx new file mode 100644 index 0000000000..9b98466e6b --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineCurve.cxx @@ -0,0 +1,441 @@ +// 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 +{ + +constexpr double THE_CURVATURE_DERIV_COEFF = 3.0; //!< Coefficient in d(KC)/dU formula +constexpr double THE_DIFF_STEP_DIVISOR = 100.0; //!< Divisor for numerical differentiation step +constexpr double THE_D2_MAGNITUDE_THRESHOLD = 1.0e-4; //!< Threshold for second derivative magnitude +constexpr double THE_EPSILON_SCALE = 1.0e-4; //!< Scale factor for epsilon relative to domain +constexpr int THE_EXTREMA_NB_SAMPLES = 100; //!< Number of samples for curvature extrema search +constexpr int THE_INFLECTION_NB_SAMPLES = 30; //!< Number of samples for inflection search +constexpr double THE_INFLECTION_TOLERANCE = 1.0e-6; //!< Tolerance for inflection point finding + +//! Function for finding curvature extrema: F = d(curvature)/dU = 0 +class FuncCurExt +{ +public: + FuncCurExt(const GeomAdaptor_Curve* theCurve, const double theTol) + : myCurve(theCurve), + myEpsX(theTol) + { + } + + bool Value(const double X, double& F) + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + myCurve->D3(X, aP, aV1, aV2, aV3); + + const gp_Vec aCPV1V2 = aV1.Crossed(aV2); + const double aCPMag = aCPV1V2.Magnitude(); + const gp_Vec aCPV1V3 = aV1.Crossed(aV3); + const double aV1V2 = aV1.Dot(aV2); + const double aV1V1 = aV1.SquareMagnitude(); + const double aNV1 = std::sqrt(aV1V1); + const double aV13 = aV1V1 * aNV1; + const double aV15 = aV13 * aV1V1; + + if (aV15 < gp::Resolution()) + { + return false; + } + + if (aCPMag < gp::Resolution()) + { + F = aCPV1V3.Magnitude() / aV13; + return true; + } + const double aDCrossDU = aCPV1V2.Dot(aCPV1V3) / aCPMag; + F = aDCrossDU / aV13 - THE_CURVATURE_DERIV_COEFF * aCPMag * aV1V2 / aV15; + return true; + } + + bool Values(const double X, double& F, double& D) + { + double aDx = myEpsX / THE_DIFF_STEP_DIVISOR; + if (X + aDx > myCurve->LastParameter()) + { + aDx = -aDx; + } + + Value(X, F); + double aF2; + Value(X + aDx, aF2); + D = (aF2 - F) / aDx; + return true; + } + + bool IsMinKC(const double X) const + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + + myCurve->D3(X, aP, aV1, aV2, aV3); + const double aV1V1 = aV1.SquareMagnitude(); + const double aNV1 = std::sqrt(aV1V1); + const double aV13 = aV1V1 * aNV1; + + if (aV13 < gp::Resolution()) + { + return false; + } + const double aKC = aV1.Crossed(aV2).Magnitude() / aV13; + + double aDx = myEpsX; + if (X + aDx > myCurve->LastParameter()) + { + aDx = -aDx; + } + + myCurve->D3(X + aDx, aP, aV1, aV2, aV3); + const double aV1V1n = aV1.SquareMagnitude(); + const double aNV1n = std::sqrt(aV1V1n); + const double aV13n = aV1V1n * aNV1n; + + if (aV13n < gp::Resolution()) + { + return false; + } + const double aKP = aV1.Crossed(aV2).Magnitude() / aV13n; + + return std::abs(aKC) > std::abs(aKP); + } + +private: + const GeomAdaptor_Curve* myCurve; + double myEpsX; +}; + +//! Function for finding inflection points: F = |V1 x V2| / (||V1|| * ||V2||) = 0 +class FuncCurNul +{ +public: + FuncCurNul(const GeomAdaptor_Curve* theCurve) + : myCurve(theCurve) + { + } + + bool Value(const double X, double& F) + { + double aD; + return Values(X, F, aD); + } + + bool Values(const double X, double& F, double& D) + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + myCurve->D3(X, aP, aV1, aV2, aV3); + + const gp_Vec aCPV1V2 = aV1.Crossed(aV2); + const double aCPMag = aCPV1V2.Magnitude(); + const gp_Vec aCPV1V3 = aV1.Crossed(aV3); + const double aV1V2 = aV1.Dot(aV2); + const double aV2V3 = aV2.Dot(aV3); + const double aNV1 = aV1.Magnitude(); + const double aNV2 = aV2.Magnitude(); + + F = 0.0; + D = 0.0; + + if (aNV2 < THE_D2_MAGNITUDE_THRESHOLD) + { + return true; + } + if (aNV1 * aNV2 < gp::Resolution()) + { + return false; + } + + F = aCPMag / (aNV1 * aNV2); + + if (aCPMag < gp::Resolution()) + { + D = aCPV1V3.Magnitude() / (aNV1 * aNV2); + } + else + { + const double aDCrossDU = aCPV1V2.Dot(aCPV1V3) / aCPMag; + D = (aDCrossDU - aCPMag * aV1V2 / (aNV1 * aNV1) - aCPMag * aV2V3 / (aNV2 * aNV2)) + / (aNV1 * aNV2); + } + return true; + } + +private: + const GeomAdaptor_Curve* myCurve; +}; + +//! Perform numeric curvature extrema finding on a curve interval. +void numericCurvatureExtrema(const GeomAdaptor_Curve* theCurve, + const double theUMin, + const double theUMax, + GeomProp::CurveAnalysis& theResult) +{ + const double aEpsH = THE_EPSILON_SCALE * (theUMax - theUMin); + + FuncCurExt aFunc(theCurve, aEpsH); + + MathRoot::MultipleConfig aConfig; + aConfig.NbSamples = THE_EXTREMA_NB_SAMPLES; + aConfig.XTolerance = aEpsH; + aConfig.FTolerance = aEpsH; + + MathRoot::MultipleResult aRoots = + MathRoot::FindAllRootsWithDerivative(aFunc, theUMin, theUMax, aConfig); + + if (aRoots.IsDone()) + { + for (int j = 0; j < aRoots.NbRoots(); ++j) + { + double aParam = aRoots[j]; + MathUtils::Config aBrentCfg; + aBrentCfg.XTolerance = Precision::PConfusion(); + aBrentCfg.FTolerance = Precision::PConfusion(); + auto aBrent = MathRoot::Brent(aFunc, aParam - aEpsH, aParam + aEpsH, aBrentCfg); + if (aBrent.IsDone() && aBrent.Root.has_value()) + { + aParam = *aBrent.Root; + } + const bool aIsMin = aFunc.IsMinKC(aParam); + const GeomProp::CIType aType = + aIsMin ? GeomProp::CIType::MinCurvature : GeomProp::CIType::MaxCurvature; + theResult.Points.Append({aParam, aType}); + } + } + else + { + theResult.IsDone = false; + } +} + +//! Perform numeric inflection finding on a curve interval. +void numericInflections(const GeomAdaptor_Curve* theCurve, + const double theUMin, + const double theUMax, + GeomProp::CurveAnalysis& theResult) +{ + FuncCurNul aFunc(theCurve); + + MathRoot::MultipleConfig aConfig; + aConfig.NbSamples = THE_INFLECTION_NB_SAMPLES; + aConfig.XTolerance = THE_INFLECTION_TOLERANCE; + aConfig.FTolerance = THE_INFLECTION_TOLERANCE; + + MathRoot::MultipleResult aRoots = MathRoot::FindAllRoots(aFunc, theUMin, theUMax, aConfig); + + if (aRoots.IsDone()) + { + for (int j = 0; j < aRoots.NbRoots(); ++j) + { + theResult.Points.Append({aRoots[j], GeomProp::CIType::Inflection}); + } + } + else + { + theResult.IsDone = false; + } +} + +//! Remove duplicate points that may appear at shared interval boundaries. +void removeDuplicatePoints(GeomProp::CurveAnalysis& theResult, const double theTol) +{ + const int aNbPts = theResult.Points.Size(); + if (aNbPts <= 1) + { + return; + } + + bool aHasDuplicates = false; + for (int i = 1; i < aNbPts && !aHasDuplicates; ++i) + { + for (int j = 0; j < i; ++j) + { + if (std::abs(theResult.Points[i].Parameter - theResult.Points[j].Parameter) < theTol) + { + aHasDuplicates = true; + break; + } + } + } + if (!aHasDuplicates) + { + return; + } + + NCollection_DynamicArray aFiltered; + aFiltered.Append(theResult.Points[0]); + for (int i = 1; i < aNbPts; ++i) + { + bool aIsDuplicate = false; + for (int j = static_cast(aFiltered.Size()) - 1; j >= 0; --j) + { + if (std::abs(theResult.Points[i].Parameter - aFiltered[j].Parameter) < theTol) + { + aIsDuplicate = true; + break; + } + } + if (!aIsDuplicate) + { + aFiltered.Append(theResult.Points[i]); + } + } + + theResult.Points = std::move(aFiltered); +} + +} // namespace + +//================================================================================================== + +GeomProp::TangentResult GeomProp_BSplineCurve::Tangent(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2, aD3; + myAdaptor->D3(theParam, aPnt, aD1, aD2, aD3); + return GeomProp::ComputeTangent(aD1, aD2, aD3, theTol); +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp_BSplineCurve::Curvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {0.0, false, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCurvature(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp_BSplineCurve::Normal(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeNormal(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp_BSplineCurve::CentreOfCurvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCentreOfCurvature(aPnt, aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_BSplineCurve::FindCurvatureExtrema() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + if (myAdaptor->Continuity() >= GeomAbs_C3) + { + numericCurvatureExtrema(myAdaptor, + myAdaptor->FirstParameter(), + myAdaptor->LastParameter(), + aResult); + } + else + { + // Subdivide into C3 intervals. + const int aNbInt = myAdaptor->NbIntervals(GeomAbs_C3); + NCollection_Array1 aParams(1, aNbInt + 1); + myAdaptor->Intervals(aParams, GeomAbs_C3); + for (int i = 1; i <= aNbInt; ++i) + { + numericCurvatureExtrema(myAdaptor, aParams(i), aParams(i + 1), aResult); + } + const double aEpsH = + THE_EPSILON_SCALE * (myAdaptor->LastParameter() - myAdaptor->FirstParameter()); + removeDuplicatePoints(aResult, aEpsH); + } + + return aResult; +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_BSplineCurve::FindInflections() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + if (myAdaptor->Continuity() >= GeomAbs_C3) + { + numericInflections(myAdaptor, myAdaptor->FirstParameter(), myAdaptor->LastParameter(), aResult); + } + else + { + const int aNbInt = myAdaptor->NbIntervals(GeomAbs_C3); + NCollection_Array1 aParams(1, aNbInt + 1); + myAdaptor->Intervals(aParams, GeomAbs_C3); + for (int i = 1; i <= aNbInt; ++i) + { + numericInflections(myAdaptor, aParams(i), aParams(i + 1), aResult); + } + removeDuplicatePoints(aResult, THE_INFLECTION_TOLERANCE); + } + + return aResult; +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineCurve.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineCurve.hxx new file mode 100644 index 0000000000..11056c2320 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineCurve.hxx @@ -0,0 +1,76 @@ +// 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 _GeomProp_BSplineCurve_HeaderFile +#define _GeomProp_BSplineCurve_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a 3D B-spline curve. +//! +//! Uses numeric root-finding for curvature extrema and inflection points. +//! For B-splines with continuity less than C3, the parameter range is subdivided +//! into C3 intervals for more robust root-finding. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_BSplineCurve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must wrap a B-spline curve, must not be null) + GeomProp_BSplineCurve(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_BSplineCurve(const GeomProp_BSplineCurve&) = delete; + GeomProp_BSplineCurve& operator=(const GeomProp_BSplineCurve&) = delete; + GeomProp_BSplineCurve(GeomProp_BSplineCurve&&) = delete; + GeomProp_BSplineCurve& operator=(GeomProp_BSplineCurve&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + Standard_EXPORT GeomProp::TangentResult Tangent(double theParam, double theTol) const; + + //! Compute curvature at given parameter. + Standard_EXPORT GeomProp::CurvatureResult Curvature(double theParam, double theTol) const; + + //! Compute normal at given parameter. + Standard_EXPORT GeomProp::NormalResult Normal(double theParam, double theTol) const; + + //! Compute centre of curvature at given parameter. + Standard_EXPORT GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const; + + //! Find curvature extrema using numeric root-finding. + //! For non-C3 B-splines, subdivides into C3 intervals. + Standard_EXPORT GeomProp::CurveAnalysis FindCurvatureExtrema() const; + + //! Find inflection points using numeric root-finding. + //! For non-C3 B-splines, subdivides into C3 intervals. + Standard_EXPORT GeomProp::CurveAnalysis FindInflections() const; + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_BSplineCurve_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineSurface.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineSurface.cxx new file mode 100644 index 0000000000..7a7c494197 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineSurface.cxx @@ -0,0 +1,62 @@ +// 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 + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_BSplineSurface::Normal(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_BSplineSurface::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_BSplineSurface::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineSurface.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineSurface.hxx new file mode 100644 index 0000000000..0fc1de7850 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_BSplineSurface.hxx @@ -0,0 +1,68 @@ +// 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 _GeomProp_BSplineSurface_HeaderFile +#define _GeomProp_BSplineSurface_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a B-spline surface. +//! +//! Uses numeric evaluation from adaptor derivatives. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_BSplineSurface +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_BSplineSurface(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_BSplineSurface(const GeomProp_BSplineSurface&) = delete; + GeomProp_BSplineSurface& operator=(const GeomProp_BSplineSurface&) = delete; + GeomProp_BSplineSurface(GeomProp_BSplineSurface&&) = delete; + GeomProp_BSplineSurface& operator=(GeomProp_BSplineSurface&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given parameter. + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given parameter. + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given parameter. + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_BSplineSurface_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierCurve.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierCurve.cxx new file mode 100644 index 0000000000..7fcc4f1d91 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierCurve.cxx @@ -0,0 +1,373 @@ +// 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 + +namespace +{ + +constexpr double THE_CURVATURE_DERIV_COEFF = 3.0; //!< Coefficient in d(KC)/dU formula +constexpr double THE_DIFF_STEP_DIVISOR = 100.0; //!< Divisor for numerical differentiation step +constexpr double THE_D2_MAGNITUDE_THRESHOLD = 1.0e-4; //!< Threshold for second derivative magnitude +constexpr double THE_EPSILON_SCALE = 1.0e-4; //!< Scale factor for epsilon relative to domain +constexpr int THE_EXTREMA_NB_SAMPLES = 100; //!< Number of samples for curvature extrema search +constexpr int THE_INFLECTION_NB_SAMPLES = 30; //!< Number of samples for inflection search +constexpr double THE_INFLECTION_TOLERANCE = 1.0e-6; //!< Tolerance for inflection point finding + +//! Function for finding curvature extrema: F = d(curvature)/dU = 0 +//! In 3D: KC = |V1 x V2| / ||V1||^3 +//! F = d KC / dU +class FuncCurExt +{ +public: + FuncCurExt(const GeomAdaptor_Curve* theCurve, const double theTol) + : myCurve(theCurve), + myEpsX(theTol) + { + } + + bool Value(const double X, double& F) + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + myCurve->D3(X, aP, aV1, aV2, aV3); + + // In 3D: |V1 x V2| = magnitude of cross product vector + const gp_Vec aCPV1V2 = aV1.Crossed(aV2); + const double aCPMag = aCPV1V2.Magnitude(); + const gp_Vec aCPV1V3 = aV1.Crossed(aV3); + const double aV1V2 = aV1.Dot(aV2); + const double aV1V1 = aV1.SquareMagnitude(); + const double aNV1 = std::sqrt(aV1V1); + const double aV13 = aV1V1 * aNV1; + const double aV15 = aV13 * aV1V1; + + if (aV15 < gp::Resolution()) + { + return false; + } + + // d(KC)/dU for 3D curves. + // KC = |V1 x V2| / |V1|^3 + // dKC/dU = d|V1xV2|/dU / |V1|^3 - 3 * |V1xV2| * (V1.V2) / |V1|^5 + // d|V1xV2|/dU = (V1xV2).(V1xV3) / |V1xV2| + if (aCPMag < gp::Resolution()) + { + // Cross product is zero - inflection or straight region. + // Use simplified formula. + F = aCPV1V3.Magnitude() / aV13; + return true; + } + const double aDCrossDU = aCPV1V2.Dot(aCPV1V3) / aCPMag; + F = aDCrossDU / aV13 - THE_CURVATURE_DERIV_COEFF * aCPMag * aV1V2 / aV15; + return true; + } + + bool Values(const double X, double& F, double& D) + { + double aDx = myEpsX / THE_DIFF_STEP_DIVISOR; + if (X + aDx > myCurve->LastParameter()) + { + aDx = -aDx; + } + + Value(X, F); + double aF2; + Value(X + aDx, aF2); + D = (aF2 - F) / aDx; + return true; + } + + //! Test if parameter corresponds to a minimum of the radius of curvature + //! (maximum of |curvature|) by comparison with a neighboring point. + bool IsMinKC(const double X) const + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + + myCurve->D3(X, aP, aV1, aV2, aV3); + const double aCPMag = aV1.Crossed(aV2).Magnitude(); + const double aV1V1 = aV1.SquareMagnitude(); + const double aNV1 = std::sqrt(aV1V1); + const double aV13 = aV1V1 * aNV1; + + if (aV13 < gp::Resolution()) + { + return false; + } + const double aKC = aCPMag / aV13; + + double aDx = myEpsX; + if (X + aDx > myCurve->LastParameter()) + { + aDx = -aDx; + } + + myCurve->D3(X + aDx, aP, aV1, aV2, aV3); + const double aCPMagN = aV1.Crossed(aV2).Magnitude(); + const double aV1V1n = aV1.SquareMagnitude(); + const double aNV1n = std::sqrt(aV1V1n); + const double aV13n = aV1V1n * aNV1n; + + if (aV13n < gp::Resolution()) + { + return false; + } + const double aKP = aCPMagN / aV13n; + + return std::abs(aKC) > std::abs(aKP); + } + +private: + const GeomAdaptor_Curve* myCurve; + double myEpsX; +}; + +//! Function for finding inflection points: F = |V1 x V2| / (||V1|| * ||V2||) = 0 +class FuncCurNul +{ +public: + FuncCurNul(const GeomAdaptor_Curve* theCurve) + : myCurve(theCurve) + { + } + + bool Value(const double X, double& F) + { + double aD; + return Values(X, F, aD); + } + + bool Values(const double X, double& F, double& D) + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + myCurve->D3(X, aP, aV1, aV2, aV3); + + const gp_Vec aCPV1V2 = aV1.Crossed(aV2); + const double aCPMag = aCPV1V2.Magnitude(); + const gp_Vec aCPV1V3 = aV1.Crossed(aV3); + const double aV1V2 = aV1.Dot(aV2); + const double aV2V3 = aV2.Dot(aV3); + const double aNV1 = aV1.Magnitude(); + const double aNV2 = aV2.Magnitude(); + + F = 0.0; + D = 0.0; + + if (aNV2 < THE_D2_MAGNITUDE_THRESHOLD) + { + return true; + } + if (aNV1 * aNV2 < gp::Resolution()) + { + return false; + } + + F = aCPMag / (aNV1 * aNV2); + + // Derivative of |V1xV2|/(|V1|*|V2|) w.r.t. parameter + if (aCPMag < gp::Resolution()) + { + D = aCPV1V3.Magnitude() / (aNV1 * aNV2); + } + else + { + const double aDCrossDU = aCPV1V2.Dot(aCPV1V3) / aCPMag; + D = (aDCrossDU - aCPMag * aV1V2 / (aNV1 * aNV1) - aCPMag * aV2V3 / (aNV2 * aNV2)) + / (aNV1 * aNV2); + } + return true; + } + +private: + const GeomAdaptor_Curve* myCurve; +}; + +//! Perform numeric curvature extrema finding on a curve interval. +void numericCurvatureExtrema(const GeomAdaptor_Curve* theCurve, + const double theUMin, + const double theUMax, + GeomProp::CurveAnalysis& theResult) +{ + const double aEpsH = THE_EPSILON_SCALE * (theUMax - theUMin); + + FuncCurExt aFunc(theCurve, aEpsH); + + MathRoot::MultipleConfig aConfig; + aConfig.NbSamples = THE_EXTREMA_NB_SAMPLES; + aConfig.XTolerance = aEpsH; + aConfig.FTolerance = aEpsH; + + MathRoot::MultipleResult aRoots = + MathRoot::FindAllRootsWithDerivative(aFunc, theUMin, theUMax, aConfig); + + if (aRoots.IsDone()) + { + for (int j = 0; j < aRoots.NbRoots(); ++j) + { + double aParam = aRoots[j]; + // Refine the solution. + MathUtils::Config aBrentCfg; + aBrentCfg.XTolerance = Precision::PConfusion(); + aBrentCfg.FTolerance = Precision::PConfusion(); + auto aBrent = MathRoot::Brent(aFunc, aParam - aEpsH, aParam + aEpsH, aBrentCfg); + if (aBrent.IsDone() && aBrent.Root.has_value()) + { + aParam = *aBrent.Root; + } + const bool aIsMin = aFunc.IsMinKC(aParam); + const GeomProp::CIType aType = + aIsMin ? GeomProp::CIType::MinCurvature : GeomProp::CIType::MaxCurvature; + theResult.Points.Append({aParam, aType}); + } + } + else + { + theResult.IsDone = false; + } +} + +//! Perform numeric inflection finding on a curve interval. +void numericInflections(const GeomAdaptor_Curve* theCurve, + const double theUMin, + const double theUMax, + GeomProp::CurveAnalysis& theResult) +{ + FuncCurNul aFunc(theCurve); + + MathRoot::MultipleConfig aConfig; + aConfig.NbSamples = THE_INFLECTION_NB_SAMPLES; + aConfig.XTolerance = THE_INFLECTION_TOLERANCE; + aConfig.FTolerance = THE_INFLECTION_TOLERANCE; + + MathRoot::MultipleResult aRoots = MathRoot::FindAllRoots(aFunc, theUMin, theUMax, aConfig); + + if (aRoots.IsDone()) + { + for (int j = 0; j < aRoots.NbRoots(); ++j) + { + theResult.Points.Append({aRoots[j], GeomProp::CIType::Inflection}); + } + } + else + { + theResult.IsDone = false; + } +} + +} // namespace + +//================================================================================================== + +GeomProp::TangentResult GeomProp_BezierCurve::Tangent(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2, aD3; + myAdaptor->D3(theParam, aPnt, aD1, aD2, aD3); + return GeomProp::ComputeTangent(aD1, aD2, aD3, theTol); +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp_BezierCurve::Curvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {0.0, false, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCurvature(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp_BezierCurve::Normal(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeNormal(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp_BezierCurve::CentreOfCurvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCentreOfCurvature(aPnt, aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_BezierCurve::FindCurvatureExtrema() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + numericCurvatureExtrema(myAdaptor, + myAdaptor->FirstParameter(), + myAdaptor->LastParameter(), + aResult); + return aResult; +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_BezierCurve::FindInflections() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + numericInflections(myAdaptor, myAdaptor->FirstParameter(), myAdaptor->LastParameter(), aResult); + return aResult; +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierCurve.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierCurve.hxx new file mode 100644 index 0000000000..913f6a7f98 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierCurve.hxx @@ -0,0 +1,72 @@ +// 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 _GeomProp_BezierCurve_HeaderFile +#define _GeomProp_BezierCurve_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a 3D Bezier curve. +//! +//! Uses numeric root-finding for curvature extrema and inflection points. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_BezierCurve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must wrap a Bezier curve, must not be null) + GeomProp_BezierCurve(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_BezierCurve(const GeomProp_BezierCurve&) = delete; + GeomProp_BezierCurve& operator=(const GeomProp_BezierCurve&) = delete; + GeomProp_BezierCurve(GeomProp_BezierCurve&&) = delete; + GeomProp_BezierCurve& operator=(GeomProp_BezierCurve&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + Standard_EXPORT GeomProp::TangentResult Tangent(double theParam, double theTol) const; + + //! Compute curvature at given parameter. + Standard_EXPORT GeomProp::CurvatureResult Curvature(double theParam, double theTol) const; + + //! Compute normal at given parameter. + Standard_EXPORT GeomProp::NormalResult Normal(double theParam, double theTol) const; + + //! Compute centre of curvature at given parameter. + Standard_EXPORT GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const; + + //! Find curvature extrema using numeric root-finding. + Standard_EXPORT GeomProp::CurveAnalysis FindCurvatureExtrema() const; + + //! Find inflection points using numeric root-finding. + Standard_EXPORT GeomProp::CurveAnalysis FindInflections() const; + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_BezierCurve_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierSurface.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierSurface.cxx new file mode 100644 index 0000000000..86e11c22a7 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierSurface.cxx @@ -0,0 +1,62 @@ +// 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 + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_BezierSurface::Normal(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_BezierSurface::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_BezierSurface::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierSurface.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierSurface.hxx new file mode 100644 index 0000000000..cb3149afc0 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_BezierSurface.hxx @@ -0,0 +1,68 @@ +// 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 _GeomProp_BezierSurface_HeaderFile +#define _GeomProp_BezierSurface_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a Bezier surface. +//! +//! Uses numeric evaluation from adaptor derivatives. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_BezierSurface +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_BezierSurface(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_BezierSurface(const GeomProp_BezierSurface&) = delete; + GeomProp_BezierSurface& operator=(const GeomProp_BezierSurface&) = delete; + GeomProp_BezierSurface(GeomProp_BezierSurface&&) = delete; + GeomProp_BezierSurface& operator=(GeomProp_BezierSurface&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given parameter. + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given parameter. + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given parameter. + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_BezierSurface_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Circle.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Circle.hxx new file mode 100644 index 0000000000..4bce3192bc --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Circle.hxx @@ -0,0 +1,116 @@ +// 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 _GeomProp_Circle_HeaderFile +#define _GeomProp_Circle_HeaderFile + +#include +#include +#include + +//! @brief Local differential properties for a 3D circle. +//! +//! A circle has constant curvature = 1/R, well-defined tangent and normal +//! at every point, and no curvature extrema or inflection points. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_Circle +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must wrap a circle, must not be null) + GeomProp_Circle(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Circle(const GeomProp_Circle&) = delete; + GeomProp_Circle& operator=(const GeomProp_Circle&) = delete; + GeomProp_Circle(GeomProp_Circle&&) = delete; + GeomProp_Circle& operator=(GeomProp_Circle&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + //! @param[in] theParam curve parameter + //! @param[in] theTol tolerance (unused for circle) + //! @return tangent result (always defined) + GeomProp::TangentResult Tangent(double theParam, double theTol) const + { + (void)theTol; + gp_Pnt aPnt; + gp_Vec aD1; + myAdaptor->D1(theParam, aPnt, aD1); + return {gp_Dir(aD1), true}; + } + + //! Compute curvature at given parameter. + //! For a circle, curvature = 1/R (constant). + //! @param[in] theParam curve parameter (unused) + //! @param[in] theTol tolerance (unused) + //! @return curvature result (always defined, constant) + GeomProp::CurvatureResult Curvature(double theParam, double theTol) const + { + (void)theParam; + (void)theTol; + return {1.0 / myAdaptor->Circle().Radius(), true, false}; + } + + //! Compute normal at given parameter. + //! @param[in] theParam curve parameter + //! @param[in] theTol tolerance (unused) + //! @return normal result (always defined) + GeomProp::NormalResult Normal(double theParam, double theTol) const + { + (void)theTol; + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + // Normal = D2 * (D1.D1) - D1 * (D1.D2) + const gp_Vec aNorm = aD2 * aD1.Dot(aD1) - aD1 * aD1.Dot(aD2); + return {gp_Dir(aNorm), true}; + } + + //! Compute centre of curvature at given parameter. + //! For a circle, the centre of curvature is the geometric centre. + //! @param[in] theParam curve parameter (unused) + //! @param[in] theTol tolerance (unused) + //! @return centre result (always the circle centre) + GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const + { + (void)theParam; + (void)theTol; + return {myAdaptor->Circle().Location(), true}; + } + + //! Find curvature extrema on the circle. + //! A circle has constant curvature, so no extrema. + //! @return empty analysis (always done) + GeomProp::CurveAnalysis FindCurvatureExtrema() const { return {{}, true}; } + + //! Find inflection points on the circle. + //! A circle has no inflection points. + //! @return empty analysis (always done) + GeomProp::CurveAnalysis FindInflections() const { return {{}, true}; } + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_Circle_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Cone.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Cone.cxx new file mode 100644 index 0000000000..5a63c7c7a6 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Cone.cxx @@ -0,0 +1,62 @@ +// 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 + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_Cone::Normal(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_Cone::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_Cone::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Cone.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Cone.hxx new file mode 100644 index 0000000000..fcbe10aed9 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Cone.hxx @@ -0,0 +1,74 @@ +// 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 _GeomProp_Cone_HeaderFile +#define _GeomProp_Cone_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a conical surface. +//! +//! Uses analytical formulas where possible; the curvature +//! varies along the meridian (V direction). +//! Min principal curvature = 0 (along the ruling direction). +//! Max principal curvature = cos(alpha) / R(V), where alpha is the half-angle +//! and R(V) is the radius at parameter V. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_Cone +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_Cone(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Cone(const GeomProp_Cone&) = delete; + GeomProp_Cone& operator=(const GeomProp_Cone&) = delete; + GeomProp_Cone(GeomProp_Cone&&) = delete; + GeomProp_Cone& operator=(GeomProp_Cone&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given parameter. + //! TODO: At the apex (V = -R/sin(alpha)), D1U degenerates and Normal returns IsDefined=false. + //! Could use analytical normal for this special case. + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given parameter. + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given parameter. + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_Cone_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Curve.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Curve.cxx new file mode 100644 index 0000000000..34b6339ef6 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Curve.cxx @@ -0,0 +1,213 @@ +// 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 + +//================================================================================================== + +void GeomProp_Curve::Initialize(const Adaptor3d_Curve& theCurve) +{ + if (theCurve.IsKind(STANDARD_TYPE(GeomAdaptor_Curve))) + { + const auto& aGeomAdaptor = static_cast(theCurve); + myAdaptor = new GeomAdaptor_Curve(aGeomAdaptor); + initFromAdaptor(); + return; + } + + // For non-GeomAdaptor, set uninitialized. + myAdaptor.Nullify(); + myCurveType = theCurve.GetType(); + myEvaluator.emplace(); +} + +//================================================================================================== + +void GeomProp_Curve::Initialize(const occ::handle& theCurve) +{ + if (theCurve.IsNull()) + { + myAdaptor.Nullify(); + myEvaluator.emplace(); + myCurveType = GeomAbs_OtherCurve; + return; + } + + myAdaptor = new GeomAdaptor_Curve(theCurve); + initFromAdaptor(); +} + +//================================================================================================== + +void GeomProp_Curve::initFromAdaptor() +{ + myCurveType = myAdaptor->GetType(); + const GeomAdaptor_Curve* aPtr = myAdaptor.get(); + + switch (myCurveType) + { + case GeomAbs_Line: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_Circle: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_Ellipse: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_Hyperbola: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_Parabola: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_BezierCurve: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_BSplineCurve: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_OffsetCurve: + myEvaluator.emplace(aPtr); + break; + default: + myEvaluator.emplace(aPtr); + break; + } +} + +//================================================================================================== + +bool GeomProp_Curve::IsInitialized() const +{ + return !std::holds_alternative(myEvaluator); +} + +//================================================================================================== + +GeomProp::TangentResult GeomProp_Curve::Tangent(const double theParam, const double theTol) const +{ + return std::visit( + [theParam, theTol](const auto& theEval) -> GeomProp::TangentResult { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {{}, false}; + } + else + { + return theEval.Tangent(theParam, theTol); + } + }, + myEvaluator); +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp_Curve::Curvature(const double theParam, + const double theTol) const +{ + return std::visit( + [theParam, theTol](const auto& theEval) -> GeomProp::CurvatureResult { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {0.0, false, false}; + } + else + { + return theEval.Curvature(theParam, theTol); + } + }, + myEvaluator); +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp_Curve::Normal(const double theParam, const double theTol) const +{ + return std::visit( + [theParam, theTol](const auto& theEval) -> GeomProp::NormalResult { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {{}, false}; + } + else + { + return theEval.Normal(theParam, theTol); + } + }, + myEvaluator); +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp_Curve::CentreOfCurvature(const double theParam, + const double theTol) const +{ + return std::visit( + [theParam, theTol](const auto& theEval) -> GeomProp::CentreResult { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {{}, false}; + } + else + { + return theEval.CentreOfCurvature(theParam, theTol); + } + }, + myEvaluator); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_Curve::FindCurvatureExtrema() const +{ + return std::visit( + [](const auto& theEval) -> GeomProp::CurveAnalysis { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {{}, false}; + } + else + { + return theEval.FindCurvatureExtrema(); + } + }, + myEvaluator); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_Curve::FindInflections() const +{ + return std::visit( + [](const auto& theEval) -> GeomProp::CurveAnalysis { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {{}, false}; + } + else + { + return theEval.FindInflections(); + } + }, + myEvaluator); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Curve.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Curve.hxx new file mode 100644 index 0000000000..84d14c4d96 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Curve.hxx @@ -0,0 +1,152 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _GeomProp_Curve_HeaderFile +#define _GeomProp_Curve_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//! @brief Unified local differential property evaluator for any 3D curve. +//! +//! Uses std::variant for compile-time type safety and zero heap allocation +//! for the evaluator itself. Automatically detects curve type from +//! Adaptor3d_Curve or Geom_Curve and dispatches to the appropriate +//! specialized evaluator. +//! +//! Supported curve types with optimized evaluation: +//! - Line: Trivial (zero curvature, constant tangent) +//! - Circle: Constant curvature 1/R +//! - Ellipse: Analytical curvature extrema at vertices +//! - Hyperbola: Analytical curvature extremum at vertex +//! - Parabola: Analytical curvature extremum at vertex +//! - BezierCurve: Numeric curvature extrema/inflection finding +//! - BSplineCurve: Numeric with C3 interval subdivision +//! - OffsetCurve: Numeric approach +//! - Other: Fallback using Geom_Curve virtual D1/D2/D3 +//! +//! Usage: +//! @code +//! GeomProp_Curve aProp; +//! aProp.Initialize(myGeomCurve); +//! GeomProp::CurvatureResult aCurv = aProp.Curvature(0.5, Precision::Confusion()); +//! if (aCurv.IsDefined) +//! { +//! double aValue = aCurv.Value; +//! } +//! @endcode +class GeomProp_Curve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Variant type holding all possible 3D curve property evaluators. + using EvaluatorVariant = std::variant; + + //! Default constructor - uninitialized state. + GeomProp_Curve() + : myEvaluator(std::monostate{}), + myCurveType(GeomAbs_OtherCurve) + { + } + + //! Non-copyable and non-movable. + GeomProp_Curve(const GeomProp_Curve&) = delete; + GeomProp_Curve& operator=(const GeomProp_Curve&) = delete; + GeomProp_Curve(GeomProp_Curve&&) = delete; + GeomProp_Curve& operator=(GeomProp_Curve&&) = delete; + + //! Initialize from 3D adaptor reference (auto-detects curve type). + //! For GeomAdaptor_Curve, extracts underlying Geom_Curve for optimized evaluation. + //! @param[in] theCurve 3D curve adaptor reference + Standard_EXPORT void Initialize(const Adaptor3d_Curve& theCurve); + + //! Initialize from geometry handle (auto-detects curve type). + //! @param[in] theCurve 3D geometry to evaluate + Standard_EXPORT void Initialize(const occ::handle& theCurve); + + //! Returns true if properly initialized. + Standard_EXPORT bool IsInitialized() const; + + //! Returns the detected curve type. + GeomAbs_CurveType GetType() const { return myCurveType; } + + //! Compute tangent at given parameter. + //! @param[in] theParam curve parameter + //! @param[in] theTol linear tolerance + //! @return tangent result with validity flag + Standard_EXPORT GeomProp::TangentResult Tangent(double theParam, double theTol) const; + + //! Compute curvature at given parameter. + //! @param[in] theParam curve parameter + //! @param[in] theTol linear tolerance + //! @return curvature result with validity and infinity flags + Standard_EXPORT GeomProp::CurvatureResult Curvature(double theParam, double theTol) const; + + //! Compute normal at given parameter. + //! @param[in] theParam curve parameter + //! @param[in] theTol linear tolerance + //! @return normal result with validity flag + Standard_EXPORT GeomProp::NormalResult Normal(double theParam, double theTol) const; + + //! Compute centre of curvature at given parameter. + //! @param[in] theParam curve parameter + //! @param[in] theTol linear tolerance + //! @return centre result with validity flag + Standard_EXPORT GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const; + + //! Find curvature extrema on the curve. + //! @return analysis result with special points sorted by parameter + Standard_EXPORT GeomProp::CurveAnalysis FindCurvatureExtrema() const; + + //! Find inflection points on the curve. + //! @return analysis result with inflection points sorted by parameter + Standard_EXPORT GeomProp::CurveAnalysis FindInflections() const; + +private: + //! Initialize from stored adaptor (dispatches to per-geometry evaluator). + //! Must be called after myAdaptor is set. Per-geometry evaluators receive + //! a non-owning pointer to myAdaptor; their lifetime is managed by the variant. + Standard_EXPORT void initFromAdaptor(); + + occ::handle myAdaptor; //!< Owns the adaptor (ensures lifetime). + EvaluatorVariant myEvaluator; //!< Per-geometry evaluator (non-owning pointer to myAdaptor). + GeomAbs_CurveType myCurveType; +}; + +#endif // _GeomProp_Curve_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Cylinder.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Cylinder.hxx new file mode 100644 index 0000000000..7c29015b8e --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Cylinder.hxx @@ -0,0 +1,96 @@ +// 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 _GeomProp_Cylinder_HeaderFile +#define _GeomProp_Cylinder_HeaderFile + +#include +#include +#include + +//! @brief Local differential properties for a cylindrical surface. +//! +//! Analytical implementation with constant principal curvatures: +//! - Min curvature = 0 (along the axis direction) +//! - Max curvature = 1/R (along the circular cross-section) +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_Cylinder +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_Cylinder(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Cylinder(const GeomProp_Cylinder&) = delete; + GeomProp_Cylinder& operator=(const GeomProp_Cylinder&) = delete; + GeomProp_Cylinder(GeomProp_Cylinder&&) = delete; + GeomProp_Cylinder& operator=(GeomProp_Cylinder&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given (U, V) parameter. + //! For a cylinder, the normal is radially outward from the axis. + //! TODO: Could use analytical normal (radial direction from axis) for degenerate D1 cases, + //! though in practice cylinder D1 never degenerates. + GeomProp::SurfaceNormalResult Normal(double theU, double theV, double theTol) const + { + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); + } + + //! Compute principal curvatures using fundamental forms for correct sign convention. + GeomProp::SurfaceCurvatureResult Curvatures(double theU, double theV, double theTol) const + { + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); + } + + //! Compute mean and Gaussian curvatures using fundamental forms for correct sign convention. + GeomProp::MeanGaussianResult MeanGaussian(double theU, double theV, double theTol) const + { + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); + } + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_Cylinder_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Ellipse.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Ellipse.cxx new file mode 100644 index 0000000000..14c9a13df2 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Ellipse.cxx @@ -0,0 +1,120 @@ +// 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 + +namespace +{ +constexpr int THE_ELLIPSE_NB_EXTREMA = 4; //!< Number of curvature extrema on full ellipse +constexpr double THE_ELLIPSE_PERIOD = 2.0 * M_PI; //!< One full period of ellipse parameter +} // namespace + +//================================================================================================== + +GeomProp::TangentResult GeomProp_Ellipse::Tangent(const double theParam, const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2, aD3; + myAdaptor->D3(theParam, aPnt, aD1, aD2, aD3); + return GeomProp::ComputeTangent(aD1, aD2, aD3, theTol); +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp_Ellipse::Curvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {0.0, false, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCurvature(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp_Ellipse::Normal(const double theParam, const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeNormal(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp_Ellipse::CentreOfCurvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCentreOfCurvature(aPnt, aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_Ellipse::FindCurvatureExtrema() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + const double aUFirst = myAdaptor->FirstParameter(); + const double aULast = myAdaptor->LastParameter(); + const double aUFPlus2PI = aUFirst + THE_ELLIPSE_PERIOD; + + // Ellipse curvature extrema at 0, PI/2, PI, 3*PI/2 + // At 0 and PI (major axis endpoints): min radius of curvature -> max |curvature| -> MinCurvature + // At PI/2 and 3*PI/2 (minor axis endpoints): max radius of curvature -> min |curvature| -> + // MaxCurvature + const double aCandidates[] = {0.0, M_PI / 2.0, M_PI, 3.0 * M_PI / 2.0}; + const bool aIsMin[] = {true, false, true, false}; + + for (int i = 0; i < THE_ELLIPSE_NB_EXTREMA; ++i) + { + const double aU = ElCLib::InPeriod(aCandidates[i], aUFirst, aUFPlus2PI); + if (aU >= aUFirst && aU <= aULast) + { + const GeomProp::CIType aType = + aIsMin[i] ? GeomProp::CIType::MinCurvature : GeomProp::CIType::MaxCurvature; + aResult.Points.Append({aU, aType}); + } + } + + return aResult; +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Ellipse.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Ellipse.hxx new file mode 100644 index 0000000000..dbb5508c71 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Ellipse.hxx @@ -0,0 +1,76 @@ +// 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 _GeomProp_Ellipse_HeaderFile +#define _GeomProp_Ellipse_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a 3D ellipse. +//! +//! An ellipse has analytically known curvature extrema at the four vertices: +//! - Parameter 0 and PI: endpoints of major axis (min radius of curvature) +//! - Parameter PI/2 and 3*PI/2: endpoints of minor axis (max radius of curvature) +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_Ellipse +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must wrap an ellipse, must not be null) + GeomProp_Ellipse(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Ellipse(const GeomProp_Ellipse&) = delete; + GeomProp_Ellipse& operator=(const GeomProp_Ellipse&) = delete; + GeomProp_Ellipse(GeomProp_Ellipse&&) = delete; + GeomProp_Ellipse& operator=(GeomProp_Ellipse&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + Standard_EXPORT GeomProp::TangentResult Tangent(double theParam, double theTol) const; + + //! Compute curvature at given parameter. + Standard_EXPORT GeomProp::CurvatureResult Curvature(double theParam, double theTol) const; + + //! Compute normal at given parameter. + Standard_EXPORT GeomProp::NormalResult Normal(double theParam, double theTol) const; + + //! Compute centre of curvature at given parameter. + Standard_EXPORT GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const; + + //! Find curvature extrema on the ellipse. + //! Extrema occur analytically at 0, PI/2, PI, and 3*PI/2, filtered to [FirstParam, LastParam]. + Standard_EXPORT GeomProp::CurveAnalysis FindCurvatureExtrema() const; + + //! Find inflection points on the ellipse. + //! An ellipse has no inflection points. + GeomProp::CurveAnalysis FindInflections() const { return {{}, true}; } + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_Ellipse_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Hyperbola.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Hyperbola.cxx new file mode 100644 index 0000000000..290a852c2c --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Hyperbola.cxx @@ -0,0 +1,98 @@ +// 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 + +//================================================================================================== + +GeomProp::TangentResult GeomProp_Hyperbola::Tangent(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2, aD3; + myAdaptor->D3(theParam, aPnt, aD1, aD2, aD3); + return GeomProp::ComputeTangent(aD1, aD2, aD3, theTol); +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp_Hyperbola::Curvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {0.0, false, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCurvature(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp_Hyperbola::Normal(const double theParam, const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeNormal(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp_Hyperbola::CentreOfCurvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCentreOfCurvature(aPnt, aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_Hyperbola::FindCurvatureExtrema() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + const double aUFirst = myAdaptor->FirstParameter(); + const double aULast = myAdaptor->LastParameter(); + + // Hyperbola has maximum |curvature| at parameter 0 (vertex). + if (aUFirst <= 0.0 && aULast >= 0.0) + { + aResult.Points.Append({0.0, GeomProp::CIType::MinCurvature}); + } + + return aResult; +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Hyperbola.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Hyperbola.hxx new file mode 100644 index 0000000000..87d8c7af10 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Hyperbola.hxx @@ -0,0 +1,75 @@ +// 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 _GeomProp_Hyperbola_HeaderFile +#define _GeomProp_Hyperbola_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a 3D hyperbola. +//! +//! A hyperbola has a single curvature extremum (maximum |curvature|) at parameter 0 +//! (the vertex). No inflection points exist. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_Hyperbola +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must wrap a hyperbola, must not be null) + GeomProp_Hyperbola(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Hyperbola(const GeomProp_Hyperbola&) = delete; + GeomProp_Hyperbola& operator=(const GeomProp_Hyperbola&) = delete; + GeomProp_Hyperbola(GeomProp_Hyperbola&&) = delete; + GeomProp_Hyperbola& operator=(GeomProp_Hyperbola&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + Standard_EXPORT GeomProp::TangentResult Tangent(double theParam, double theTol) const; + + //! Compute curvature at given parameter. + Standard_EXPORT GeomProp::CurvatureResult Curvature(double theParam, double theTol) const; + + //! Compute normal at given parameter. + Standard_EXPORT GeomProp::NormalResult Normal(double theParam, double theTol) const; + + //! Compute centre of curvature at given parameter. + Standard_EXPORT GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const; + + //! Find curvature extrema on the hyperbola. + //! Single extremum at parameter 0 (the vertex), if within parameter range. + Standard_EXPORT GeomProp::CurveAnalysis FindCurvatureExtrema() const; + + //! Find inflection points on the hyperbola. + //! A hyperbola has no inflection points. + GeomProp::CurveAnalysis FindInflections() const { return {{}, true}; } + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_Hyperbola_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Line.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Line.hxx new file mode 100644 index 0000000000..e095c03f0b --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Line.hxx @@ -0,0 +1,112 @@ +// 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 _GeomProp_Line_HeaderFile +#define _GeomProp_Line_HeaderFile + +#include +#include +#include + +//! @brief Local differential properties for a 3D line. +//! +//! A line has constant tangent, zero curvature, undefined normal and centre. +//! No curvature extrema or inflection points exist. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_Line +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must wrap a line, must not be null) + GeomProp_Line(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Line(const GeomProp_Line&) = delete; + GeomProp_Line& operator=(const GeomProp_Line&) = delete; + GeomProp_Line(GeomProp_Line&&) = delete; + GeomProp_Line& operator=(GeomProp_Line&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + //! For a line, the tangent is always the line direction. + //! @param[in] theParam curve parameter (unused) + //! @param[in] theTol tolerance (unused) + //! @return tangent result (always defined) + GeomProp::TangentResult Tangent(double theParam, double theTol) const + { + (void)theParam; + (void)theTol; + return {myAdaptor->Line().Direction(), true}; + } + + //! Compute curvature at given parameter. + //! For a line, curvature is always zero. + //! @param[in] theParam curve parameter (unused) + //! @param[in] theTol tolerance (unused) + //! @return curvature result (always zero) + GeomProp::CurvatureResult Curvature(double theParam, double theTol) const + { + (void)theParam; + (void)theTol; + return {0.0, true, false}; + } + + //! Compute normal at given parameter. + //! For a line, the normal is undefined (zero curvature). + //! @param[in] theParam curve parameter (unused) + //! @param[in] theTol tolerance (unused) + //! @return normal result (always undefined) + GeomProp::NormalResult Normal(double theParam, double theTol) const + { + (void)theParam; + (void)theTol; + return {{}, false}; + } + + //! Compute centre of curvature at given parameter. + //! For a line, the centre is undefined (zero curvature). + //! @param[in] theParam curve parameter (unused) + //! @param[in] theTol tolerance (unused) + //! @return centre result (always undefined) + GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const + { + (void)theParam; + (void)theTol; + return {{}, false}; + } + + //! Find curvature extrema on the line. + //! A line has no curvature extrema. + //! @return empty analysis (always done) + GeomProp::CurveAnalysis FindCurvatureExtrema() const { return {{}, true}; } + + //! Find inflection points on the line. + //! A line has no inflection points. + //! @return empty analysis (always done) + GeomProp::CurveAnalysis FindInflections() const { return {{}, true}; } + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_Line_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetCurve.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetCurve.cxx new file mode 100644 index 0000000000..851094c6f3 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetCurve.cxx @@ -0,0 +1,340 @@ +// 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 + +namespace +{ + +constexpr double THE_CURVATURE_DERIV_COEFF = 3.0; +constexpr double THE_DIFF_STEP_DIVISOR = 100.0; +constexpr double THE_D2_MAGNITUDE_THRESHOLD = 1.0e-4; +constexpr double THE_EPSILON_SCALE = 1.0e-4; +constexpr int THE_EXTREMA_NB_SAMPLES = 100; +constexpr int THE_INFLECTION_NB_SAMPLES = 30; +constexpr double THE_INFLECTION_TOLERANCE = 1.0e-6; + +//! Function for finding curvature extrema on offset curves. +class FuncCurExt +{ +public: + FuncCurExt(const GeomAdaptor_Curve* theCurve, const double theTol) + : myCurve(theCurve), + myEpsX(theTol) + { + } + + bool Value(const double X, double& F) + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + myCurve->D3(X, aP, aV1, aV2, aV3); + + const gp_Vec aCPV1V2 = aV1.Crossed(aV2); + const double aCPMag = aCPV1V2.Magnitude(); + const gp_Vec aCPV1V3 = aV1.Crossed(aV3); + const double aV1V2 = aV1.Dot(aV2); + const double aV1V1 = aV1.SquareMagnitude(); + const double aNV1 = std::sqrt(aV1V1); + const double aV13 = aV1V1 * aNV1; + const double aV15 = aV13 * aV1V1; + + if (aV15 < gp::Resolution()) + { + return false; + } + + if (aCPMag < gp::Resolution()) + { + F = aCPV1V3.Magnitude() / aV13; + return true; + } + const double aDCrossDU = aCPV1V2.Dot(aCPV1V3) / aCPMag; + F = aDCrossDU / aV13 - THE_CURVATURE_DERIV_COEFF * aCPMag * aV1V2 / aV15; + return true; + } + + bool Values(const double X, double& F, double& D) + { + double aDx = myEpsX / THE_DIFF_STEP_DIVISOR; + if (X + aDx > myCurve->LastParameter()) + { + aDx = -aDx; + } + + Value(X, F); + double aF2; + Value(X + aDx, aF2); + D = (aF2 - F) / aDx; + return true; + } + + bool IsMinKC(const double X) const + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + + myCurve->D3(X, aP, aV1, aV2, aV3); + const double aV1V1 = aV1.SquareMagnitude(); + const double aNV1 = std::sqrt(aV1V1); + const double aV13 = aV1V1 * aNV1; + if (aV13 < gp::Resolution()) + { + return false; + } + const double aKC = aV1.Crossed(aV2).Magnitude() / aV13; + + double aDx = myEpsX; + if (X + aDx > myCurve->LastParameter()) + { + aDx = -aDx; + } + + myCurve->D3(X + aDx, aP, aV1, aV2, aV3); + const double aV1V1n = aV1.SquareMagnitude(); + const double aNV1n = std::sqrt(aV1V1n); + const double aV13n = aV1V1n * aNV1n; + if (aV13n < gp::Resolution()) + { + return false; + } + const double aKP = aV1.Crossed(aV2).Magnitude() / aV13n; + + return std::abs(aKC) > std::abs(aKP); + } + +private: + const GeomAdaptor_Curve* myCurve; + double myEpsX; +}; + +//! Function for finding inflection points on offset curves. +class FuncCurNul +{ +public: + FuncCurNul(const GeomAdaptor_Curve* theCurve) + : myCurve(theCurve) + { + } + + bool Value(const double X, double& F) + { + double aD; + return Values(X, F, aD); + } + + bool Values(const double X, double& F, double& D) + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + myCurve->D3(X, aP, aV1, aV2, aV3); + + const gp_Vec aCPV1V2 = aV1.Crossed(aV2); + const double aCPMag = aCPV1V2.Magnitude(); + const gp_Vec aCPV1V3 = aV1.Crossed(aV3); + const double aV1V2 = aV1.Dot(aV2); + const double aV2V3 = aV2.Dot(aV3); + const double aNV1 = aV1.Magnitude(); + const double aNV2 = aV2.Magnitude(); + + F = 0.0; + D = 0.0; + + if (aNV2 < THE_D2_MAGNITUDE_THRESHOLD) + { + return true; + } + if (aNV1 * aNV2 < gp::Resolution()) + { + return false; + } + + F = aCPMag / (aNV1 * aNV2); + + if (aCPMag < gp::Resolution()) + { + D = aCPV1V3.Magnitude() / (aNV1 * aNV2); + } + else + { + const double aDCrossDU = aCPV1V2.Dot(aCPV1V3) / aCPMag; + D = (aDCrossDU - aCPMag * aV1V2 / (aNV1 * aNV1) - aCPMag * aV2V3 / (aNV2 * aNV2)) + / (aNV1 * aNV2); + } + return true; + } + +private: + const GeomAdaptor_Curve* myCurve; +}; + +} // namespace + +//================================================================================================== + +GeomProp::TangentResult GeomProp_OffsetCurve::Tangent(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2, aD3; + myAdaptor->D3(theParam, aPnt, aD1, aD2, aD3); + return GeomProp::ComputeTangent(aD1, aD2, aD3, theTol); +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp_OffsetCurve::Curvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {0.0, false, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCurvature(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp_OffsetCurve::Normal(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeNormal(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp_OffsetCurve::CentreOfCurvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCentreOfCurvature(aPnt, aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_OffsetCurve::FindCurvatureExtrema() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + const double aUMin = myAdaptor->FirstParameter(); + const double aUMax = myAdaptor->LastParameter(); + const double aEpsH = THE_EPSILON_SCALE * (aUMax - aUMin); + + FuncCurExt aFunc(myAdaptor, aEpsH); + + MathRoot::MultipleConfig aConfig; + aConfig.NbSamples = THE_EXTREMA_NB_SAMPLES; + aConfig.XTolerance = aEpsH; + aConfig.FTolerance = aEpsH; + + MathRoot::MultipleResult aRoots = + MathRoot::FindAllRootsWithDerivative(aFunc, aUMin, aUMax, aConfig); + + if (aRoots.IsDone()) + { + for (int j = 0; j < aRoots.NbRoots(); ++j) + { + double aParam = aRoots[j]; + MathUtils::Config aBrentCfg; + aBrentCfg.XTolerance = Precision::PConfusion(); + aBrentCfg.FTolerance = Precision::PConfusion(); + auto aBrent = MathRoot::Brent(aFunc, aParam - aEpsH, aParam + aEpsH, aBrentCfg); + if (aBrent.IsDone() && aBrent.Root.has_value()) + { + aParam = *aBrent.Root; + } + const bool aIsMin = aFunc.IsMinKC(aParam); + const GeomProp::CIType aType = + aIsMin ? GeomProp::CIType::MinCurvature : GeomProp::CIType::MaxCurvature; + aResult.Points.Append({aParam, aType}); + } + } + else + { + aResult.IsDone = false; + } + + return aResult; +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_OffsetCurve::FindInflections() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + FuncCurNul aFunc(myAdaptor); + + MathRoot::MultipleConfig aConfig; + aConfig.NbSamples = THE_INFLECTION_NB_SAMPLES; + aConfig.XTolerance = THE_INFLECTION_TOLERANCE; + aConfig.FTolerance = THE_INFLECTION_TOLERANCE; + + MathRoot::MultipleResult aRoots = + MathRoot::FindAllRoots(aFunc, myAdaptor->FirstParameter(), myAdaptor->LastParameter(), aConfig); + + if (aRoots.IsDone()) + { + for (int j = 0; j < aRoots.NbRoots(); ++j) + { + aResult.Points.Append({aRoots[j], GeomProp::CIType::Inflection}); + } + } + else + { + aResult.IsDone = false; + } + + return aResult; +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetCurve.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetCurve.hxx new file mode 100644 index 0000000000..d9de4ad748 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetCurve.hxx @@ -0,0 +1,73 @@ +// 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 _GeomProp_OffsetCurve_HeaderFile +#define _GeomProp_OffsetCurve_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a 3D offset curve. +//! +//! Uses numeric root-finding for curvature extrema and inflection points. +//! Local properties are computed from the offset curve's own D1/D2/D3. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_OffsetCurve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must wrap an offset curve, must not be null) + GeomProp_OffsetCurve(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_OffsetCurve(const GeomProp_OffsetCurve&) = delete; + GeomProp_OffsetCurve& operator=(const GeomProp_OffsetCurve&) = delete; + GeomProp_OffsetCurve(GeomProp_OffsetCurve&&) = delete; + GeomProp_OffsetCurve& operator=(GeomProp_OffsetCurve&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + Standard_EXPORT GeomProp::TangentResult Tangent(double theParam, double theTol) const; + + //! Compute curvature at given parameter. + Standard_EXPORT GeomProp::CurvatureResult Curvature(double theParam, double theTol) const; + + //! Compute normal at given parameter. + Standard_EXPORT GeomProp::NormalResult Normal(double theParam, double theTol) const; + + //! Compute centre of curvature at given parameter. + Standard_EXPORT GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const; + + //! Find curvature extrema using numeric root-finding. + Standard_EXPORT GeomProp::CurveAnalysis FindCurvatureExtrema() const; + + //! Find inflection points using numeric root-finding. + Standard_EXPORT GeomProp::CurveAnalysis FindInflections() const; + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_OffsetCurve_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetSurface.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetSurface.cxx new file mode 100644 index 0000000000..1a42040ec7 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetSurface.cxx @@ -0,0 +1,62 @@ +// 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 + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_OffsetSurface::Normal(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_OffsetSurface::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_OffsetSurface::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetSurface.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetSurface.hxx new file mode 100644 index 0000000000..01c6547a3f --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_OffsetSurface.hxx @@ -0,0 +1,68 @@ +// 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 _GeomProp_OffsetSurface_HeaderFile +#define _GeomProp_OffsetSurface_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for an offset surface. +//! +//! Uses numeric evaluation from adaptor derivatives. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_OffsetSurface +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_OffsetSurface(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_OffsetSurface(const GeomProp_OffsetSurface&) = delete; + GeomProp_OffsetSurface& operator=(const GeomProp_OffsetSurface&) = delete; + GeomProp_OffsetSurface(GeomProp_OffsetSurface&&) = delete; + GeomProp_OffsetSurface& operator=(GeomProp_OffsetSurface&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given parameter. + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given parameter. + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given parameter. + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_OffsetSurface_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherCurve.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherCurve.cxx new file mode 100644 index 0000000000..f129d59e14 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherCurve.cxx @@ -0,0 +1,339 @@ +// 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 + +namespace +{ + +constexpr double THE_CURVATURE_DERIV_COEFF = 3.0; +constexpr double THE_DIFF_STEP_DIVISOR = 100.0; +constexpr double THE_D2_MAGNITUDE_THRESHOLD = 1.0e-4; +constexpr double THE_EPSILON_SCALE = 1.0e-4; +constexpr int THE_EXTREMA_NB_SAMPLES = 100; +constexpr int THE_INFLECTION_NB_SAMPLES = 30; +constexpr double THE_INFLECTION_TOLERANCE = 1.0e-6; + +//! Function for finding curvature extrema. +class FuncCurExt +{ +public: + FuncCurExt(const GeomAdaptor_Curve* theCurve, const double theTol) + : myCurve(theCurve), + myEpsX(theTol) + { + } + + bool Value(const double X, double& F) + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + myCurve->D3(X, aP, aV1, aV2, aV3); + + const gp_Vec aCPV1V2 = aV1.Crossed(aV2); + const double aCPMag = aCPV1V2.Magnitude(); + const gp_Vec aCPV1V3 = aV1.Crossed(aV3); + const double aV1V2 = aV1.Dot(aV2); + const double aV1V1 = aV1.SquareMagnitude(); + const double aNV1 = std::sqrt(aV1V1); + const double aV13 = aV1V1 * aNV1; + const double aV15 = aV13 * aV1V1; + + if (aV15 < gp::Resolution()) + { + return false; + } + + if (aCPMag < gp::Resolution()) + { + F = aCPV1V3.Magnitude() / aV13; + return true; + } + const double aDCrossDU = aCPV1V2.Dot(aCPV1V3) / aCPMag; + F = aDCrossDU / aV13 - THE_CURVATURE_DERIV_COEFF * aCPMag * aV1V2 / aV15; + return true; + } + + bool Values(const double X, double& F, double& D) + { + double aDx = myEpsX / THE_DIFF_STEP_DIVISOR; + if (X + aDx > myCurve->LastParameter()) + { + aDx = -aDx; + } + + Value(X, F); + double aF2; + Value(X + aDx, aF2); + D = (aF2 - F) / aDx; + return true; + } + + bool IsMinKC(const double X) const + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + + myCurve->D3(X, aP, aV1, aV2, aV3); + const double aV1V1 = aV1.SquareMagnitude(); + const double aNV1 = std::sqrt(aV1V1); + const double aV13 = aV1V1 * aNV1; + if (aV13 < gp::Resolution()) + { + return false; + } + const double aKC = aV1.Crossed(aV2).Magnitude() / aV13; + + double aDx = myEpsX; + if (X + aDx > myCurve->LastParameter()) + { + aDx = -aDx; + } + + myCurve->D3(X + aDx, aP, aV1, aV2, aV3); + const double aV1V1n = aV1.SquareMagnitude(); + const double aNV1n = std::sqrt(aV1V1n); + const double aV13n = aV1V1n * aNV1n; + if (aV13n < gp::Resolution()) + { + return false; + } + const double aKP = aV1.Crossed(aV2).Magnitude() / aV13n; + + return std::abs(aKC) > std::abs(aKP); + } + +private: + const GeomAdaptor_Curve* myCurve; + double myEpsX; +}; + +//! Function for finding inflection points. +class FuncCurNul +{ +public: + FuncCurNul(const GeomAdaptor_Curve* theCurve) + : myCurve(theCurve) + { + } + + bool Value(const double X, double& F) + { + double aD; + return Values(X, F, aD); + } + + bool Values(const double X, double& F, double& D) + { + gp_Pnt aP; + gp_Vec aV1, aV2, aV3; + myCurve->D3(X, aP, aV1, aV2, aV3); + + const gp_Vec aCPV1V2 = aV1.Crossed(aV2); + const double aCPMag = aCPV1V2.Magnitude(); + const gp_Vec aCPV1V3 = aV1.Crossed(aV3); + const double aV1V2 = aV1.Dot(aV2); + const double aV2V3 = aV2.Dot(aV3); + const double aNV1 = aV1.Magnitude(); + const double aNV2 = aV2.Magnitude(); + + F = 0.0; + D = 0.0; + + if (aNV2 < THE_D2_MAGNITUDE_THRESHOLD) + { + return true; + } + if (aNV1 * aNV2 < gp::Resolution()) + { + return false; + } + + F = aCPMag / (aNV1 * aNV2); + + if (aCPMag < gp::Resolution()) + { + D = aCPV1V3.Magnitude() / (aNV1 * aNV2); + } + else + { + const double aDCrossDU = aCPV1V2.Dot(aCPV1V3) / aCPMag; + D = (aDCrossDU - aCPMag * aV1V2 / (aNV1 * aNV1) - aCPMag * aV2V3 / (aNV2 * aNV2)) + / (aNV1 * aNV2); + } + return true; + } + +private: + const GeomAdaptor_Curve* myCurve; +}; + +} // namespace + +//================================================================================================== + +GeomProp::TangentResult GeomProp_OtherCurve::Tangent(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2, aD3; + myAdaptor->D3(theParam, aPnt, aD1, aD2, aD3); + return GeomProp::ComputeTangent(aD1, aD2, aD3, theTol); +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp_OtherCurve::Curvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {0.0, false, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCurvature(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp_OtherCurve::Normal(const double theParam, const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeNormal(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp_OtherCurve::CentreOfCurvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCentreOfCurvature(aPnt, aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_OtherCurve::FindCurvatureExtrema() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + const double aUMin = myAdaptor->FirstParameter(); + const double aUMax = myAdaptor->LastParameter(); + const double aEpsH = THE_EPSILON_SCALE * (aUMax - aUMin); + + FuncCurExt aFunc(myAdaptor, aEpsH); + + MathRoot::MultipleConfig aConfig; + aConfig.NbSamples = THE_EXTREMA_NB_SAMPLES; + aConfig.XTolerance = aEpsH; + aConfig.FTolerance = aEpsH; + + MathRoot::MultipleResult aRoots = + MathRoot::FindAllRootsWithDerivative(aFunc, aUMin, aUMax, aConfig); + + if (aRoots.IsDone()) + { + for (int j = 0; j < aRoots.NbRoots(); ++j) + { + double aParam = aRoots[j]; + MathUtils::Config aBrentCfg; + aBrentCfg.XTolerance = Precision::PConfusion(); + aBrentCfg.FTolerance = Precision::PConfusion(); + auto aBrent = MathRoot::Brent(aFunc, aParam - aEpsH, aParam + aEpsH, aBrentCfg); + if (aBrent.IsDone() && aBrent.Root.has_value()) + { + aParam = *aBrent.Root; + } + const bool aIsMin = aFunc.IsMinKC(aParam); + const GeomProp::CIType aType = + aIsMin ? GeomProp::CIType::MinCurvature : GeomProp::CIType::MaxCurvature; + aResult.Points.Append({aParam, aType}); + } + } + else + { + aResult.IsDone = false; + } + + return aResult; +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_OtherCurve::FindInflections() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + FuncCurNul aFunc(myAdaptor); + + MathRoot::MultipleConfig aConfig; + aConfig.NbSamples = THE_INFLECTION_NB_SAMPLES; + aConfig.XTolerance = THE_INFLECTION_TOLERANCE; + aConfig.FTolerance = THE_INFLECTION_TOLERANCE; + + MathRoot::MultipleResult aRoots = + MathRoot::FindAllRoots(aFunc, myAdaptor->FirstParameter(), myAdaptor->LastParameter(), aConfig); + + if (aRoots.IsDone()) + { + for (int j = 0; j < aRoots.NbRoots(); ++j) + { + aResult.Points.Append({aRoots[j], GeomProp::CIType::Inflection}); + } + } + else + { + aResult.IsDone = false; + } + + return aResult; +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherCurve.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherCurve.hxx new file mode 100644 index 0000000000..0c3d083ff5 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherCurve.hxx @@ -0,0 +1,73 @@ +// 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 _GeomProp_OtherCurve_HeaderFile +#define _GeomProp_OtherCurve_HeaderFile + +#include +#include +#include +#include + +//! @brief Fallback local differential properties for any 3D curve type. +//! +//! Uses adaptor D1/D2/D3 methods for property computation +//! and numeric root-finding for curvature extrema and inflection points. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_OtherCurve +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must not be null) + GeomProp_OtherCurve(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_OtherCurve(const GeomProp_OtherCurve&) = delete; + GeomProp_OtherCurve& operator=(const GeomProp_OtherCurve&) = delete; + GeomProp_OtherCurve(GeomProp_OtherCurve&&) = delete; + GeomProp_OtherCurve& operator=(GeomProp_OtherCurve&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + Standard_EXPORT GeomProp::TangentResult Tangent(double theParam, double theTol) const; + + //! Compute curvature at given parameter. + Standard_EXPORT GeomProp::CurvatureResult Curvature(double theParam, double theTol) const; + + //! Compute normal at given parameter. + Standard_EXPORT GeomProp::NormalResult Normal(double theParam, double theTol) const; + + //! Compute centre of curvature at given parameter. + Standard_EXPORT GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const; + + //! Find curvature extrema using numeric root-finding. + Standard_EXPORT GeomProp::CurveAnalysis FindCurvatureExtrema() const; + + //! Find inflection points using numeric root-finding. + Standard_EXPORT GeomProp::CurveAnalysis FindInflections() const; + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_OtherCurve_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherSurface.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherSurface.cxx new file mode 100644 index 0000000000..7b520994d7 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherSurface.cxx @@ -0,0 +1,62 @@ +// 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 + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_OtherSurface::Normal(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_OtherSurface::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_OtherSurface::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherSurface.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherSurface.hxx new file mode 100644 index 0000000000..a3ddb46ccc --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_OtherSurface.hxx @@ -0,0 +1,68 @@ +// 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 _GeomProp_OtherSurface_HeaderFile +#define _GeomProp_OtherSurface_HeaderFile + +#include +#include +#include +#include + +//! @brief Fallback local differential properties for any surface type. +//! +//! Uses adaptor D1/D2 methods for property computation. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_OtherSurface +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_OtherSurface(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_OtherSurface(const GeomProp_OtherSurface&) = delete; + GeomProp_OtherSurface& operator=(const GeomProp_OtherSurface&) = delete; + GeomProp_OtherSurface(GeomProp_OtherSurface&&) = delete; + GeomProp_OtherSurface& operator=(GeomProp_OtherSurface&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given parameter. + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given parameter. + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given parameter. + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_OtherSurface_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Parabola.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Parabola.cxx new file mode 100644 index 0000000000..ebd8d8b084 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Parabola.cxx @@ -0,0 +1,97 @@ +// 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 + +//================================================================================================== + +GeomProp::TangentResult GeomProp_Parabola::Tangent(const double theParam, const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2, aD3; + myAdaptor->D3(theParam, aPnt, aD1, aD2, aD3); + return GeomProp::ComputeTangent(aD1, aD2, aD3, theTol); +} + +//================================================================================================== + +GeomProp::CurvatureResult GeomProp_Parabola::Curvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {0.0, false, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCurvature(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::NormalResult GeomProp_Parabola::Normal(const double theParam, const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeNormal(aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CentreResult GeomProp_Parabola::CentreOfCurvature(const double theParam, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1, aD2; + myAdaptor->D2(theParam, aPnt, aD1, aD2); + return GeomProp::ComputeCentreOfCurvature(aPnt, aD1, aD2, theTol); +} + +//================================================================================================== + +GeomProp::CurveAnalysis GeomProp_Parabola::FindCurvatureExtrema() const +{ + GeomProp::CurveAnalysis aResult; + aResult.IsDone = true; + + if (myAdaptor == nullptr) + { + aResult.IsDone = false; + return aResult; + } + + const double aUFirst = myAdaptor->FirstParameter(); + const double aULast = myAdaptor->LastParameter(); + + // Parabola has maximum |curvature| at parameter 0 (vertex). + if (aUFirst <= 0.0 && aULast >= 0.0) + { + aResult.Points.Append({0.0, GeomProp::CIType::MinCurvature}); + } + + return aResult; +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Parabola.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Parabola.hxx new file mode 100644 index 0000000000..759e4e0022 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Parabola.hxx @@ -0,0 +1,75 @@ +// 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 _GeomProp_Parabola_HeaderFile +#define _GeomProp_Parabola_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a 3D parabola. +//! +//! A parabola has a single curvature extremum (maximum |curvature|) at parameter 0 +//! (the vertex). No inflection points exist. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. This class does not manage +//! the adaptor's lifetime. +class GeomProp_Parabola +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the 3D curve adaptor (must wrap a parabola, must not be null) + GeomProp_Parabola(const GeomAdaptor_Curve* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Parabola(const GeomProp_Parabola&) = delete; + GeomProp_Parabola& operator=(const GeomProp_Parabola&) = delete; + GeomProp_Parabola(GeomProp_Parabola&&) = delete; + GeomProp_Parabola& operator=(GeomProp_Parabola&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Curve* Adaptor() const { return myAdaptor; } + + //! Compute tangent at given parameter. + Standard_EXPORT GeomProp::TangentResult Tangent(double theParam, double theTol) const; + + //! Compute curvature at given parameter. + Standard_EXPORT GeomProp::CurvatureResult Curvature(double theParam, double theTol) const; + + //! Compute normal at given parameter. + Standard_EXPORT GeomProp::NormalResult Normal(double theParam, double theTol) const; + + //! Compute centre of curvature at given parameter. + Standard_EXPORT GeomProp::CentreResult CentreOfCurvature(double theParam, double theTol) const; + + //! Find curvature extrema on the parabola. + //! Single extremum at parameter 0 (the vertex), if within parameter range. + Standard_EXPORT GeomProp::CurveAnalysis FindCurvatureExtrema() const; + + //! Find inflection points on the parabola. + //! A parabola has no inflection points. + GeomProp::CurveAnalysis FindInflections() const { return {{}, true}; } + +private: + const GeomAdaptor_Curve* myAdaptor; +}; + +#endif // _GeomProp_Parabola_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Plane.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Plane.hxx new file mode 100644 index 0000000000..eb666fda12 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Plane.hxx @@ -0,0 +1,98 @@ +// 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 _GeomProp_Plane_HeaderFile +#define _GeomProp_Plane_HeaderFile + +#include +#include +#include + +//! @brief Local differential properties for a plane surface. +//! +//! Trivial implementation: constant normal, zero curvatures everywhere. +//! All properties are computed analytically without numerical evaluation. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_Plane +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_Plane(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Plane(const GeomProp_Plane&) = delete; + GeomProp_Plane& operator=(const GeomProp_Plane&) = delete; + GeomProp_Plane(GeomProp_Plane&&) = delete; + GeomProp_Plane& operator=(GeomProp_Plane&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal. Constant for a plane. + //! Uses D1U x D1V cross product to ensure correct sign for flipped planes. + GeomProp::SurfaceNormalResult Normal(double theU, double theV, double theTol) const + { + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); + } + + //! Compute principal curvatures. Both are zero for a plane. + GeomProp::SurfaceCurvatureResult Curvatures(double /*theU*/, + double /*theV*/, + double /*theTol*/) const + { + if (myAdaptor == nullptr) + { + return {}; + } + GeomProp::SurfaceCurvatureResult aResult; + aResult.MinCurvature = 0.0; + aResult.MaxCurvature = 0.0; + aResult.MinDirection = myAdaptor->Plane().Position().XDirection(); + aResult.MaxDirection = myAdaptor->Plane().Position().YDirection(); + aResult.IsDefined = true; + aResult.IsUmbilic = true; + return aResult; + } + + //! Compute mean and Gaussian curvatures. Both are zero for a plane. + GeomProp::MeanGaussianResult MeanGaussian(double /*theU*/, + double /*theV*/, + double /*theTol*/) const + { + if (myAdaptor == nullptr) + { + return {}; + } + return {0.0, 0.0, true}; + } + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_Plane_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Sphere.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Sphere.hxx new file mode 100644 index 0000000000..aa6e62d1fc --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Sphere.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 _GeomProp_Sphere_HeaderFile +#define _GeomProp_Sphere_HeaderFile + +#include +#include +#include + +//! @brief Local differential properties for a spherical surface. +//! +//! Analytical implementation: constant curvature 1/R, umbilic everywhere. +//! Both principal curvatures equal 1/R. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_Sphere +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_Sphere(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Sphere(const GeomProp_Sphere&) = delete; + GeomProp_Sphere& operator=(const GeomProp_Sphere&) = delete; + GeomProp_Sphere(GeomProp_Sphere&&) = delete; + GeomProp_Sphere& operator=(GeomProp_Sphere&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given (U, V) parameter. + //! For a sphere, the normal is radially outward from the center. + //! TODO: At poles (V = +/-PI/2), D1U degenerates and Normal returns IsDefined=false. + //! Could use analytical normal (radial direction from center) for these special cases. + GeomProp::SurfaceNormalResult Normal(double theU, double theV, double theTol) const + { + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); + } + + //! Compute principal curvatures using fundamental forms for correct sign convention. + GeomProp::SurfaceCurvatureResult Curvatures(double theU, double theV, double theTol) const + { + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); + } + + //! Compute mean and Gaussian curvatures using fundamental forms for correct sign convention. + GeomProp::MeanGaussianResult MeanGaussian(double theU, double theV, double theTol) const + { + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); + } + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_Sphere_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Surface.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Surface.cxx new file mode 100644 index 0000000000..f3f49818e3 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Surface.cxx @@ -0,0 +1,165 @@ +// 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 + +//================================================================================================== + +void GeomProp_Surface::Initialize(const Adaptor3d_Surface& theSurface) +{ + if (theSurface.IsKind(STANDARD_TYPE(GeomAdaptor_Surface))) + { + const auto& aGeomAdaptor = static_cast(theSurface); + myAdaptor = new GeomAdaptor_Surface(aGeomAdaptor); + initFromAdaptor(); + return; + } + + // For non-GeomAdaptor, set uninitialized. + myAdaptor.Nullify(); + mySurfaceType = theSurface.GetType(); + myEvaluator.emplace(); +} + +//================================================================================================== + +void GeomProp_Surface::Initialize(const occ::handle& theSurface) +{ + if (theSurface.IsNull()) + { + myAdaptor.Nullify(); + myEvaluator.emplace(); + mySurfaceType = GeomAbs_OtherSurface; + return; + } + + myAdaptor = new GeomAdaptor_Surface(theSurface); + initFromAdaptor(); +} + +//================================================================================================== + +void GeomProp_Surface::initFromAdaptor() +{ + mySurfaceType = myAdaptor->GetType(); + const GeomAdaptor_Surface* aPtr = myAdaptor.get(); + + switch (mySurfaceType) + { + case GeomAbs_Plane: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_Cylinder: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_Cone: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_Sphere: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_Torus: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_BezierSurface: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_BSplineSurface: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_SurfaceOfRevolution: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_SurfaceOfExtrusion: + myEvaluator.emplace(aPtr); + break; + case GeomAbs_OffsetSurface: + myEvaluator.emplace(aPtr); + break; + default: + myEvaluator.emplace(aPtr); + break; + } +} + +//================================================================================================== + +bool GeomProp_Surface::IsInitialized() const +{ + return !std::holds_alternative(myEvaluator); +} + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_Surface::Normal(const double theU, + const double theV, + const double theTol) const +{ + return std::visit( + [theU, theV, theTol](const auto& theEval) -> GeomProp::SurfaceNormalResult { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {{}, false}; + } + else + { + return theEval.Normal(theU, theV, theTol); + } + }, + myEvaluator); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_Surface::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + return std::visit( + [theU, theV, theTol](const auto& theEval) -> GeomProp::SurfaceCurvatureResult { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {}; + } + else + { + return theEval.Curvatures(theU, theV, theTol); + } + }, + myEvaluator); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_Surface::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + return std::visit( + [theU, theV, theTol](const auto& theEval) -> GeomProp::MeanGaussianResult { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return {}; + } + else + { + return theEval.MeanGaussian(theU, theV, theTol); + } + }, + myEvaluator); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Surface.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Surface.hxx new file mode 100644 index 0000000000..76a669fb6a --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Surface.hxx @@ -0,0 +1,154 @@ +// 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 _GeomProp_Surface_HeaderFile +#define _GeomProp_Surface_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//! @brief Unified local differential property evaluator for any 3D surface. +//! +//! Uses std::variant for compile-time type safety and zero heap allocation +//! for the evaluator itself. Automatically detects surface type from +//! Adaptor3d_Surface or Geom_Surface and dispatches to the appropriate +//! specialized evaluator. +//! +//! Supported surface types with optimized evaluation: +//! - Plane: Trivial (constant normal, zero curvatures) +//! - Cylinder: Constant principal curvatures (0 and 1/R) +//! - Cone: Analytical curvatures (vary along meridian) +//! - Sphere: Constant curvature 1/R, umbilic +//! - Torus: Analytical curvatures (vary along meridian) +//! - BezierSurface: Numeric from derivatives +//! - BSplineSurface: Numeric from derivatives +//! - SurfaceOfRevolution: Numeric from derivatives +//! - SurfaceOfExtrusion: Numeric from derivatives +//! - OffsetSurface: Numeric from derivatives +//! - Other: Fallback using adaptor derivatives +//! +//! Usage: +//! @code +//! GeomProp_Surface aProp; +//! aProp.Initialize(myGeomSurface); +//! GeomProp::SurfaceCurvatureResult aCurv = aProp.Curvatures(0.5, 0.5, Precision::Confusion()); +//! if (aCurv.IsDefined) +//! { +//! double aMinK = aCurv.MinCurvature; +//! double aMaxK = aCurv.MaxCurvature; +//! } +//! @endcode +class GeomProp_Surface +{ +public: + DEFINE_STANDARD_ALLOC + + //! Variant type holding all possible 3D surface property evaluators. + using EvaluatorVariant = std::variant; + + //! Default constructor - uninitialized state. + GeomProp_Surface() + : myEvaluator(std::monostate{}), + mySurfaceType(GeomAbs_OtherSurface) + { + } + + //! Non-copyable and non-movable. + GeomProp_Surface(const GeomProp_Surface&) = delete; + GeomProp_Surface& operator=(const GeomProp_Surface&) = delete; + GeomProp_Surface(GeomProp_Surface&&) = delete; + GeomProp_Surface& operator=(GeomProp_Surface&&) = delete; + + //! Initialize from 3D adaptor reference (auto-detects surface type). + //! For GeomAdaptor_Surface, extracts underlying Geom_Surface for optimized evaluation. + //! @param[in] theSurface 3D surface adaptor reference + Standard_EXPORT void Initialize(const Adaptor3d_Surface& theSurface); + + //! Initialize from geometry handle (auto-detects surface type). + //! @param[in] theSurface 3D geometry to evaluate + Standard_EXPORT void Initialize(const occ::handle& theSurface); + + //! Returns true if properly initialized. + Standard_EXPORT bool IsInitialized() const; + + //! Returns the detected surface type. + GeomAbs_SurfaceType GetType() const { return mySurfaceType; } + + //! Compute surface normal at given (U, V) parameter. + //! @param[in] theU U parameter on the surface + //! @param[in] theV V parameter on the surface + //! @param[in] theTol linear tolerance + //! @return surface normal result with validity flag + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given (U, V) parameter. + //! @param[in] theU U parameter on the surface + //! @param[in] theV V parameter on the surface + //! @param[in] theTol linear tolerance + //! @return curvature result with principal curvatures and directions + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given (U, V) parameter. + //! @param[in] theU U parameter on the surface + //! @param[in] theV V parameter on the surface + //! @param[in] theTol linear tolerance + //! @return mean and Gaussian curvature result + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + //! Initialize from stored adaptor (dispatches to per-geometry evaluator). + //! Must be called after myAdaptor is set. Per-geometry evaluators receive + //! a non-owning pointer to myAdaptor; their lifetime is managed by the variant. + Standard_EXPORT void initFromAdaptor(); + + occ::handle myAdaptor; //!< Owns the adaptor (ensures lifetime). + EvaluatorVariant myEvaluator; //!< Per-geometry evaluator (non-owning pointer to myAdaptor). + GeomAbs_SurfaceType mySurfaceType; +}; + +#endif // _GeomProp_Surface_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfExtrusion.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfExtrusion.cxx new file mode 100644 index 0000000000..00ddaebc66 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfExtrusion.cxx @@ -0,0 +1,62 @@ +// 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 + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_SurfaceOfExtrusion::Normal(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_SurfaceOfExtrusion::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_SurfaceOfExtrusion::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfExtrusion.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfExtrusion.hxx new file mode 100644 index 0000000000..55e73b4790 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfExtrusion.hxx @@ -0,0 +1,68 @@ +// 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 _GeomProp_SurfaceOfExtrusion_HeaderFile +#define _GeomProp_SurfaceOfExtrusion_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a surface of extrusion. +//! +//! Uses numeric evaluation from adaptor derivatives. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_SurfaceOfExtrusion +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_SurfaceOfExtrusion(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_SurfaceOfExtrusion(const GeomProp_SurfaceOfExtrusion&) = delete; + GeomProp_SurfaceOfExtrusion& operator=(const GeomProp_SurfaceOfExtrusion&) = delete; + GeomProp_SurfaceOfExtrusion(GeomProp_SurfaceOfExtrusion&&) = delete; + GeomProp_SurfaceOfExtrusion& operator=(GeomProp_SurfaceOfExtrusion&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given parameter. + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given parameter. + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given parameter. + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_SurfaceOfExtrusion_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfRevolution.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfRevolution.cxx new file mode 100644 index 0000000000..92af991ad9 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfRevolution.cxx @@ -0,0 +1,62 @@ +// 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 + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_SurfaceOfRevolution::Normal(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_SurfaceOfRevolution::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_SurfaceOfRevolution::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfRevolution.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfRevolution.hxx new file mode 100644 index 0000000000..5688537a3d --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_SurfaceOfRevolution.hxx @@ -0,0 +1,68 @@ +// 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 _GeomProp_SurfaceOfRevolution_HeaderFile +#define _GeomProp_SurfaceOfRevolution_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a surface of revolution. +//! +//! Uses numeric evaluation from adaptor derivatives. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_SurfaceOfRevolution +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_SurfaceOfRevolution(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_SurfaceOfRevolution(const GeomProp_SurfaceOfRevolution&) = delete; + GeomProp_SurfaceOfRevolution& operator=(const GeomProp_SurfaceOfRevolution&) = delete; + GeomProp_SurfaceOfRevolution(GeomProp_SurfaceOfRevolution&&) = delete; + GeomProp_SurfaceOfRevolution& operator=(GeomProp_SurfaceOfRevolution&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given parameter. + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given parameter. + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given parameter. + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_SurfaceOfRevolution_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Torus.cxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Torus.cxx new file mode 100644 index 0000000000..fbe3fdce16 --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Torus.cxx @@ -0,0 +1,62 @@ +// 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 + +//================================================================================================== + +GeomProp::SurfaceNormalResult GeomProp_Torus::Normal(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {{}, false}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V; + myAdaptor->D1(theU, theV, aPnt, aD1U, aD1V); + return GeomProp::ComputeSurfaceNormal(aD1U, aD1V, theTol); +} + +//================================================================================================== + +GeomProp::SurfaceCurvatureResult GeomProp_Torus::Curvatures(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeSurfaceCurvatures(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} + +//================================================================================================== + +GeomProp::MeanGaussianResult GeomProp_Torus::MeanGaussian(const double theU, + const double theV, + const double theTol) const +{ + if (myAdaptor == nullptr) + { + return {}; + } + gp_Pnt aPnt; + gp_Vec aD1U, aD1V, aD2U, aD2V, aD2UV; + myAdaptor->D2(theU, theV, aPnt, aD1U, aD1V, aD2U, aD2V, aD2UV); + return GeomProp::ComputeMeanGaussian(aD1U, aD1V, aD2U, aD2V, aD2UV, theTol); +} diff --git a/src/ModelingData/TKG3d/GeomProp/GeomProp_Torus.hxx b/src/ModelingData/TKG3d/GeomProp/GeomProp_Torus.hxx new file mode 100644 index 0000000000..bf2284ae3c --- /dev/null +++ b/src/ModelingData/TKG3d/GeomProp/GeomProp_Torus.hxx @@ -0,0 +1,71 @@ +// 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 _GeomProp_Torus_HeaderFile +#define _GeomProp_Torus_HeaderFile + +#include +#include +#include +#include + +//! @brief Local differential properties for a toroidal surface. +//! +//! Uses analytical formulas. Curvature varies along the meridian (V direction): +//! - k1 = 1/r (constant, along the minor circle direction) +//! - k2 = cos(V) / (R + r*cos(V)) (varies, along the major circle direction) +//! where R is the major radius and r is the minor radius. +//! +//! @warning The caller must ensure that the adaptor pointer remains valid +//! for the entire lifetime of this object. +class GeomProp_Torus +{ +public: + DEFINE_STANDARD_ALLOC + + //! Constructor with adaptor pointer (non-owning). + //! @param theAdaptor the surface adaptor (must not be null) + GeomProp_Torus(const GeomAdaptor_Surface* theAdaptor) + : myAdaptor(theAdaptor) + { + } + + //! Non-copyable and non-movable. + GeomProp_Torus(const GeomProp_Torus&) = delete; + GeomProp_Torus& operator=(const GeomProp_Torus&) = delete; + GeomProp_Torus(GeomProp_Torus&&) = delete; + GeomProp_Torus& operator=(GeomProp_Torus&&) = delete; + + //! Returns the adaptor pointer. + const GeomAdaptor_Surface* Adaptor() const { return myAdaptor; } + + //! Compute surface normal at given parameter. + Standard_EXPORT GeomProp::SurfaceNormalResult Normal(double theU, + double theV, + double theTol) const; + + //! Compute principal curvatures at given parameter. + Standard_EXPORT GeomProp::SurfaceCurvatureResult Curvatures(double theU, + double theV, + double theTol) const; + + //! Compute mean and Gaussian curvatures at given parameter. + Standard_EXPORT GeomProp::MeanGaussianResult MeanGaussian(double theU, + double theV, + double theTol) const; + +private: + const GeomAdaptor_Surface* myAdaptor; +}; + +#endif // _GeomProp_Torus_HeaderFile diff --git a/src/ModelingData/TKG3d/PACKAGES.cmake b/src/ModelingData/TKG3d/PACKAGES.cmake index 537e911b71..42dfc77f2f 100644 --- a/src/ModelingData/TKG3d/PACKAGES.cmake +++ b/src/ModelingData/TKG3d/PACKAGES.cmake @@ -11,4 +11,5 @@ set(OCCT_TKG3d_LIST_OF_PACKAGES GProp GeomHash GeomEval + GeomProp )