From 3b8185bafbae36504cce9d2ef83f55678bf325a2 Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Mon, 8 Dec 2025 09:08:13 +0000 Subject: [PATCH] Shape Healing - Optimize PCurve projection (#890) - Implemented various test cases including projections of lines, circles, and B-splines on different surface types (planes, cylinders, spheres, and toroids). - Refactored ShapeConstruct_ProjectCurveOnSurface to improve handling of periodic surfaces and edge cases. - Updated header files to reflect new type aliases and improved structure for better readability and maintainability. - Added a new function `extractBSplineCurve` to streamline the extraction of B-spline curves from both trimmed and untrimmed curves. - Refactored `isBSplineCurveInvalid` to utilize the new extraction function, improving clarity and reducing code duplication. - Updated `generateCurvePoints` to leverage the new extraction method for better handling of B-spline curves. - Replaced std::vector with NCollection_Vector for better memory management in isBSplineCurveInvalid. - Enhanced rebuildBSpline function to improve knot adjustment logic while preserving curve geometry. - Introduced a new utility class, SurfaceProjectorWithCache, to enhance the projection of points onto B-spline surfaces by caching pole positions and their UV parameters. --- .../TKShHealing/GTests/FILES.cmake | 1 + ...peConstruct_ProjectCurveOnSurface_Test.cxx | 994 ++++++ .../ShapeConstruct_ProjectCurveOnSurface.cxx | 3125 +++++++++-------- .../ShapeConstruct_ProjectCurveOnSurface.hxx | 340 +- tests/offset/bugs/bug5806 | 3 - 5 files changed, 2843 insertions(+), 1620 deletions(-) create mode 100644 src/ModelingAlgorithms/TKShHealing/GTests/ShapeConstruct_ProjectCurveOnSurface_Test.cxx diff --git a/src/ModelingAlgorithms/TKShHealing/GTests/FILES.cmake b/src/ModelingAlgorithms/TKShHealing/GTests/FILES.cmake index c2f52ea8d6..f721c29512 100644 --- a/src/ModelingAlgorithms/TKShHealing/GTests/FILES.cmake +++ b/src/ModelingAlgorithms/TKShHealing/GTests/FILES.cmake @@ -3,4 +3,5 @@ set(OCCT_TKShHealing_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_TKShHealing_GTests_FILES ShapeAnalysis_CanonicalRecognition_Test.cxx + ShapeConstruct_ProjectCurveOnSurface_Test.cxx ) diff --git a/src/ModelingAlgorithms/TKShHealing/GTests/ShapeConstruct_ProjectCurveOnSurface_Test.cxx b/src/ModelingAlgorithms/TKShHealing/GTests/ShapeConstruct_ProjectCurveOnSurface_Test.cxx new file mode 100644 index 0000000000..081aab5233 --- /dev/null +++ b/src/ModelingAlgorithms/TKShHealing/GTests/ShapeConstruct_ProjectCurveOnSurface_Test.cxx @@ -0,0 +1,994 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//================================================================================================== +// Test fixture for ShapeConstruct_ProjectCurveOnSurface +//================================================================================================== + +class ShapeConstruct_ProjectCurveOnSurfaceTest : public ::testing::Test +{ +protected: + void SetUp() override + { + myProjector = new ShapeConstruct_ProjectCurveOnSurface(); + myTolerance = Precision::Confusion(); + } + + //! Helper to create a simple B-spline curve lying on Z=0 plane + Handle(Geom_BSplineCurve) createPlanarBSpline() + { + TColgp_Array1OfPnt aPoles(1, 5); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(2, 3, 0); + aPoles(3) = gp_Pnt(5, 4, 0); + aPoles(4) = gp_Pnt(8, 2, 0); + aPoles(5) = gp_Pnt(10, 0, 0); + + GeomAPI_PointsToBSpline aBuilder(aPoles); + return aBuilder.Curve(); + } + + //! Helper to create a helical B-spline on a cylinder + Handle(Geom_BSplineCurve) createHelicalBSpline(const double theRadius, const int theNbPoints) + { + TColgp_Array1OfPnt aPoles(1, theNbPoints); + for (int i = 1; i <= theNbPoints; ++i) + { + const double anAngle = (i - 1) * M_PI / 4.0; + const double aZ = (i - 1) * 1.0; + aPoles(i) = gp_Pnt(theRadius * cos(anAngle), theRadius * sin(anAngle), aZ); + } + GeomAPI_PointsToBSpline aBuilder(aPoles); + return aBuilder.Curve(); + } + + //! Verifies that pcurve correctly maps to 3D points on surface + void verifyProjection(const Handle(Geom_Curve)& theCurve, + const Handle(Geom2d_Curve)& thePCurve, + const Handle(ShapeAnalysis_Surface)& theSurface, + const double theTolerance) + { + const double aFirst = theCurve->FirstParameter(); + const double aLast = theCurve->LastParameter(); + + for (double t = 0.0; t <= 1.0; t += 0.25) + { + const double aParam = aFirst + t * (aLast - aFirst); + gp_Pnt a3DPnt; + theCurve->D0(aParam, a3DPnt); + + const gp_Pnt2d a2DPnt = thePCurve->Value(aParam); + gp_Pnt aSurfPnt = theSurface->Value(a2DPnt); + + EXPECT_NEAR(a3DPnt.Distance(aSurfPnt), 0.0, theTolerance) << "Point mismatch at t=" << t; + } + } + + Handle(ShapeConstruct_ProjectCurveOnSurface) myProjector; + double myTolerance; +}; + +//================================================================================================== +// Basic projection tests - lines and circles on analytical surfaces +//================================================================================================== + +// Test: Project diagonal line on horizontal plane, verify 2D coordinates match X,Y of 3D points +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, LineOnPlane_A1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(Geom_TrimmedCurve) aLine = GC_MakeSegment(gp_Pnt(0, 0, 0), gp_Pnt(10, 10, 0)).Value(); + + Handle(ShapeAnalysis_Surface) aSAS = new ShapeAnalysis_Surface(aPlane); + myProjector->Init(aSAS, myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const double aFirst = aLine->FirstParameter(); + const double aLast = aLine->LastParameter(); + const Standard_Boolean aResult = myProjector->Perform(aLine, aFirst, aLast, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + // Verify start point maps to (0,0) and end point maps to (10,10) + const gp_Pnt2d aStart = aPCurve->Value(aFirst); + const gp_Pnt2d aEnd = aPCurve->Value(aLast); + EXPECT_NEAR(aStart.X(), 0.0, 0.01); + EXPECT_NEAR(aStart.Y(), 0.0, 0.01); + EXPECT_NEAR(aEnd.X(), 10.0, 0.01); + EXPECT_NEAR(aEnd.Y(), 10.0, 0.01); +} + +// Test: Project circle lying on cylinder surface, V-coordinate should be constant +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, CircleOnCylinder_A2) +{ + const double aRadius = 5.0; + Handle(Geom_CylindricalSurface) aCylinder = + new Geom_CylindricalSurface(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aRadius); + + // Circle at height Z=3 on the cylinder + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 3), gp_Dir(0, 0, 1)), aRadius); + Handle(Geom_Circle) aCircle = new Geom_Circle(aCirc); + + myProjector->Init(new ShapeAnalysis_Surface(aCylinder), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aCircle, 0.0, 2.0 * M_PI, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + // On cylinder parametrization, V = Z, so circle at Z=3 should have V=3 + const gp_Pnt2d aMid = aPCurve->Value(M_PI); + EXPECT_NEAR(aMid.Y(), 3.0, 0.01) << "V coordinate should be 3"; +} + +// Test: Project vertical line on cylinder (isoparametric case), U should be constant +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, IsoparametricLineOnCylinder_A3) +{ + const double aRadius = 5.0; + Handle(Geom_CylindricalSurface) aCylinder = + new Geom_CylindricalSurface(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aRadius); + + // Line along cylinder axis at X=radius, Y=0 (U=0 on cylinder) + Handle(Geom_TrimmedCurve) aLine = + GC_MakeSegment(gp_Pnt(aRadius, 0, 0), gp_Pnt(aRadius, 0, 10)).Value(); + + Handle(ShapeAnalysis_Surface) aSAS = new ShapeAnalysis_Surface(aCylinder); + myProjector->Init(aSAS, myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const double aFirst = aLine->FirstParameter(); + const double aLast = aLine->LastParameter(); + const Standard_Boolean aResult = myProjector->Perform(aLine, aFirst, aLast, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + // U should be constant (isoparametric curve) + const gp_Pnt2d aStart = aPCurve->Value(aFirst); + const gp_Pnt2d aEnd = aPCurve->Value(aLast); + EXPECT_NEAR(aStart.X(), aEnd.X(), 0.01) << "U should be constant for isoparametric curve"; +} + +//================================================================================================== +// B-Spline curve projection tests +//================================================================================================== + +// Test: Project planar B-spline on plane, endpoints should match original curve +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, BSplineOnPlane_B1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(Geom_BSplineCurve) aBSpline = createPlanarBSpline(); + ASSERT_FALSE(aBSpline.IsNull()); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aBSpline, aBSpline->FirstParameter(), aBSpline->LastParameter(), aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + // Verify endpoints + const gp_Pnt2d aStart = aPCurve->Value(aBSpline->FirstParameter()); + const gp_Pnt2d aEnd = aPCurve->Value(aBSpline->LastParameter()); + EXPECT_NEAR(aStart.X(), 0.0, 0.1); + EXPECT_NEAR(aStart.Y(), 0.0, 0.1); + EXPECT_NEAR(aEnd.X(), 10.0, 0.1); + EXPECT_NEAR(aEnd.Y(), 0.0, 0.1); +} + +// Test: Project helical B-spline on cylinder, verify all sample points lie on surface +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, HelicalBSplineOnCylinder_B2) +{ + const double aRadius = 5.0; + Handle(Geom_CylindricalSurface) aCylinder = + new Geom_CylindricalSurface(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aRadius); + + Handle(Geom_BSplineCurve) aBSpline = createHelicalBSpline(aRadius, 9); + ASSERT_FALSE(aBSpline.IsNull()); + + Handle(ShapeAnalysis_Surface) aSAS = new ShapeAnalysis_Surface(aCylinder); + myProjector->Init(aSAS, myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aBSpline, aBSpline->FirstParameter(), aBSpline->LastParameter(), aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + verifyProjection(aBSpline, aPCurve, aSAS, 0.1); +} + +// Test: Project high-degree B-spline with many control points +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, HighDegreeBSpline_B3) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + + // Create B-spline with 50 control points (sinusoidal shape) + const int aNbPoles = 50; + TColgp_Array1OfPnt aPoles(1, aNbPoles); + for (int i = 1; i <= aNbPoles; ++i) + { + const double aX = (i - 1) * 0.5; + const double aY = 5.0 * sin(aX * M_PI / 10.0); + aPoles(i) = gp_Pnt(aX, aY, 0); + } + + GeomAPI_PointsToBSpline aBuilder(aPoles, 3, 8); + Handle(Geom_BSplineCurve) aBSpline = aBuilder.Curve(); + ASSERT_FALSE(aBSpline.IsNull()); + + Handle(ShapeAnalysis_Surface) aSAS = new ShapeAnalysis_Surface(aPlane); + myProjector->Init(aSAS, myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aBSpline, aBSpline->FirstParameter(), aBSpline->LastParameter(), aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + verifyProjection(aBSpline, aPCurve, aSAS, 0.5); +} + +//================================================================================================== +// Periodic surface tests - sphere, torus +//================================================================================================== + +// Test: Project equator circle on sphere +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, EquatorOnSphere_C1) +{ + const double aRadius = 10.0; + Handle(Geom_SphericalSurface) aSphere = + new Geom_SphericalSurface(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aRadius); + + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aRadius); + Handle(Geom_Circle) aCircle = new Geom_Circle(aCirc); + + myProjector->Init(new ShapeAnalysis_Surface(aSphere), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aCircle, 0.0, 2.0 * M_PI, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; +} + +// Test: Project latitude circle on sphere at 45 degrees, V should be constant +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, LatitudeOnSphere_C2) +{ + const double aRadius = 10.0; + Handle(Geom_SphericalSurface) aSphere = + new Geom_SphericalSurface(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aRadius); + + // Latitude at 45 degrees + const double aLatitude = M_PI / 4.0; + const double aZ = aRadius * sin(aLatitude); + const double aCircRadius = aRadius * cos(aLatitude); + + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, aZ), gp_Dir(0, 0, 1)), aCircRadius); + Handle(Geom_Circle) aCircle = new Geom_Circle(aCirc); + + myProjector->Init(new ShapeAnalysis_Surface(aSphere), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aCircle, 0.0, 2.0 * M_PI, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + // V should be constant for latitude circle + const gp_Pnt2d aStart = aPCurve->Value(0.0); + const gp_Pnt2d aMid = aPCurve->Value(M_PI); + EXPECT_NEAR(aStart.Y(), aMid.Y(), 0.01) << "V should be constant for latitude circle"; +} + +// Test: Project on toroidal surface (both U and V periodic) +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, CircleOnTorus_C3) +{ + const double aMajorRadius = 10.0; + const double aMinorRadius = 2.0; + Handle(Geom_ToroidalSurface) aTorus = + new Geom_ToroidalSurface(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aMajorRadius, aMinorRadius); + + // Circle in the minor cross-section plane at U=0 + gp_Circ aCirc(gp_Ax2(gp_Pnt(aMajorRadius + aMinorRadius, 0, 0), gp_Dir(0, 1, 0)), aMinorRadius); + Handle(Geom_Circle) aCircle = new Geom_Circle(aCirc); + + myProjector->Init(new ShapeAnalysis_Surface(aTorus), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aCircle, 0.0, 2.0 * M_PI, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; +} + +//================================================================================================== +// Conical surface tests +//================================================================================================== + +// Test: Project circle on cone at specific height +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, CircleOnCone_D1) +{ + const double aRadius = 5.0; + const double aSemiAngle = M_PI / 6.0; // 30 degrees + Handle(Geom_ConicalSurface) aCone = + new Geom_ConicalSurface(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aSemiAngle, aRadius); + + // Circle at height Z=5, radius increases with height + const double aZ = 5.0; + const double aRadiusAtZ = aRadius + aZ * tan(aSemiAngle); + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, aZ), gp_Dir(0, 0, 1)), aRadiusAtZ); + Handle(Geom_Circle) aCircle = new Geom_Circle(aCirc); + + myProjector->Init(new ShapeAnalysis_Surface(aCone), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aCircle, 0.0, 2.0 * M_PI, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; +} + +//================================================================================================== +// Trimmed curve tests +//================================================================================================== + +// Test: Project 90-degree arc on plane +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, ArcOnPlane_E1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0); + Handle(Geom_Circle) aFullCircle = new Geom_Circle(aCirc); + Handle(Geom_TrimmedCurve) aArc = new Geom_TrimmedCurve(aFullCircle, 0.0, M_PI / 2.0); + + Handle(ShapeAnalysis_Surface) aSAS = new ShapeAnalysis_Surface(aPlane); + myProjector->Init(aSAS, myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const double aFirst = aArc->FirstParameter(); + const double aLast = aArc->LastParameter(); + const Standard_Boolean aResult = myProjector->Perform(aArc, aFirst, aLast, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + // Arc from (5,0) to (0,5) + const gp_Pnt2d aStart = aPCurve->Value(aFirst); + const gp_Pnt2d aEnd = aPCurve->Value(aLast); + EXPECT_NEAR(aStart.X(), 5.0, 0.01); + EXPECT_NEAR(aStart.Y(), 0.0, 0.01); + EXPECT_NEAR(aEnd.X(), 0.0, 0.01); + EXPECT_NEAR(aEnd.Y(), 5.0, 0.01); +} + +// Test: Project doubly-trimmed curve (tests recursive unwrapping of trimmed curves) +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, DoublyTrimmedCurve_E2) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(Geom_BSplineCurve) aBSpline = createPlanarBSpline(); + ASSERT_FALSE(aBSpline.IsNull()); + + // First trim: take first half + const double aFirst1 = aBSpline->FirstParameter(); + const double aLast1 = aBSpline->LastParameter(); + const double aMid = (aFirst1 + aLast1) / 2.0; + Handle(Geom_TrimmedCurve) aTrim1 = new Geom_TrimmedCurve(aBSpline, aFirst1, aMid); + + // Second trim (nested): take first quarter of the first trim + const double aFirst2 = aTrim1->FirstParameter(); + const double aLast2 = aTrim1->LastParameter(); + const double aQuart = aFirst2 + (aLast2 - aFirst2) / 4.0; + Handle(Geom_TrimmedCurve) aTrim2 = new Geom_TrimmedCurve(aTrim1, aFirst2, aQuart); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aTrim2, aTrim2->FirstParameter(), aTrim2->LastParameter(), aPCurve); + + ASSERT_TRUE(aResult) << "Projection of doubly-trimmed curve should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; +} + +//================================================================================================== +// Ellipse projection tests +//================================================================================================== + +// Test: Project ellipse on plane, verify quadrant points +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, EllipseOnPlane_F1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + + // Ellipse with major radius 10, minor radius 5 + gp_Elips anElips(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0, 5.0); + Handle(Geom_Ellipse) anEllipse = new Geom_Ellipse(anElips); + + Handle(ShapeAnalysis_Surface) aSAS = new ShapeAnalysis_Surface(aPlane); + myProjector->Init(aSAS, myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(anEllipse, 0.0, 2.0 * M_PI, aPCurve); + + ASSERT_TRUE(aResult) << "Projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()) << "PCurve should not be null"; + + // At parameter 0, ellipse is at (10, 0) + gp_Pnt2d aP0 = aPCurve->Value(0.0); + EXPECT_NEAR(aP0.X(), 10.0, 0.01); + EXPECT_NEAR(aP0.Y(), 0.0, 0.01); + + // At parameter PI/2, ellipse is at (0, 5) + gp_Pnt2d aP1 = aPCurve->Value(M_PI / 2.0); + EXPECT_NEAR(aP1.X(), 0.0, 0.01); + EXPECT_NEAR(aP1.Y(), 5.0, 0.01); +} + +//================================================================================================== +// API method tests - Init, SetSurface, SetPrecision +//================================================================================================== + +// Test: Init with Geom_Surface directly (not wrapped in ShapeAnalysis_Surface) +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, InitWithGeomSurface_G1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(Geom_TrimmedCurve) aLine = GC_MakeSegment(gp_Pnt(0, 0, 0), gp_Pnt(10, 10, 0)).Value(); + + myProjector->Init(aPlane, Precision::Confusion()); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aLine, aLine->FirstParameter(), aLine->LastParameter(), aPCurve); + + ASSERT_TRUE(aResult) << "Init with Geom_Surface should work"; + ASSERT_FALSE(aPCurve.IsNull()); +} + +// Test: SetSurface + SetPrecision separately +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, SetSurfaceAndPrecision_G2) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(Geom_TrimmedCurve) aLine = GC_MakeSegment(gp_Pnt(0, 0, 0), gp_Pnt(10, 10, 0)).Value(); + + myProjector->SetSurface(aPlane); + myProjector->SetPrecision(Precision::Confusion()); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aLine, aLine->FirstParameter(), aLine->LastParameter(), aPCurve); + + ASSERT_TRUE(aResult) << "SetSurface + SetPrecision should work"; + ASSERT_FALSE(aPCurve.IsNull()); +} + +// Test: AdjustOverDegenMode accessor returns modifiable reference +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, AdjustOverDegenModeAccessor_G3) +{ + // Default value should be 1 + EXPECT_EQ(myProjector->AdjustOverDegenMode(), 1); + + // Modify through reference + myProjector->AdjustOverDegenMode() = 0; + EXPECT_EQ(myProjector->AdjustOverDegenMode(), 0); + + // Restore + myProjector->AdjustOverDegenMode() = 1; + EXPECT_EQ(myProjector->AdjustOverDegenMode(), 1); +} + +// Test: Status method returns meaningful status after projection +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, StatusMethod_G4) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(Geom_TrimmedCurve) aLine = GC_MakeSegment(gp_Pnt(0, 0, 0), gp_Pnt(10, 10, 0)).Value(); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + myProjector->Perform(aLine, aLine->FirstParameter(), aLine->LastParameter(), aPCurve); + + // Status should indicate OK or DONE + EXPECT_TRUE(myProjector->Status(ShapeExtend_OK) || myProjector->Status(ShapeExtend_DONE)) + << "Status should indicate OK or DONE"; +} + +//================================================================================================== +// Tolerance and precision tests +//================================================================================================== + +// Test: Projection with very tight tolerance on exact curve +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, TightTolerance_H1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(Geom_TrimmedCurve) aLine = GC_MakeSegment(gp_Pnt(0, 0, 0), gp_Pnt(10, 10, 0)).Value(); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), 1e-15); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aLine, aLine->FirstParameter(), aLine->LastParameter(), aPCurve); + + ASSERT_TRUE(aResult) << "Tight tolerance should work for exact curve"; + ASSERT_FALSE(aPCurve.IsNull()); +} + +// Test: Projection with different endpoint tolerances +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, DifferentEndpointTolerances_H2) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + + // Curve slightly off plane at end + TColgp_Array1OfPnt aPoles(1, 3); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(5, 5, 0.05); + aPoles(3) = gp_Pnt(10, 10, 0.1); + + GeomAPI_PointsToBSpline aBuilder(aPoles, 2, 2); + Handle(Geom_BSplineCurve) aBSpline = aBuilder.Curve(); + ASSERT_FALSE(aBSpline.IsNull()); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const double aTolFirst = 0.01; + const double aTolLast = 0.2; + const Standard_Boolean aResult = myProjector->Perform(aBSpline, + aBSpline->FirstParameter(), + aBSpline->LastParameter(), + aPCurve, + aTolFirst, + aTolLast); + + ASSERT_TRUE(aResult) << "Projection with different endpoint tolerances should succeed"; + ASSERT_FALSE(aPCurve.IsNull()); +} + +//================================================================================================== +// Partial curve projection tests +//================================================================================================== + +// Test: Project only first quarter of full circle +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, PartialCircle_I1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0); + Handle(Geom_Circle) aCircle = new Geom_Circle(aCirc); + + Handle(ShapeAnalysis_Surface) aSAS = new ShapeAnalysis_Surface(aPlane); + myProjector->Init(aSAS, myTolerance); + + // Project only 0 to PI/2 (first quadrant) + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aCircle, 0.0, M_PI / 2.0, aPCurve); + + ASSERT_TRUE(aResult) << "Partial projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()); + + // Verify endpoints: (5,0) to (0,5) + gp_Pnt2d aStart = aPCurve->Value(0.0); + gp_Pnt2d aEnd = aPCurve->Value(M_PI / 2.0); + EXPECT_NEAR(aStart.X(), 5.0, 0.01); + EXPECT_NEAR(aStart.Y(), 0.0, 0.01); + EXPECT_NEAR(aEnd.X(), 0.0, 0.01); + EXPECT_NEAR(aEnd.Y(), 5.0, 0.01); +} + +// Test: Project middle portion of B-spline +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, MiddlePortionBSpline_I2) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(Geom_BSplineCurve) aBSpline = createPlanarBSpline(); + ASSERT_FALSE(aBSpline.IsNull()); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), myTolerance); + + const double aFirst = aBSpline->FirstParameter(); + const double aLast = aBSpline->LastParameter(); + const double aRange = aLast - aFirst; + + // Project middle 50% + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aBSpline, aFirst + 0.25 * aRange, aFirst + 0.75 * aRange, aPCurve); + + ASSERT_TRUE(aResult) << "Middle portion projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()); +} + +//================================================================================================== +// Multiple projection and reinitialization tests +//================================================================================================== + +// Test: Multiple projections on same surface reuse setup correctly +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, MultipleProjections_J1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + Handle(ShapeAnalysis_Surface) aSAS = new ShapeAnalysis_Surface(aPlane); + myProjector->Init(aSAS, myTolerance); + + // Project 5 different curves + for (int i = 0; i < 5; ++i) + { + const double aOffset = i * 10.0; + Handle(Geom_TrimmedCurve) aLine = + GC_MakeSegment(gp_Pnt(aOffset, 0, 0), gp_Pnt(aOffset + 5, 5, 0)).Value(); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aLine, aLine->FirstParameter(), aLine->LastParameter(), aPCurve); + + EXPECT_TRUE(aResult) << "Projection " << i << " should succeed"; + EXPECT_FALSE(aPCurve.IsNull()) << "PCurve " << i << " should not be null"; + } +} + +// Test: Surface reinitialization between projections +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, SurfaceReinitialization_J2) +{ + // First surface + Handle(Geom_Plane) aPlane1 = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + myProjector->Init(new ShapeAnalysis_Surface(aPlane1), myTolerance); + + Handle(Geom_TrimmedCurve) aLine1 = GC_MakeSegment(gp_Pnt(0, 0, 0), gp_Pnt(10, 10, 0)).Value(); + Handle(Geom2d_Curve) aPCurve1; + myProjector->Perform(aLine1, aLine1->FirstParameter(), aLine1->LastParameter(), aPCurve1); + ASSERT_FALSE(aPCurve1.IsNull()); + + // Second surface (different plane) + Handle(Geom_Plane) aPlane2 = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 5), gp_Dir(0, 0, 1))); + myProjector->Init(new ShapeAnalysis_Surface(aPlane2), myTolerance); + + Handle(Geom_TrimmedCurve) aLine2 = GC_MakeSegment(gp_Pnt(0, 0, 5), gp_Pnt(10, 10, 5)).Value(); + Handle(Geom2d_Curve) aPCurve2; + myProjector->Perform(aLine2, aLine2->FirstParameter(), aLine2->LastParameter(), aPCurve2); + ASSERT_FALSE(aPCurve2.IsNull()); +} + +//================================================================================================== +// Near-singularity tests (poles, degenerate regions) +//================================================================================================== + +// Test: Project small circle near north pole of sphere +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, NearPoleOnSphere_K1) +{ + const double aRadius = 10.0; + Handle(Geom_SphericalSurface) aSphere = + new Geom_SphericalSurface(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), aRadius); + + // Small circle very close to north pole (latitude = 89.4 degrees) + const double aLatitude = M_PI / 2.0 - 0.01; + const double aZ = aRadius * sin(aLatitude); + const double aCircRadius = aRadius * cos(aLatitude); + + gp_Circ aCirc(gp_Ax2(gp_Pnt(0, 0, aZ), gp_Dir(0, 0, 1)), aCircRadius); + Handle(Geom_Circle) aCircle = new Geom_Circle(aCirc); + + myProjector->Init(new ShapeAnalysis_Surface(aSphere), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aCircle, 0.0, 2.0 * M_PI, aPCurve); + + // Should succeed even near pole + ASSERT_TRUE(aResult) << "Projection near pole should succeed"; + ASSERT_FALSE(aPCurve.IsNull()); +} + +//================================================================================================== +// Edge case tests +//================================================================================================== + +// Test: Very short curve projection should not crash +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, VeryShortCurve_L1) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + + // Very short line segment (1e-6 length) + Handle(Geom_TrimmedCurve) aLine = GC_MakeSegment(gp_Pnt(0, 0, 0), gp_Pnt(1e-6, 0, 0)).Value(); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), 1e-4); + + Handle(Geom2d_Curve) aPCurve; + // Result may vary but should not crash + (void)myProjector->Perform(aLine, aLine->FirstParameter(), aLine->LastParameter(), aPCurve); + + SUCCEED() << "Short curve projection completed without crash"; +} + +// Test: Curve far from surface still projects +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, CurveFarFromSurface_L2) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + + // Line at Z=100, far from plane at Z=0 + Handle(Geom_TrimmedCurve) aLine = GC_MakeSegment(gp_Pnt(0, 0, 100), gp_Pnt(10, 10, 100)).Value(); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), myTolerance); + + Handle(Geom2d_Curve) aPCurve; + (void)myProjector->Perform(aLine, aLine->FirstParameter(), aLine->LastParameter(), aPCurve); + + // Projection should still produce result (it projects onto the surface) + EXPECT_FALSE(aPCurve.IsNull()) << "PCurve should be created even for distant curve"; +} + +// Test: Large tolerance projection +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, LargeTolerance_L3) +{ + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); + + // Line slightly off plane + Handle(Geom_TrimmedCurve) aLine = GC_MakeSegment(gp_Pnt(0, 0, 0.1), gp_Pnt(10, 10, 0.1)).Value(); + + myProjector->Init(new ShapeAnalysis_Surface(aPlane), 1.0); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = + myProjector->Perform(aLine, aLine->FirstParameter(), aLine->LastParameter(), aPCurve); + + ASSERT_TRUE(aResult) << "Large tolerance projection should succeed"; + ASSERT_FALSE(aPCurve.IsNull()); +} + +//================================================================================================== +// Regression tests from bug reports +//================================================================================================== + +// Test: Bug 27569 - B-spline with many knots on B-spline surface +// This test verifies that normal B-spline curves with many knots +// (but uniform spacing) project correctly without triggering false +// positive in knot spacing detection +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, Bug27569_ManyKnotsBSpline_M1) +{ + // Create a B-spline curve with many knots by manually constructing it + // with degree 3 and 15 knots (requiring 15 + 3 + 1 - 2*(3+1) = 11 internal knots) + // For degree 3 with 15 knots (all simple): nbPoles = nbKnots + degree - 1 = 15 + 3 - 1 = 17 + const int aDegree = 3; + const int aNbKnots = 15; + const int aNbPoles = aNbKnots + aDegree - 1; // = 17 + + TColgp_Array1OfPnt aPoles(1, aNbPoles); + for (int i = 1; i <= aNbPoles; ++i) + { + const double t = (i - 1.0) / (aNbPoles - 1.0); + const double aAngle = t * 2.0 * M_PI; + const double aRadius = 50.0 + 20.0 * cos(3.0 * aAngle); + aPoles(i) = gp_Pnt(aRadius * cos(aAngle), -30.0 * t, aRadius * sin(aAngle)); + } + + // Uniform knot vector from 0 to 1 + TColStd_Array1OfReal aKnots(1, aNbKnots); + TColStd_Array1OfInteger aMults(1, aNbKnots); + for (int i = 1; i <= aNbKnots; ++i) + { + aKnots(i) = (i - 1.0) / (aNbKnots - 1.0); + aMults(i) = 1; + } + // Clamp end knots + aMults(1) = aDegree + 1; + aMults(aNbKnots) = aDegree + 1; + + Handle(Geom_BSplineCurve) aBSplineCurve = new Geom_BSplineCurve(aPoles, aKnots, aMults, aDegree); + ASSERT_FALSE(aBSplineCurve.IsNull()) << "B-Spline curve should be created"; + EXPECT_GE(aBSplineCurve->NbKnots(), 10) << "Curve should have at least 10 knots"; + + // Create a simple plane-like surface containing the curve + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pnt(0, -15, 0), gp_Dir(0, 1, 0)); + + // Test projection onto plane + myProjector->Init(aPlane, Precision::Confusion()); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aBSplineCurve, + aBSplineCurve->FirstParameter(), + aBSplineCurve->LastParameter(), + aPCurve); + + EXPECT_TRUE(aResult) << "Projection should succeed for B-spline with many knots"; + EXPECT_FALSE(aPCurve.IsNull()) << "PCurve should be created"; +} + +// Test: Bug 27569 - Actual bug reproduction with high-degree B-spline curve on B-spline surface +// This test uses a B-spline curve of degree 7 with high multiplicities and a section of +// repeated poles (degenerate section), which is the actual problematic geometry from bug27569. +// The curve is projected onto a B-spline surface. +TEST_F(ShapeConstruct_ProjectCurveOnSurfaceTest, Bug27569_HighMultiplicityBSpline_M2) +{ + // Construct a degree 7 B-spline curve with structure similar to bug27569: + // - 7 unique knots with high multiplicities: 15, 14, 14, 14, 14, 14, 15 + // - Total knot count (with mults) = 15 + 14*5 + 15 = 100 + // - Number of poles = 100 - 7 - 1 = 92 (approximately, depends on exact formula) + // For B-spline: sum of multiplicities = nbPoles + degree + 1 + // => nbPoles = sum(mults) - degree - 1 = (15 + 14*5 + 15) - 7 - 1 = 100 - 8 = 92... but file has + // 85 Actually for non-periodic: sum(mults) = nbPoles + degree + 1 + // => 15 + 14 + 14 + 14 + 14 + 14 + 15 = 100 = 85 + 7 + 1 = 93... doesn't match + // Looking at file: 85 poles, degree 7, knots with mults summing to 100 + // Actually file shows: "14 85 7" meaning 14 spans(?), 85 poles, degree 7 + + // Let's construct a simpler problematic case: + // Degree 7 B-spline with a degenerate section (repeated identical poles) + const int aDegree = 7; + const int aNbKnots = 7; + + // High multiplicities similar to bug27569 (adjusted for valid B-spline) + TColStd_Array1OfInteger aMults(1, aNbKnots); + aMults(1) = 8; // degree + 1 for clamped start + aMults(2) = 7; + aMults(3) = 7; + aMults(4) = 7; + aMults(5) = 7; + aMults(6) = 7; + aMults(7) = 8; // degree + 1 for clamped end + // Sum = 8 + 7*5 + 8 = 51 + // nbPoles = sum(mults) - degree - 1 = 51 - 7 - 1 = 43 + + const int aNbPoles = 43; + + // Uniform knot vector + TColStd_Array1OfReal aKnots(1, aNbKnots); + for (int i = 1; i <= aNbKnots; ++i) + { + aKnots(i) = (i - 1); // Knots: 0, 1, 2, 3, 4, 5, 6 + } + + // Create poles with a degenerate section (repeated identical poles in the middle) + TColgp_Array1OfPnt aPoles(1, aNbPoles); + + // First section: smooth curve from start + for (int i = 1; i <= 15; ++i) + { + const double t = (i - 1.0) / 14.0; + const double aX = 144.0 - t * 74.0; // Decreasing from 144 to 70 + const double aY = -t * 0.0002; + const double aZ = 8.3 + t * 51.7; // Increasing from 8.3 to 60 + aPoles(i) = gp_Pnt(aX, aY, aZ); + } + + // Middle section: degenerate (repeated identical poles) - like in bug27569 + for (int i = 16; i <= 28; ++i) + { + aPoles(i) = gp_Pnt(70.0, 0.0, 60.0); // All identical + } + + // Last section: smooth curve to end + for (int i = 29; i <= aNbPoles; ++i) + { + const double t = (i - 29.0) / (aNbPoles - 29.0); + const double aX = 70.0 - t * 80.0; // Decreasing from 70 to -10 + const double aY = -t * 32.0; + const double aZ = 60.0 - t * 60.0; // Decreasing from 60 to 0 + aPoles(i) = gp_Pnt(aX, aY, aZ); + } + + Handle(Geom_BSplineCurve) aBSplineCurve; + try + { + aBSplineCurve = new Geom_BSplineCurve(aPoles, aKnots, aMults, aDegree); + } + catch (Standard_Failure const&) + { + // If construction fails, the test documents the expected behavior + GTEST_SKIP() << "Could not construct B-spline with high multiplicities"; + } + ASSERT_FALSE(aBSplineCurve.IsNull()) << "B-Spline curve should be created"; + + // Create B-spline surface (simplified version of bug27569 surface) + // 8x2 poles B-spline surface + const int aNbUPoles = 8; + const int aNbVPoles = 2; + + TColgp_Array2OfPnt aSurfPoles(1, aNbUPoles, 1, aNbVPoles); + // V=0 row + aSurfPoles(1, 1) = gp_Pnt(70, 0, 60); + aSurfPoles(2, 1) = gp_Pnt(85.01, 0, 60); + aSurfPoles(3, 1) = gp_Pnt(98.87, 0, 55.07); + aSurfPoles(4, 1) = gp_Pnt(111.6, 0, 46.57); + aSurfPoles(5, 1) = gp_Pnt(123.15, 0, 35.68); + aSurfPoles(6, 1) = gp_Pnt(133.46, 0, 23.56); + aSurfPoles(7, 1) = gp_Pnt(142.46, 0, 11.32); + aSurfPoles(8, 1) = gp_Pnt(150, 0, 0); + // V=1 row (same with Y offset) + aSurfPoles(1, 2) = gp_Pnt(54.99, 0, 60); + aSurfPoles(2, 2) = gp_Pnt(41.13, 0, 55.07); + aSurfPoles(3, 2) = gp_Pnt(28.4, 0, 46.57); + aSurfPoles(4, 2) = gp_Pnt(16.85, 0, 35.68); + aSurfPoles(5, 2) = gp_Pnt(6.54, 0, 23.56); + aSurfPoles(6, 2) = gp_Pnt(-2.46, 0, 11.32); + aSurfPoles(7, 2) = gp_Pnt(-10, 0, 0); + aSurfPoles(8, 2) = gp_Pnt(-10, -35.91, 0); + + TColStd_Array1OfReal aUKnots(1, 2); + TColStd_Array1OfReal aVKnots(1, 2); + TColStd_Array1OfInteger aUMults(1, 2); + TColStd_Array1OfInteger aVMults(1, 2); + + aUKnots(1) = 0; + aUKnots(2) = 1; + aVKnots(1) = 0; + aVKnots(2) = 1; + aUMults(1) = 8; + aUMults(2) = 8; + aVMults(1) = 2; + aVMults(2) = 2; + + Handle(Geom_BSplineSurface) aBSplineSurface; + try + { + aBSplineSurface = new Geom_BSplineSurface(aSurfPoles, aUKnots, aVKnots, aUMults, aVMults, 7, 1); + } + catch (Standard_Failure const&) + { + // Fall back to a simple plane if surface construction fails + Handle(Geom_Plane) aPlane = new Geom_Plane(gp_Pnt(70, 0, 30), gp_Dir(0, 1, 0)); + myProjector->Init(aPlane, Precision::Confusion()); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aBSplineCurve, + aBSplineCurve->FirstParameter(), + aBSplineCurve->LastParameter(), + aPCurve); + + // This test documents expected behavior - projection should succeed + EXPECT_TRUE(aResult) << "Projection should succeed"; + EXPECT_FALSE(aPCurve.IsNull()) << "PCurve should be created"; + return; + } + + myProjector->Init(aBSplineSurface, Precision::Confusion()); + + Handle(Geom2d_Curve) aPCurve; + const Standard_Boolean aResult = myProjector->Perform(aBSplineCurve, + aBSplineCurve->FirstParameter(), + aBSplineCurve->LastParameter(), + aPCurve); + + // This is the key assertion - the projection SHOULD succeed but currently may fail + // for curves with degenerate sections (repeated poles) + EXPECT_TRUE(aResult) << "Projection should succeed for B-spline with degenerate section"; + EXPECT_FALSE(aPCurve.IsNull()) << "PCurve should be created"; +} diff --git a/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.cxx b/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.cxx index 04a90d5848..d88ee1aaa5 100644 --- a/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.cxx +++ b/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.cxx @@ -11,26 +11,10 @@ // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. -//: k1 abv 16.12.98 K4L PRO10107, PRO10108, PRO10109 -//: j8 abv 10.12.98 TR10 r0501_db.stp #9423 -//: S4030 abv, pdn: new methods - interface to standard ProjLib_CompProjectedCurve -//%12 pdn 15.02.99 PRO9234 optimizing -//%12 pdn 15.02.99 PRO9234 using improved ProjectDegenerated method -// rln 03.03.99 S4135: bm2_sd_t4-A.stp treatment of Geom_SphericalSurface together with V-closed -// surfaces -//: p9 abv 11.03.99 PRO7226 #489490: make IsAnIsoparametric to find nearest case -//: q1 abv 15.03.99 (pdn) PRO7226 #525030: limit NextValueOfUV() by tolerance -//: q5 abv 19.03.99 code improvement -//: q9 abv 23.03.99 PRO7226.stp #489490: cache for projecting end points -// #78 rln 12.03.99 S4135: checking spatial closure with myPreci -// pdn 12.03.99 S4135: creating pcurve with minimal length in the case of densed points -// abv 29.03.99 IsAnIsoparametric with Precision::Confusion -// pdn 09.04.99 IsAnisoparametric uses already computed parameters (S4030, fix PRO14323) -// szv#4 S4163 -//: s5 abv 22.04.99 Adding debug printouts in catch {} blocks -// #1 svv 11.01.00 Porting on DEC +#include #include +#include #include #include #include @@ -42,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -56,487 +42,263 @@ #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 + IMPLEMENT_STANDARD_RTTIEXT(ShapeConstruct_ProjectCurveOnSurface, Standard_Transient) -#define NCONTROL 23 +namespace +{ +//! Default number of control points for discretization. +constexpr Standard_Integer THE_NCONTROL = 23; -static void AdjustSecondPointToFirstPoint(const gp_Pnt2d& theFirstPoint, - gp_Pnt2d& theSecondPoint, - const Handle(Geom_Surface)& theSurf) +//! Utility class for projecting points onto a surface with B-spline corner cache optimization. +//! For clamped B-spline surfaces, caches corner pole positions and their exact UV parameters +//! to avoid expensive ValueOfUV calls when projected points coincide with surface corners. +//! Only corner poles are cached because B-spline surfaces pass through corners only +//! (when end multiplicities equal degree + 1). +class SurfaceProjectorWithCache +{ +public: + //! Constructor - initializes the projector with a surface. + //! For B-spline surfaces, builds the corner cache automatically. + //! @param[in] theSurf the surface analysis object + SurfaceProjectorWithCache(const Handle(ShapeAnalysis_Surface)& theSurf) + : mySurf(theSurf) + { + if (theSurf.IsNull()) + return; + + Handle(Geom_BSplineSurface) aBSplineSurf = + Handle(Geom_BSplineSurface)::DownCast(theSurf->Surface()); + if (!aBSplineSurf.IsNull()) + { + buildCornerCache(aBSplineSurf); + } + } + + //! Projects a 3D point onto the surface. + //! First checks B-spline corner cache, then falls back to ValueOfUV. + //! @param[in] thePoint the 3D point to project + //! @param[in] theTol the tolerance for projection + //! @param[in] theTolSq squared tolerance for corner matching + //! @return the UV coordinates on the surface + gp_Pnt2d ValueOfUV(const gp_Pnt& thePoint, + const Standard_Real theTol, + const Standard_Real theTolSq) const + { + gp_Pnt2d aResult; + if (findInCornerCache(thePoint, theTolSq, aResult)) + { + // Corner UV is exact, but refine for numerical stability + return mySurf->NextValueOfUV(aResult, thePoint, theTol, theTol); + } + return mySurf->ValueOfUV(thePoint, theTol); + } + + //! Projects a 3D point onto the surface using a hint from previous projection. + //! First checks B-spline corner cache, then falls back to NextValueOfUV. + //! @param[in] theHint the UV hint from previous projection + //! @param[in] thePoint the 3D point to project + //! @param[in] theTol the tolerance for projection + //! @param[in] theTolSq squared tolerance for corner matching + //! @param[in] theStep the step tolerance + //! @return the UV coordinates on the surface + gp_Pnt2d NextValueOfUV(const gp_Pnt2d& theHint, + const gp_Pnt& thePoint, + const Standard_Real theTol, + const Standard_Real theTolSq, + const Standard_Real theStep) const + { + gp_Pnt2d aResult; + if (findInCornerCache(thePoint, theTolSq, aResult)) + { + // Corner UV is exact, but refine for numerical stability + return mySurf->NextValueOfUV(aResult, thePoint, theTol, theTol); + } + return mySurf->NextValueOfUV(theHint, thePoint, theTol, theStep); + } + + //! Returns the gap from the last projection + Standard_Real Gap() const { return mySurf->Gap(); } + +private: + //! Builds cache from B-spline surface corner poles. + //! For clamped B-splines (multiplicity = degree + 1 at ends), the surface passes + //! exactly through corner poles, so we can use their UV parameters directly. + //! @param[in] theSurface the B-spline surface + void buildCornerCache(const Handle(Geom_BSplineSurface)& theSurface) + { + const Standard_Integer aNbPolesU = theSurface->NbUPoles(); + const Standard_Integer aNbPolesV = theSurface->NbVPoles(); + const Standard_Integer aDegreeU = theSurface->UDegree(); + const Standard_Integer aDegreeV = theSurface->VDegree(); + + // Check if surface is clamped (end multiplicities = degree + 1) + const Standard_Integer aFirstUMult = theSurface->UMultiplicity(1); + const Standard_Integer aLastUMult = theSurface->UMultiplicity(theSurface->NbUKnots()); + const Standard_Integer aFirstVMult = theSurface->VMultiplicity(1); + const Standard_Integer aLastVMult = theSurface->VMultiplicity(theSurface->NbVKnots()); + + const Standard_Boolean isUFirstClamped = (aFirstUMult >= aDegreeU + 1); + const Standard_Boolean isULastClamped = (aLastUMult >= aDegreeU + 1); + const Standard_Boolean isVFirstClamped = (aFirstVMult >= aDegreeV + 1); + const Standard_Boolean isVLastClamped = (aLastVMult >= aDegreeV + 1); + + // Get parameter bounds + const Standard_Real aUFirst = theSurface->UKnot(1); + const Standard_Real aULast = theSurface->UKnot(theSurface->NbUKnots()); + const Standard_Real aVFirst = theSurface->VKnot(1); + const Standard_Real aVLast = theSurface->VKnot(theSurface->NbVKnots()); + + // Cache corner poles where surface passes through them + // Corner (1, 1) - UFirst, VFirst + if (isUFirstClamped && isVFirstClamped) + { + myCorners3d.Append(theSurface->Pole(1, 1)); + myCorners2d.Append(gp_Pnt2d(aUFirst, aVFirst)); + } + + // Corner (NbPolesU, 1) - ULast, VFirst + if (isULastClamped && isVFirstClamped) + { + myCorners3d.Append(theSurface->Pole(aNbPolesU, 1)); + myCorners2d.Append(gp_Pnt2d(aULast, aVFirst)); + } + + // Corner (1, NbPolesV) - UFirst, VLast + if (isUFirstClamped && isVLastClamped) + { + myCorners3d.Append(theSurface->Pole(1, aNbPolesV)); + myCorners2d.Append(gp_Pnt2d(aUFirst, aVLast)); + } + + // Corner (NbPolesU, NbPolesV) - ULast, VLast + if (isULastClamped && isVLastClamped) + { + myCorners3d.Append(theSurface->Pole(aNbPolesU, aNbPolesV)); + myCorners2d.Append(gp_Pnt2d(aULast, aVLast)); + } + } + + //! Finds the closest corner to a 3D point within tolerance. + //! @param[in] thePoint the 3D point to find + //! @param[in] theTolSq squared tolerance for matching + //! @param[out] theUV the UV parameter if found + //! @return true if a matching corner was found + Standard_Boolean findInCornerCache(const gp_Pnt& thePoint, + const Standard_Real theTolSq, + gp_Pnt2d& theUV) const + { + for (Standard_Integer i = 0; i < myCorners3d.Length(); ++i) + { + if (myCorners3d(i).SquareDistance(thePoint) < theTolSq) + { + theUV = myCorners2d(i); + return Standard_True; + } + } + return Standard_False; + } + +private: + Handle(ShapeAnalysis_Surface) mySurf; //!< Surface to project on + NCollection_Vector myCorners3d; //!< 3D positions of B-spline surface corners + NCollection_Vector myCorners2d; //!< UV parameters of B-spline surface corners +}; + +//================================================================================================= + +//! Extracts B-spline curve from a possibly nested trimmed curve. +//! Recursively unwraps trimmed curves to find B-spline basis. +//! @param[in] theCurve the curve to extract from +//! @return the extracted B-spline curve, or null if not a B-spline +Handle(Geom_BSplineCurve) extractBSplineCurve(const Handle(Geom_Curve)& theCurve) +{ + Handle(Geom_Curve) aCurve = theCurve; + + // Recursively unwrap trimmed curves + while (!aCurve.IsNull() && aCurve->IsKind(STANDARD_TYPE(Geom_TrimmedCurve))) + { + aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve(); + } + + return Handle(Geom_BSplineCurve)::DownCast(aCurve); +} + +//================================================================================================= + +//! Adjusts second point to first point considering surface periodicity. +//! For periodic surfaces, adjusts the second point coordinates to be within +//! half-period of the first point. +//! @param[in] theFirstPoint the reference point +//! @param[in,out] theSecondPoint the point to adjust +//! @param[in] theSurf the surface to check for periodicity +void adjustSecondToFirstPoint(const gp_Pnt2d& theFirstPoint, + gp_Pnt2d& theSecondPoint, + const Handle(Geom_Surface)& theSurf) { if (theSurf->IsUPeriodic()) { - Standard_Real UPeriod = theSurf->UPeriod(); - Standard_Real NewU = ElCLib::InPeriod(theSecondPoint.X(), - theFirstPoint.X() - UPeriod / 2, - theFirstPoint.X() + UPeriod / 2); - theSecondPoint.SetX(NewU); + const Standard_Real anUPeriod = theSurf->UPeriod(); + const Standard_Real aNewU = ElCLib::InPeriod(theSecondPoint.X(), + theFirstPoint.X() - anUPeriod / 2, + theFirstPoint.X() + anUPeriod / 2); + theSecondPoint.SetX(aNewU); } if (theSurf->IsVPeriodic()) { - Standard_Real VPeriod = theSurf->VPeriod(); - Standard_Real NewV = ElCLib::InPeriod(theSecondPoint.Y(), - theFirstPoint.Y() - VPeriod / 2, - theFirstPoint.Y() + VPeriod / 2); - theSecondPoint.SetY(NewV); + const Standard_Real aVPeriod = theSurf->VPeriod(); + const Standard_Real aNewV = ElCLib::InPeriod(theSecondPoint.Y(), + theFirstPoint.Y() - aVPeriod / 2, + theFirstPoint.Y() + aVPeriod / 2); + theSecondPoint.SetY(aNewV); } } //================================================================================================= -ShapeConstruct_ProjectCurveOnSurface::ShapeConstruct_ProjectCurveOnSurface() -{ - myPreci = Precision::Confusion(); - myBuild = Standard_False; - myAdjustOverDegen = 1; //: c0 //szv#4:S4163:12Mar99 was boolean - myNbCashe = 0; //: q9 - myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK); -} - -//================================================================================================= - -void ShapeConstruct_ProjectCurveOnSurface::Init(const Handle(Geom_Surface)& surf, - const Standard_Real preci) -{ - Init(new ShapeAnalysis_Surface(surf), preci); -} - -//================================================================================================= - -void ShapeConstruct_ProjectCurveOnSurface::Init(const Handle(ShapeAnalysis_Surface)& surf, - const Standard_Real preci) -{ - SetSurface(surf); - SetPrecision(preci); -} - -//================================================================================================= - -void ShapeConstruct_ProjectCurveOnSurface::SetSurface(const Handle(Geom_Surface)& surf) -{ - SetSurface(new ShapeAnalysis_Surface(surf)); -} - -//================================================================================================= - -void ShapeConstruct_ProjectCurveOnSurface::SetSurface(const Handle(ShapeAnalysis_Surface)& surf) -{ - if (mySurf == surf) - return; - mySurf = surf; - myNbCashe = 0; //: q9 -} - -//================================================================================================= - -void ShapeConstruct_ProjectCurveOnSurface::SetPrecision(const Standard_Real preci) -{ - myPreci = preci; -} - -//================================================================================================= - -Standard_Boolean& ShapeConstruct_ProjectCurveOnSurface::BuildCurveMode() -{ - return myBuild; -} - -//================================================================================================= - -//: c0 - -// szv#4:S4163:12Mar99 was Boolean -Standard_Integer& ShapeConstruct_ProjectCurveOnSurface::AdjustOverDegenMode() -{ - return myAdjustOverDegen; -} - -//================================================================================================= - -Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::Status( - const ShapeExtend_Status theStatus) const -{ - return ShapeExtend::DecodeStatus(myStatus, theStatus); -} - -//================================================================================================= - -Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::Perform(Handle(Geom_Curve)& c3d, - const Standard_Real First, - const Standard_Real Last, - Handle(Geom2d_Curve)& c2d, - const Standard_Real TolFirst, - const Standard_Real TolLast) -{ - myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK); - // Standard_Boolean OK = Standard_True; //szv#4:S4163:12Mar99 not needed - - if (mySurf.IsNull()) - { - c2d.Nullify(); - myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL1); - return Standard_False; - } - // Projection Analytique - Handle(Geom_Curve) crv3dtrim = c3d; - if (!c3d->IsKind(STANDARD_TYPE(Geom_BoundedCurve))) - crv3dtrim = new Geom_TrimmedCurve(c3d, First, Last); - c2d = ProjectAnalytic(crv3dtrim); - if (!c2d.IsNull()) - { - myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE1); - return Standard_True; - } - - // Projection par approximation - - // discretize the 3d curve - - Standard_Integer nbrPnt; - - // $$$$ :92 abv 28 Jan 98 see PRO10107, big BSplineCurve C0 - Standard_Integer nbPini = NCONTROL; // as in BRepCheck_Edge (RLN/Nijni) - // 20; // number of points for interpolation, should be "parametric dependent" - - //: 92 abv 28 Jan 98: if curve is BSpline with many intervals, - // increase number of points to provide at least Degree()+1 points per interval - Handle(Geom_BSplineCurve) bspl; - if (c3d->IsKind(STANDARD_TYPE(Geom_TrimmedCurve))) - { - Handle(Geom_TrimmedCurve) ctrim = Handle(Geom_TrimmedCurve)::DownCast(c3d); - bspl = Handle(Geom_BSplineCurve)::DownCast(ctrim->BasisCurve()); - } - else - bspl = Handle(Geom_BSplineCurve)::DownCast(c3d); - if (!bspl.IsNull()) - { - Standard_Integer nint = 0; - for (Standard_Integer i = 1; i < bspl->NbKnots(); i++) - { - if (bspl->Knot(i + 1) > First && bspl->Knot(i) < Last) - nint++; - } - Standard_Integer minPnt = nint * (bspl->Degree() + 1); - while (nbPini < minPnt) - nbPini += NCONTROL - 1; -#ifdef OCCT_DEBUG - if (nbPini > NCONTROL) - std::cout << "Warning: number of points for projecting is " << nbPini << std::endl; -#endif - } - - // $$$$ end :92 (big BSplineCurve C0) - - // this number should be "parametric dependent" - TColgp_SequenceOfPnt points; - TColStd_SequenceOfReal params; - NCollection_Sequence aKnotCoeffs; - gp_Pnt p3d; - Standard_Integer iPnt; - - // In case of bspline compute parametrization speed on each - // knot interval inside [aFirstParam, aLastParam]. - // If quotient = (MaxSpeed / MinSpeed) >= aMaxQuotientCoeff then - // use PerformByProjLib algorithm. - if (!bspl.IsNull()) - { - Standard_Real aFirstParam = First; // First parameter of current interval. - Standard_Real aLastParam = Last; // Last parameter of current interval. - - // First index computation. - Standard_Integer anIdx = 1; - for (; anIdx <= bspl->NbKnots() && aFirstParam < Last; anIdx++) - { - if (bspl->Knot(anIdx) > First) - { - break; - } - } - - GeomAdaptor_Curve aC3DAdaptor(c3d); - Standard_Real aMinParSpeed = Precision::Infinite(); // Minimal parameterization speed. - for (; anIdx <= bspl->NbKnots() && aFirstParam < Last; anIdx++) - { - // Fill current knot interval. - aLastParam = std::min(Last, bspl->Knot(anIdx)); - Standard_Integer aNbIntPnts = NCONTROL; - // Number of inner points is adapted according to the length of the interval - // to avoid a lot of calculations on small range of parameters. - if (anIdx > 1) - { - const Standard_Real aLenThres = 1.e-2; - const Standard_Real aLenRatio = - (aLastParam - aFirstParam) / (bspl->Knot(anIdx) - bspl->Knot(anIdx - 1)); - if (aLenRatio < aLenThres) - { - aNbIntPnts = Standard_Integer(aLenRatio / aLenThres * aNbIntPnts); - if (aNbIntPnts < 2) - aNbIntPnts = 2; - } - } - Standard_Real aStep = (aLastParam - aFirstParam) / (aNbIntPnts - 1); - Standard_Integer anIntIdx; - gp_Pnt p3d1, p3d2; - // Start filling from first point. - aC3DAdaptor.D0(aFirstParam, p3d1); - - Standard_Real aLength3d = 0.0; - for (anIntIdx = 1; anIntIdx < aNbIntPnts; anIntIdx++) - { - Standard_Real aParam = aFirstParam + aStep * anIntIdx; - aC3DAdaptor.D0(aParam, p3d2); - const Standard_Real aDist = p3d2.Distance(p3d1); - - aLength3d += aDist; - p3d1 = p3d2; - - aMinParSpeed = std::min(aMinParSpeed, aDist / aStep); - } - const Standard_Real aCoeff = aLength3d / (aLastParam - aFirstParam); - if (std::abs(aCoeff) > gp::Resolution()) - aKnotCoeffs.Append(aCoeff); - aFirstParam = aLastParam; - } - - Standard_Real anEvenlyCoeff = 0; - if (aKnotCoeffs.Size() > 0) - { - anEvenlyCoeff = *std::max_element(aKnotCoeffs.begin(), aKnotCoeffs.end()) - / *std::min_element(aKnotCoeffs.begin(), aKnotCoeffs.end()); - } - - const Standard_Real aMaxQuotientCoeff = 1500.0; - if (anEvenlyCoeff > aMaxQuotientCoeff && aMinParSpeed > Precision::Confusion()) - { - PerformByProjLib(c3d, First, Last, c2d); - // PerformByProjLib fail detection: - if (!c2d.IsNull()) - { - return Status(ShapeExtend_DONE); - } - } - } - - Standard_Real deltaT, t; - deltaT = (Last - First) / (nbPini - 1); - nbrPnt = nbPini; - for (iPnt = 1; iPnt <= nbPini; iPnt++) - { - if (iPnt == 1) - t = First; - else if (iPnt == nbPini) - t = Last; - else - t = First + (iPnt - 1) * deltaT; - - c3d->D0(t, p3d); - points.Append(p3d); - params.Append(t); - } - - // CALCUL par approximation - TColgp_SequenceOfPnt2d pnt2d; - ApproxPCurve(nbrPnt, c3d, TolFirst, TolLast, points, params, pnt2d, c2d); // szv#4:S4163:12Mar99 - // OK not needed - nbPini = points.Length(); - if (!c2d.IsNull()) - { - myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE2); - return Standard_True; - } // cas particulier d iso - - // INTERPOLATION du resultat - - if (myBuild) - { - Handle(TColgp_HArray1OfPnt) thePnts = new TColgp_HArray1OfPnt(1, nbPini); - Handle(TColStd_HArray1OfReal) theParams = new TColStd_HArray1OfReal(1, nbPini); - for (iPnt = 1; iPnt <= nbPini; iPnt++) - { - thePnts->SetValue(iPnt, points(iPnt)); - theParams->SetValue(iPnt, params(iPnt)); - } - - Handle(Geom_Curve) newc3d = InterpolateCurve3d(nbPini, thePnts, theParams, c3d); - if (newc3d.IsNull()) - myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL2); - else - { - myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE3); - c3d = newc3d; - } - } - - Handle(TColgp_HArray1OfPnt2d) thePnts2d = new TColgp_HArray1OfPnt2d(1, nbPini); - Handle(TColStd_HArray1OfReal) theParams2d = new TColStd_HArray1OfReal(1, nbPini); - for (iPnt = 1; iPnt <= nbPini; iPnt++) - { - theParams2d->SetValue(iPnt, params(iPnt)); - thePnts2d->SetValue(iPnt, pnt2d(iPnt)); - } - c2d = InterpolatePCurve(nbPini, thePnts2d, theParams2d, c3d); - - // Faut-il aussi reprendre la C3D ? - myStatus |= ShapeExtend::EncodeStatus(c2d.IsNull() ? ShapeExtend_FAIL1 : ShapeExtend_DONE2); - return Status(ShapeExtend_DONE); -} - -//================================================================================================= - -Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::PerformByProjLib( - Handle(Geom_Curve)& c3d, - const Standard_Real First, - const Standard_Real Last, - Handle(Geom2d_Curve)& c2d, - const GeomAbs_Shape /*continuity*/, - const Standard_Integer /*maxdeg */, - const Standard_Integer /*nbinterval */) -{ - // Standard_Boolean OK = Standard_True; //szv#4:S4163:12Mar99 unused - c2d.Nullify(); - if (mySurf.IsNull()) - { - myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL1); - return Standard_False; - } - - try - { - OCC_CATCH_SIGNALS - Handle(GeomAdaptor_Surface) GAS = mySurf->Adaptor3d(); - Handle(GeomAdaptor_Curve) GAC = new GeomAdaptor_Curve(c3d, First, Last); - ProjLib_ProjectedCurve Projector(GAS, GAC); - - switch (Projector.GetType()) - { - case GeomAbs_Line: - c2d = new Geom2d_Line(Projector.Line()); - break; - case GeomAbs_Circle: - c2d = new Geom2d_Circle(Projector.Circle()); - break; - case GeomAbs_Ellipse: - c2d = new Geom2d_Ellipse(Projector.Ellipse()); - break; - case GeomAbs_Parabola: - c2d = new Geom2d_Parabola(Projector.Parabola()); - break; - case GeomAbs_Hyperbola: - c2d = new Geom2d_Hyperbola(Projector.Hyperbola()); - break; - case GeomAbs_BSplineCurve: - c2d = Projector.BSpline(); - break; - default: - // Not possible, handling added to avoid gcc warning. - break; - } - - if (c2d.IsNull()) - { - myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL2); - return Standard_False; - } - else - { - myStatus = ShapeExtend::EncodeStatus(ShapeExtend_DONE1); - return Standard_True; - } - } - catch (Standard_Failure const& anException) - { -#ifdef OCCT_DEBUG - std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::PerformByProjLib(): Exception: "; - anException.Print(std::cout); - std::cout << std::endl; -#endif - (void)anException; - myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL3); - c2d.Nullify(); - } - return Standard_False; -} - -//================================================================================================= - -Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::ProjectAnalytic( - const Handle(Geom_Curve)& c3d) const -{ - Handle(Geom2d_Curve) result; - - //: k1 abv 16 Dec 98: limit analytic cases by Plane surfaces only - // This is necessary for K4L since it fails on other surfaces - // when general method GeomProjLib::Curve2d() is used - // Projection is done as in BRep_Tool and BRepCheck_Edge - Handle(Geom_Surface) surf = mySurf->Surface(); - Handle(Geom_Plane) Plane = Handle(Geom_Plane)::DownCast(surf); - if (Plane.IsNull()) - { - Handle(Geom_RectangularTrimmedSurface) RTS = - Handle(Geom_RectangularTrimmedSurface)::DownCast(surf); - if (!RTS.IsNull()) - Plane = Handle(Geom_Plane)::DownCast(RTS->BasisSurface()); - else - { - Handle(Geom_OffsetSurface) OS = Handle(Geom_OffsetSurface)::DownCast(surf); - if (!OS.IsNull()) - Plane = Handle(Geom_Plane)::DownCast(OS->BasisSurface()); - } - } - if (!Plane.IsNull()) - { - Handle(Geom_Curve) ProjOnPlane = - GeomProjLib::ProjectOnPlane(c3d, Plane, Plane->Position().Direction(), Standard_True); - Handle(GeomAdaptor_Curve) HC = new GeomAdaptor_Curve(ProjOnPlane); - ProjLib_ProjectedCurve Proj(mySurf->Adaptor3d(), HC); - - result = Geom2dAdaptor::MakeCurve(Proj); - if (result.IsNull()) - return result; - if (result->IsKind(STANDARD_TYPE(Geom2d_TrimmedCurve))) - { - Handle(Geom2d_TrimmedCurve) TC = Handle(Geom2d_TrimmedCurve)::DownCast(result); - result = TC->BasisCurve(); - } - - return result; - } - - return result; -} - -//! Fix possible period jump and handle walking period parameter. -static Standard_Boolean fixPeriodictyTroubles( - gp_Pnt2d* thePnt, // pointer to gp_Pnt2d[4] beginning - Standard_Integer theIdx, // Index of objective coord: 1 ~ X, 2 ~ Y - Standard_Real thePeriod, // Period on objective coord - Standard_Integer theSavedPoint, // Point number to choose period - Standard_Real theSavedParam) // Param from cache to choose period +//! Fixes possible period jumps in an array of 4 points. +//! Handles the walking period parameter to ensure smooth transition +//! across periodic boundaries. +//! @param[in,out] thePnt array of 4 points to fix +//! @param[in] theIdx coordinate index (1 for U, 2 for V) +//! @param[in] thePeriod the period value +//! @param[in] theSavedPoint index of the reference point (-1 for none) +//! @param[in] theSavedParam the saved parameter value +//! @return true if a period jump was detected and fixed +Standard_Boolean fixPeriodicityTroubles(gp_Pnt2d* thePnt, + const Standard_Integer theIdx, + const Standard_Real thePeriod, + const Standard_Integer theSavedPoint, + const Standard_Real theSavedParam) { Standard_Real aSavedParam; Standard_Integer aSavedPoint; Standard_Real aMinParam = 0.0, aMaxParam = thePeriod; + if (theSavedPoint < 0) { - // normalize to first period by default aSavedParam = 0.5 * thePeriod; aSavedPoint = 0; } @@ -564,13 +326,14 @@ static Standard_Boolean fixPeriodictyTroubles( aFixIsoParam = aSavedParam; isIsoLine = Standard_True; } - // normalize all coordinates to [aMinParam, aMaxParam) + + // Normalize all coordinates to [aMinParam, aMaxParam) for (Standard_Integer i = 0; i < 4; i++) { Standard_Real aParam = thePnt[i].Coord(theIdx); Standard_Real aShift = ShapeAnalysis::AdjustToPeriod(aParam, aMinParam, aMaxParam); aParam += aShift; - // Walk over period coord -> not walking on another isoline in parameter space. + if (isIsoLine) { if (aMaxParam - aParam < Precision::PConfusion() @@ -588,7 +351,7 @@ static Standard_Boolean fixPeriodictyTroubles( thePnt[i].SetCoord(theIdx, aParam); } - // find possible period jump and increasing or decreasing coordinates vector + // Find possible period jump and increasing or decreasing coordinates vector Standard_Boolean isJump = Standard_False; Standard_Real aPrevDiff = 0.0; Standard_Real aSumDiff = 1.0; @@ -599,7 +362,6 @@ static Standard_Boolean fixPeriodictyTroubles( { aSumDiff *= -1.0; } - // if first derivative changes its sign then period jump may exists in this place if (aDiff * aPrevDiff < -Precision::PConfusion()) { isJump = Standard_True; @@ -611,7 +373,7 @@ static Standard_Boolean fixPeriodictyTroubles( return Standard_False; if (aSumDiff > 0) - { // decreasing sequence (parameters decrease twice(--) and one period jump(+)) + { for (Standard_Integer i = aSavedPoint; i > 0; i--) if (thePnt[i].Coord(theIdx) > thePnt[i - 1].Coord(theIdx)) { @@ -624,7 +386,7 @@ static Standard_Boolean fixPeriodictyTroubles( } } else - { // increasing sequence (parameters increase twice(++) and one period jump(-)) + { for (Standard_Integer i = aSavedPoint; i > 0; i--) if (thePnt[i].Coord(theIdx) < thePnt[i - 1].Coord(theIdx)) { @@ -637,148 +399,757 @@ static Standard_Boolean fixPeriodictyTroubles( } } - // Do not return false, because for nonlinear 2d curves vector of parameters - // may change its first derivative and shifted parameters will be broken for this case. return Standard_True; } //================================================================================================= -Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::getLine( - const TColgp_SequenceOfPnt& thepoints, - const TColStd_SequenceOfReal& theparams, - TColgp_SequenceOfPnt2d& thePnt2ds, - Standard_Real theTol, - Standard_Boolean& isRecompute, - Standard_Boolean& isFromCashe) const +//! Checks if B-spline curve has problematic knot spacing that could cause issues. +//! Detects cases where knot intervals are extremely small relative to the median +//! interval or the working parameter range, or where the curve has degenerate sections. +//! @param[in] theCurve the curve to check (may be trimmed) +//! @param[in] theFirst the first parameter of the working range +//! @param[in] theLast the last parameter of the working range +//! @param[out] theBSpline the extracted B-spline curve (if any) +//! @return true if the curve has problematic characteristics +Standard_Boolean isBSplineCurveInvalid(const Handle(Geom_Curve)& theCurve, + const Standard_Real theFirst, + const Standard_Real theLast, + Handle(Geom_BSplineCurve)& theBSpline) { - Standard_Integer nb = thepoints.Length(); - gp_Pnt aP[4]; - aP[0] = thepoints(1); - aP[1] = thepoints(2); - aP[2] = thepoints(nb - 1); - aP[3] = thepoints(nb); - gp_Pnt2d aP2d[4]; - Standard_Integer i = 0; + theBSpline = extractBSplineCurve(theCurve); + if (theBSpline.IsNull()) + return Standard_False; - Standard_Real aTol2 = theTol * theTol; - Standard_Boolean isPeriodicU = mySurf->Surface()->IsUPeriodic(); - Standard_Boolean isPeriodicV = mySurf->Surface()->IsVPeriodic(); - - // Workaround: - // Protection against bad "tolerance" shapes. - if (aTol2 > 1.0) + try { - theTol = Precision::Confusion(); - aTol2 = theTol * theTol; - } - if (aTol2 < Precision::SquareConfusion()) - aTol2 = Precision::SquareConfusion(); - Standard_Real anOldTol2 = aTol2; - // auxiliary variables to choose period for connection with previous 2dcurve (if exist) - Standard_Integer aSavedPointNum = -1; - gp_Pnt2d aSavedPoint; + OCC_CATCH_SIGNALS - // project first and last points - for (; i < 4; i += 3) - { - Standard_Integer j; - for (j = 0; j < myNbCashe; j++) + const TColStd_Array1OfReal& aKnots = theBSpline->Knots(); + if (aKnots.Length() < 10) + return Standard_False; + + NCollection_Vector anIntervals; + for (Standard_Integer i = aKnots.Lower() + 1; i <= aKnots.Upper(); i++) { - if (myCashe3d[j].SquareDistance(aP[i]) < aTol2) + const Standard_Real anInterval = aKnots(i) - aKnots(i - 1); + if (anInterval > Precision::Confusion()) { - aP2d[i] = mySurf->NextValueOfUV(myCashe2d[j], aP[i], theTol, theTol); - aSavedPointNum = i; - aSavedPoint = myCashe2d[j]; - if (i == 0) - isFromCashe = Standard_True; + anIntervals.Append(anInterval); + } + } + + if (anIntervals.IsEmpty()) + return Standard_True; + + std::sort(anIntervals.begin(), anIntervals.end()); + + Standard_Real aMedianInterval; + const Standard_Integer aMid = anIntervals.Length() / 2; + if (anIntervals.Length() % 2 == 0) + { + aMedianInterval = (anIntervals[aMid - 1] + anIntervals[aMid]) / 2.0; + } + else + { + aMedianInterval = anIntervals[aMid]; + } + + Standard_Real aTolerance = aMedianInterval * 1e-3; + aTolerance = Max(aTolerance, Precision::Confusion() * 100); + + for (Standard_Integer i = aKnots.Lower() + 1; i <= aKnots.Upper(); i++) + { + const Standard_Real anInterval = aKnots(i) - aKnots(i - 1); + if (anInterval > Precision::Confusion() && anInterval < aTolerance) + { + return Standard_True; + } + } + + const Standard_Real aWorkingRange = theLast - theFirst; + if (aWorkingRange > Precision::Confusion()) + { + const Standard_Real aRelativeTolerance = aWorkingRange * 1e-6; + for (Standard_Integer i = aKnots.Lower() + 1; i <= aKnots.Upper(); i++) + { + const Standard_Real anInterval = aKnots(i) - aKnots(i - 1); + if (aKnots(i - 1) >= theFirst - Precision::Confusion() + && aKnots(i) <= theLast + Precision::Confusion() && anInterval > Precision::Confusion() + && anInterval < aRelativeTolerance) + { + return Standard_True; + } + } + } + + return Standard_False; + } + catch (Standard_Failure const&) + { + return Standard_True; + } +} + +//================================================================================================= + +//! Rebuilds B-spline curve by adjusting problematic knot spacing while preserving poles. +//! Only modifies the knot vector to fix degenerate parametrization; the curve geometry +//! (poles, weights, degree, multiplicities) remains unchanged. +//! @param[in] theBSpline the B-spline curve to rebuild +//! @return the rebuilt B-spline curve, or null if cannot be fixed by knot adjustment +Handle(Geom_BSplineCurve) rebuildBSpline(const Handle(Geom_BSplineCurve)& theBSpline) +{ + if (theBSpline.IsNull()) + return nullptr; + + try + { + OCC_CATCH_SIGNALS + + const Standard_Integer aDegree = theBSpline->Degree(); + const TColgp_Array1OfPnt& aPoles = theBSpline->Poles(); + const TColStd_Array1OfReal& anOriginalKnots = theBSpline->Knots(); + const TColStd_Array1OfInteger& aMults = theBSpline->Multiplicities(); + const Standard_Integer aNbKnots = anOriginalKnots.Length(); + + if (aNbKnots < 2) + return nullptr; + + // Check for problematic knot spacing + NCollection_Vector anIntervals; + for (Standard_Integer i = anOriginalKnots.Lower() + 1; i <= anOriginalKnots.Upper(); i++) + { + const Standard_Real anInterval = anOriginalKnots(i) - anOriginalKnots(i - 1); + if (anInterval > Precision::Confusion()) + { + anIntervals.Append(anInterval); + } + } + + if (anIntervals.IsEmpty()) + return nullptr; + + std::sort(anIntervals.begin(), anIntervals.end()); + + Standard_Real aMedianInterval; + const Standard_Integer aMid = anIntervals.Length() / 2; + if (anIntervals.Length() % 2 == 0) + { + aMedianInterval = (anIntervals[aMid - 1] + anIntervals[aMid]) / 2.0; + } + else + { + aMedianInterval = anIntervals[aMid]; + } + + Standard_Real aTotalNormalInterval = 0.0; + Standard_Integer aNormalCount = 0; + const Standard_Integer aQuarter = anIntervals.Length() / 4; + for (Standard_Integer i = aQuarter; i < anIntervals.Length(); i++) + { + aTotalNormalInterval += anIntervals[i]; + aNormalCount++; + } + Standard_Real anAverageNormalInterval = + (aNormalCount > 0) ? (aTotalNormalInterval / aNormalCount) : aMedianInterval; + + Standard_Real aTolerance = aMedianInterval * 0.1; + aTolerance = Max(aTolerance, Precision::Confusion() * 100); + + Standard_Boolean hasProblematicKnots = Standard_False; + for (Standard_Integer i = anOriginalKnots.Lower() + 1; i <= anOriginalKnots.Upper(); i++) + { + const Standard_Real anInterval = anOriginalKnots(i) - anOriginalKnots(i - 1); + if (anInterval > Precision::Confusion() && anInterval < aTolerance) + { + hasProblematicKnots = Standard_True; break; } } - if (j >= myNbCashe) - aP2d[i] = mySurf->ValueOfUV(aP[i], theTol); + if (!hasProblematicKnots) + return theBSpline; - Standard_Real aDist = mySurf->Gap(); - Standard_Real aCurDist = aDist * aDist; - if (aTol2 < aDist * aDist) + Standard_Real aMinSpacing = Max(anAverageNormalInterval * 0.5, aMedianInterval * 0.3); + aMinSpacing = Max(aMinSpacing, Precision::Confusion() * 10000); + + TColStd_Array1OfReal aNewKnots(anOriginalKnots.Lower(), anOriginalKnots.Upper()); + aNewKnots(anOriginalKnots.Lower()) = anOriginalKnots(anOriginalKnots.Lower()); + + for (Standard_Integer i = anOriginalKnots.Lower() + 1; i <= anOriginalKnots.Upper(); i++) + { + const Standard_Real aPrevKnot = aNewKnots(i - 1); + const Standard_Real aCurrKnot = anOriginalKnots(i); + const Standard_Real anOriginalInterval = aCurrKnot - anOriginalKnots(i - 1); + + if (anOriginalInterval > Precision::Confusion() && anOriginalInterval < aTolerance) + { + aNewKnots(i) = aPrevKnot + Max(aMinSpacing, anOriginalInterval * 2.0); + } + else + { + aNewKnots(i) = Max(aCurrKnot, aPrevKnot + aMinSpacing); + } + } + + const Standard_Real aOriginalRange = + anOriginalKnots(anOriginalKnots.Upper()) - anOriginalKnots(anOriginalKnots.Lower()); + const Standard_Real aNewRange = aNewKnots(aNewKnots.Upper()) - aNewKnots(aNewKnots.Lower()); + + if (aNewRange > aOriginalRange * 1.5) + { + const Standard_Real aFirstKnot = anOriginalKnots(anOriginalKnots.Lower()); + const Standard_Real aLastKnot = anOriginalKnots(anOriginalKnots.Upper()); + + for (Standard_Integer i = anOriginalKnots.Lower(); i <= anOriginalKnots.Upper(); i++) + { + const Standard_Real t = + Standard_Real(i - anOriginalKnots.Lower()) / Standard_Real(aNbKnots - 1); + aNewKnots(i) = aFirstKnot + t * (aLastKnot - aFirstKnot); + } + } + + Handle(Geom_BSplineCurve) aNewBSpline; + if (theBSpline->IsRational()) + { + const TColStd_Array1OfReal* aWeights = theBSpline->Weights(); + aNewBSpline = new Geom_BSplineCurve(aPoles, *aWeights, aNewKnots, aMults, aDegree); + } + else + { + aNewBSpline = new Geom_BSplineCurve(aPoles, aNewKnots, aMults, aDegree); + } + + return aNewBSpline; + } + catch (Standard_Failure const&) + { + return nullptr; + } +} + +//================================================================================================= + +//! Generates discretization points for a curve. +//! Uses uniform distribution for general curves, and adjusts the number of points +//! for B-splines based on their knot structure to ensure adequate sampling. +//! @param[in] theCurve the curve to discretize +//! @param[in] theFirst the first parameter +//! @param[in] theLast the last parameter +//! @param[in] theNbControlPoints the minimum number of points +//! @param[out] thePoints the generated 3D points (resized as needed) +//! @param[out] theParams the corresponding parameters (resized as needed) +//! @return the actual number of generated points +Standard_Integer generateCurvePoints(const Handle(Geom_Curve)& theCurve, + const Standard_Real theFirst, + const Standard_Real theLast, + const Standard_Integer theNbControlPoints, + ShapeConstruct_ProjectCurveOnSurface::ArrayOfPnt& thePoints, + ShapeConstruct_ProjectCurveOnSurface::ArrayOfReal& theParams) +{ + Handle(Geom_BSplineCurve) aBSpline = extractBSplineCurve(theCurve); + Standard_Integer aNbPini = theNbControlPoints; + + if (!aBSpline.IsNull()) + { + Standard_Integer aUsedKnots = 0; + for (Standard_Integer i = 1; i < aBSpline->NbKnots(); i++) + { + if (aBSpline->Knot(i + 1) > theFirst && aBSpline->Knot(i) < theLast) + { + aUsedKnots++; + } + } + Standard_Integer aMinPnt = aUsedKnots * (aBSpline->Degree() + 1); + while (aNbPini < aMinPnt) + { + aNbPini += THE_NCONTROL - 1; + } + } + + thePoints.Resize(1, aNbPini, Standard_False); + theParams.Resize(1, aNbPini, Standard_False); + + const Standard_Real aDeltaParam = (theLast - theFirst) / (aNbPini - 1); + + for (Standard_Integer i = 1; i <= aNbPini; ++i) + { + Standard_Real aParam; + if (i == 1) + aParam = theFirst; + else if (i == aNbPini) + aParam = theLast; + else + aParam = theFirst + (i - 1) * aDeltaParam; + + gp_Pnt aPoint; + theCurve->D0(aParam, aPoint); + thePoints.SetValue(i, aPoint); + theParams.SetValue(i, aParam); + } + + return aNbPini; +} + +//================================================================================================= + +//! Wrapper for ShapeAnalysis_Surface::ProjectDegenerated. +//! Converts NCollection_Array1 containers to sequences, performs projection, +//! then copies results back. Required because ShapeAnalysis_Surface uses +//! sequence containers. +//! @param[in] theSurf the analysis surface +//! @param[in] theNbPnt the number of points +//! @param[in] thePoints the 3D points array +//! @param[in,out] thePoints2d the 2D points array (adjusted for degenerate regions) +//! @param[in] thePreci the precision +//! @param[in] theDirect true to check from start, false to check from end +void projectDegeneratedPoints(const Handle(ShapeAnalysis_Surface)& theSurf, + const Standard_Integer theNbPnt, + const NCollection_Array1& thePoints, + NCollection_Array1& thePoints2d, + const Standard_Real thePreci, + const Standard_Boolean theDirect) +{ + // Use incremental allocator for sequences when there are enough elements + // to benefit from pooled memory allocation. + constexpr Standard_Integer THE_ALLOC_THRESHOLD = 100; + + Handle(NCollection_BaseAllocator) anAlloc; + if (theNbPnt > THE_ALLOC_THRESHOLD) + { + anAlloc = new NCollection_IncAllocator(NCollection_IncAllocator::THE_MINIMUM_BLOCK_SIZE); + } + + // Convert arrays to sequences for ShapeAnalysis_Surface::ProjectDegenerated + TColgp_SequenceOfPnt aPoints3d(anAlloc); + TColgp_SequenceOfPnt2d aPoints2d(anAlloc); + + for (Standard_Integer i = 1; i <= theNbPnt; ++i) + { + aPoints3d.Append(thePoints(i)); + aPoints2d.Append(thePoints2d(i)); + } + + // Call the method that expects sequences + theSurf->ProjectDegenerated(theNbPnt, aPoints3d, aPoints2d, thePreci, theDirect); + + // Copy results back to array + for (Standard_Integer i = 1; i <= theNbPnt; ++i) + { + thePoints2d.SetValue(i, aPoints2d.Value(i)); + } +} + +} // namespace + +//================================================================================================= + +ShapeConstruct_ProjectCurveOnSurface::ShapeConstruct_ProjectCurveOnSurface() + : myPreci(Precision::Confusion()), + myStatus(ShapeExtend::EncodeStatus(ShapeExtend_OK)), + myAdjustOverDegen(1) +{ +} + +//================================================================================================= + +void ShapeConstruct_ProjectCurveOnSurface::Init(const Handle(Geom_Surface)& theSurf, + const Standard_Real thePreci) +{ + Init(new ShapeAnalysis_Surface(theSurf), thePreci); +} + +//================================================================================================= + +void ShapeConstruct_ProjectCurveOnSurface::Init(const Handle(ShapeAnalysis_Surface)& theSurf, + const Standard_Real thePreci) +{ + SetSurface(theSurf); + SetPrecision(thePreci); +} + +//================================================================================================= + +void ShapeConstruct_ProjectCurveOnSurface::SetSurface(const Handle(Geom_Surface)& theSurf) +{ + SetSurface(new ShapeAnalysis_Surface(theSurf)); +} + +//================================================================================================= + +void ShapeConstruct_ProjectCurveOnSurface::SetSurface(const Handle(ShapeAnalysis_Surface)& theSurf) +{ + if (mySurf == theSurf) + return; + mySurf = theSurf; + myCache = CacheArray(); +} + +//================================================================================================= + +void ShapeConstruct_ProjectCurveOnSurface::SetPrecision(const Standard_Real thePreci) +{ + myPreci = thePreci; +} + +//================================================================================================= + +Standard_Integer& ShapeConstruct_ProjectCurveOnSurface::AdjustOverDegenMode() +{ + return myAdjustOverDegen; +} + +//================================================================================================= + +Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::Status( + const ShapeExtend_Status theStatus) const +{ + return ShapeExtend::DecodeStatus(myStatus, theStatus); +} + +//================================================================================================= + +Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::Perform(const Handle(Geom_Curve)& theC3D, + const Standard_Real theFirst, + const Standard_Real theLast, + Handle(Geom2d_Curve)& theC2D, + const Standard_Real theTolFirst, + const Standard_Real theTolLast) +{ + myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK); + + if (mySurf.IsNull() || theC3D.IsNull()) + { + theC2D.Nullify(); + myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL1); + return Standard_False; + } + + // Try analytical projection first + Handle(Geom_Curve) aCurve3DTrim = theC3D; + if (!theC3D->IsKind(STANDARD_TYPE(Geom_BoundedCurve))) + { + aCurve3DTrim = new Geom_TrimmedCurve(theC3D, theFirst, theLast); + } + + theC2D = projectAnalytic(aCurve3DTrim); + if (!theC2D.IsNull()) + { + myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE1); + return Standard_True; + } + + // Handle problematic B-spline curves + Handle(Geom_Curve) aCurve = theC3D; + Standard_Real aFirst = theFirst; + Standard_Real aLast = theLast; + Handle(Geom_BSplineCurve) aBSpline; + + if (isBSplineCurveInvalid(aCurve, aFirst, aLast, aBSpline)) + { + // Work with a copy to preserve the original geometry + Handle(Geom_BSplineCurve) aSegmented = Handle(Geom_BSplineCurve)::DownCast(aBSpline->Copy()); + aSegmented->Segment(aFirst, aLast, Precision::PConfusion()); + + Handle(Geom_BSplineCurve) aNewBSpline = rebuildBSpline(aSegmented); + if (aNewBSpline.IsNull()) + { + if (PerformByProjLib(aSegmented, aFirst, aLast, theC2D)) + { + return Status(ShapeExtend_DONE); + } + } + else + { + aFirst = aNewBSpline->FirstParameter(); + aLast = aNewBSpline->LastParameter(); + aCurve = aNewBSpline; + } + } + + // Generate curve points + ArrayOfPnt aPoints; + ArrayOfReal aParams; + const Standard_Integer aNbPnt = + generateCurvePoints(aCurve, aFirst, aLast, THE_NCONTROL, aPoints, aParams); + + // Approximate pcurve + ArrayOfPnt2d aPoints2d(1, aNbPnt); + for (Standard_Integer i = 1; i <= aNbPnt; i++) + { + aPoints2d.SetValue(i, gp_Pnt2d(0., 0.)); + } + + approxPCurve(aNbPnt, aCurve, theTolFirst, theTolLast, aPoints, aParams, aPoints2d, theC2D); + + const Standard_Integer aNbPini = aPoints.Length(); + if (!theC2D.IsNull()) + { + myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE2); + return Standard_True; + } + + // Interpolate the result + theC2D = interpolatePCurve(aNbPini, aPoints2d, aParams); + + myStatus |= ShapeExtend::EncodeStatus(theC2D.IsNull() ? ShapeExtend_FAIL1 : ShapeExtend_DONE2); + return Status(ShapeExtend_DONE); +} + +//================================================================================================= + +Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::PerformByProjLib( + const Handle(Geom_Curve)& theC3D, + const Standard_Real theFirst, + const Standard_Real theLast, + Handle(Geom2d_Curve)& theC2D) +{ + theC2D.Nullify(); + if (mySurf.IsNull()) + { + myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL1); + return Standard_False; + } + + try + { + OCC_CATCH_SIGNALS + Handle(GeomAdaptor_Surface) aGAS = mySurf->Adaptor3d(); + Handle(GeomAdaptor_Curve) aGAC = new GeomAdaptor_Curve(theC3D, theFirst, theLast); + ProjLib_ProjectedCurve aProjector(aGAS, aGAC); + + switch (aProjector.GetType()) + { + case GeomAbs_Line: + theC2D = new Geom2d_Line(aProjector.Line()); + break; + case GeomAbs_Circle: + theC2D = new Geom2d_Circle(aProjector.Circle()); + break; + case GeomAbs_Ellipse: + theC2D = new Geom2d_Ellipse(aProjector.Ellipse()); + break; + case GeomAbs_Parabola: + theC2D = new Geom2d_Parabola(aProjector.Parabola()); + break; + case GeomAbs_Hyperbola: + theC2D = new Geom2d_Hyperbola(aProjector.Hyperbola()); + break; + case GeomAbs_BSplineCurve: + theC2D = aProjector.BSpline(); + break; + default: + break; + } + + if (theC2D.IsNull()) + { + myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL2); + return Standard_False; + } + else + { + myStatus = ShapeExtend::EncodeStatus(ShapeExtend_DONE1); + return Standard_True; + } + } + catch (Standard_Failure const& anException) + { +#ifdef OCCT_DEBUG + std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::PerformByProjLib(): Exception: "; + anException.Print(std::cout); + std::cout << std::endl; +#endif + (void)anException; + myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL3); + theC2D.Nullify(); + } + return Standard_False; +} + +//================================================================================================= + +Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::projectAnalytic( + const Handle(Geom_Curve)& theC3D) const +{ + Handle(Geom2d_Curve) aResult; + + Handle(Geom_Surface) aSurf = mySurf->Surface(); + Handle(Geom_Plane) aPlane = Handle(Geom_Plane)::DownCast(aSurf); + + if (aPlane.IsNull()) + { + Handle(Geom_RectangularTrimmedSurface) aRTS = + Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurf); + if (!aRTS.IsNull()) + aPlane = Handle(Geom_Plane)::DownCast(aRTS->BasisSurface()); + else + { + Handle(Geom_OffsetSurface) anOS = Handle(Geom_OffsetSurface)::DownCast(aSurf); + if (!anOS.IsNull()) + aPlane = Handle(Geom_Plane)::DownCast(anOS->BasisSurface()); + } + } + + if (!aPlane.IsNull()) + { + Handle(Geom_Curve) aProjOnPlane = + GeomProjLib::ProjectOnPlane(theC3D, aPlane, aPlane->Position().Direction(), Standard_True); + Handle(GeomAdaptor_Curve) aHC = new GeomAdaptor_Curve(aProjOnPlane); + ProjLib_ProjectedCurve aProj(mySurf->Adaptor3d(), aHC); + + aResult = Geom2dAdaptor::MakeCurve(aProj); + if (aResult.IsNull()) + return aResult; + + if (aResult->IsKind(STANDARD_TYPE(Geom2d_TrimmedCurve))) + { + Handle(Geom2d_TrimmedCurve) aTC = Handle(Geom2d_TrimmedCurve)::DownCast(aResult); + aResult = aTC->BasisCurve(); + } + + return aResult; + } + + return aResult; +} + +//================================================================================================= + +Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::getLine( + const ArrayOfPnt& thePoints, + const ArrayOfReal& theParams, + ArrayOfPnt2d& thePoints2d, + const Standard_Real theTol, + Standard_Boolean& theIsRecompute, + Standard_Boolean& theIsFromCache) const +{ + const Standard_Integer aNb = thePoints.Length(); + gp_Pnt aP[4]; + aP[0] = thePoints(1); + aP[1] = thePoints(2); + aP[2] = thePoints(aNb - 1); + aP[3] = thePoints(aNb); + + gp_Pnt2d aP2d[4]; + Standard_Integer i = 0; + + Standard_Real aTol2 = theTol * theTol; + Standard_Real aTolWorking = theTol; + const Standard_Boolean isPeriodicU = mySurf->Surface()->IsUPeriodic(); + const Standard_Boolean isPeriodicV = mySurf->Surface()->IsVPeriodic(); + + // Protection against bad tolerance shapes + if (aTol2 > 1.0) + { + aTolWorking = Precision::Confusion(); + aTol2 = aTolWorking * aTolWorking; + } + if (aTol2 < Precision::SquareConfusion()) + aTol2 = Precision::SquareConfusion(); + const Standard_Real anOldTol2 = aTol2; + + Standard_Integer aSavedPointNum = -1; + gp_Pnt2d aSavedPoint; + + // Create projector with B-spline surface pole cache optimization + SurfaceProjectorWithCache aProjector(mySurf); + + // Helper lambda to project a point with cache lookup + auto projectPoint = + [&](const gp_Pnt& thePoint, gp_Pnt2d& theResult, const Standard_Integer theIndex) -> void { + // Try existing endpoint cache first + const Standard_Integer aNbCache = myCache.Length(); + for (Standard_Integer j = 0; j < aNbCache; ++j) + { + const CachePoint& aCachePnt = myCache(j); + if (aCachePnt.first.SquareDistance(thePoint) < aTol2) + { + theResult = + aProjector.NextValueOfUV(aCachePnt.second, thePoint, aTolWorking, aTol2, aTolWorking); + aSavedPointNum = theIndex; + aSavedPoint = aCachePnt.second; + if (theIndex == 0) + theIsFromCache = Standard_True; + return; + } + } + + // Fall back to full projection (with B-spline pole cache optimization inside) + theResult = aProjector.ValueOfUV(thePoint, aTolWorking, aTol2); + }; + + // Project first and last points + for (; i < 4; i += 3) + { + projectPoint(aP[i], aP2d[i], i); + + const Standard_Real aDist = aProjector.Gap(); + const Standard_Real aCurDist = aDist * aDist; + if (aTol2 < aCurDist) aTol2 = aCurDist; } if (isPeriodicU || isPeriodicV) { - // Compute second and last but one c2d points. + // Compute second and last but one c2d points for (i = 1; i < 3; i++) { - Standard_Integer j; - for (j = 0; j < myNbCashe; j++) - { - if (myCashe3d[j].SquareDistance(aP[i]) < aTol2) - { - aP2d[i] = mySurf->NextValueOfUV(myCashe2d[j], aP[i], theTol, theTol); - aSavedPointNum = i; - aSavedPoint = myCashe2d[j]; - break; - } - } + projectPoint(aP[i], aP2d[i], i); - if (j >= myNbCashe) - aP2d[i] = mySurf->ValueOfUV(aP[i], theTol); - - Standard_Real aDist = mySurf->Gap(); - Standard_Real aCurDist = aDist * aDist; - if (aTol2 < aDist * aDist) + const Standard_Real aDist = aProjector.Gap(); + const Standard_Real aCurDist = aDist * aDist; + if (aTol2 < aCurDist) aTol2 = aCurDist; } if (isPeriodicU) { - isRecompute = fixPeriodictyTroubles(&aP2d[0], - 1 /* X Coord */, - mySurf->Surface()->UPeriod(), - aSavedPointNum, - aSavedPoint.X()); + theIsRecompute = fixPeriodicityTroubles(&aP2d[0], + 1, + mySurf->Surface()->UPeriod(), + aSavedPointNum, + aSavedPoint.X()); } if (isPeriodicV) { - isRecompute = fixPeriodictyTroubles(&aP2d[0], - 2 /* Y Coord */, - mySurf->Surface()->VPeriod(), - aSavedPointNum, - aSavedPoint.Y()); + theIsRecompute = fixPeriodicityTroubles(&aP2d[0], + 2, + mySurf->Surface()->VPeriod(), + aSavedPointNum, + aSavedPoint.Y()); } } - if (isRecompute && mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface))) + if (theIsRecompute && mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface))) { - // Do not try to make line, because in this case may be very special case when 3d curve - // go over the pole of, e.g., sphere, and partly lies along seam - // (see ApproxPCurve() for more information). - return 0; + return nullptr; } - thePnt2ds.SetValue(1, aP2d[0]); - thePnt2ds.SetValue(nb, aP2d[3]); + thePoints2d.SetValue(1, aP2d[0]); + thePoints2d.SetValue(aNb, aP2d[3]); - // Restore old tolerance in 2d space to avoid big gap cases. + // Restore old tolerance aTol2 = anOldTol2; - // Check that straight line in 2d with parameterisation as in 3d will fit - // fit 3d curve at all points. - Standard_Real dPar = theparams(nb) - theparams(1); + + const Standard_Real dPar = theParams(aNb) - theParams(1); if (std::abs(dPar) < Precision::PConfusion()) - return 0; - gp_Vec2d aVec0(aP2d[0], aP2d[3]); - gp_Vec2d aVec = aVec0 / dPar; + return nullptr; + + const gp_Vec2d aVec0(aP2d[0], aP2d[3]); + const gp_Vec2d aVec = aVec0 / dPar; Handle(Geom_Surface) aSurf = mySurf->Surface(); Standard_Boolean isNormalCheck = aSurf->IsCNu(1) && aSurf->IsCNv(1); + if (isNormalCheck) { - for (i = 1; i <= nb; i++) + for (i = 1; i <= aNb; i++) { - gp_XY aCurPoint = aP2d[0].XY() + aVec.XY() * (theparams(i) - theparams(1)); - gp_Pnt aCurP; - gp_Vec aNormalVec, aDu, aDv; + const gp_XY aCurPoint = aP2d[0].XY() + aVec.XY() * (theParams(i) - theParams(1)); + gp_Pnt aCurP; + gp_Vec aNormalVec, aDu, aDv; aSurf->D1(aCurPoint.X(), aCurPoint.Y(), aCurP, aDu, aDv); aNormalVec = aDu ^ aDv; if (aNormalVec.SquareMagnitude() < Precision::SquareConfusion()) @@ -786,104 +1157,101 @@ Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::getLine( isNormalCheck = Standard_False; break; } - gp_Lin aNormalLine(aCurP, gp_Dir(aNormalVec)); - Standard_Real aDist = aNormalLine.Distance(thepoints(i)); - if (aDist > theTol) - return 0; + const gp_Lin aNormalLine(aCurP, gp_Dir(aNormalVec)); + const Standard_Real aDist = aNormalLine.Distance(thePoints(i)); + if (aDist > aTolWorking) + return nullptr; } } + if (!isNormalCheck) { - Standard_Real aFirstPointDist = - mySurf->Surface()->Value(aP2d[0].X(), aP2d[0].Y()).SquareDistance(thepoints(1)); + const Standard_Real aFirstPointDist = + mySurf->Surface()->Value(aP2d[0].X(), aP2d[0].Y()).SquareDistance(thePoints(1)); aTol2 = std::max(aTol2, aTol2 * 2 * aFirstPointDist); - for (i = 2; i < nb; i++) + for (i = 2; i < aNb; i++) { - gp_XY aCurPoint = aP2d[0].XY() + aVec.XY() * (theparams(i) - theparams(1)); - gp_Pnt aCurP; + const gp_XY aCurPoint = aP2d[0].XY() + aVec.XY() * (theParams(i) - theParams(1)); + gp_Pnt aCurP; aSurf->D0(aCurPoint.X(), aCurPoint.Y(), aCurP); - Standard_Real aDist1 = aCurP.SquareDistance(thepoints(i)); + const Standard_Real aDist1 = aCurP.SquareDistance(thePoints(i)); if (std::abs(aFirstPointDist - aDist1) > aTol2) - return 0; + return nullptr; } } - // check if pcurve can be represented by Geom2d_Line (parameterised by length) - Standard_Real aLLength = aVec0.Magnitude(); + // Check if pcurve can be represented by Geom2d_Line + const Standard_Real aLLength = aVec0.Magnitude(); if (std::abs(aLLength - dPar) <= Precision::PConfusion()) { - gp_XY aDirL = aVec0.XY() / aLLength; - gp_Pnt2d aPL(aP2d[0].XY() - theparams(1) * aDirL); + const gp_XY aDirL = aVec0.XY() / aLLength; + const gp_Pnt2d aPL(aP2d[0].XY() - theParams(1) * aDirL); return new Geom2d_Line(aPL, gp_Dir2d(aDirL)); } - // create straight bspline + // Create straight bspline TColgp_Array1OfPnt2d aPoles(1, 2); aPoles(1) = aP2d[0]; aPoles(2) = aP2d[3]; TColStd_Array1OfReal aKnots(1, 2); - aKnots(1) = theparams(1); - aKnots(2) = theparams(theparams.Length()); + aKnots(1) = theParams(1); + aKnots(2) = theParams(theParams.Length()); TColStd_Array1OfInteger aMults(1, 2); - aMults(1) = 2; - aMults(2) = 2; - Standard_Integer aDegree = 1; - Handle(Geom2d_BSplineCurve) abspl2d = new Geom2d_BSplineCurve(aPoles, aKnots, aMults, aDegree); - return abspl2d; + aMults(1) = 2; + aMults(2) = 2; + + return new Geom2d_BSplineCurve(aPoles, aKnots, aMults, 1); } //================================================================================================= -Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standard_Integer nbrPnt, - const Handle(Geom_Curve)& c3d, - const Standard_Real TolFirst, - const Standard_Real TolLast, - TColgp_SequenceOfPnt& points, - TColStd_SequenceOfReal& params, - TColgp_SequenceOfPnt2d& pnt2d, - Handle(Geom2d_Curve)& c2d) +Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::approxPCurve( + const Standard_Integer theNbPnt, + const Handle(Geom_Curve)& theC3D, + const Standard_Real theTolFirst, + const Standard_Real theTolLast, + ArrayOfPnt& thePoints, + ArrayOfReal& theParams, + ArrayOfPnt2d& thePoints2d, + Handle(Geom2d_Curve)& theC2D) { - // for performance, first try to handle typical case when pcurve is straight + // For performance, first try to handle typical case when pcurve is straight Standard_Boolean isRecompute = Standard_False; - Standard_Boolean isFromCasheLine = Standard_False; - for (Standard_Integer iseq = 1; iseq <= nbrPnt; iseq++) + Standard_Boolean isFromCacheLine = Standard_False; + + theC2D = getLine(thePoints, theParams, thePoints2d, myPreci, isRecompute, isFromCacheLine); + if (!theC2D.IsNull()) { - gp_Pnt2d aP2d(0., 0.); - pnt2d.Append(aP2d); - } - c2d = getLine(points, params, pnt2d, myPreci, isRecompute, isFromCasheLine); - if (!c2d.IsNull()) - { - // fill cache - Standard_Boolean ChangeCycle = Standard_False; - if (myNbCashe > 0 && myCashe3d[0].Distance(points(1)) > myCashe3d[0].Distance(points(nbrPnt)) - && myCashe3d[0].Distance(points(nbrPnt)) < Precision::Confusion()) - ChangeCycle = Standard_True; - myNbCashe = 2; - if (ChangeCycle) + // Fill cache + Standard_Boolean aChangeCycle = Standard_False; + if (!myCache.IsEmpty() + && myCache(0).first.Distance(thePoints(1)) > myCache(0).first.Distance(thePoints(theNbPnt)) + && myCache(0).first.Distance(thePoints(theNbPnt)) < Precision::Confusion()) { - myCashe3d[0] = points(1); - myCashe3d[1] = points(nbrPnt); - myCashe2d[0] = pnt2d(1); - myCashe2d[1] = pnt2d(nbrPnt); + aChangeCycle = Standard_True; + } + + myCache.Resize(0, 1, Standard_False); + if (aChangeCycle) + { + myCache(0) = CachePoint(thePoints(1), thePoints2d(1)); + myCache(1) = CachePoint(thePoints(theNbPnt), thePoints2d(theNbPnt)); } else { - myCashe3d[1] = points(1); - myCashe3d[0] = points(nbrPnt); - myCashe2d[1] = pnt2d(1); - myCashe2d[0] = pnt2d(nbrPnt); + myCache(0) = CachePoint(thePoints(theNbPnt), thePoints2d(theNbPnt)); + myCache(1) = CachePoint(thePoints(1), thePoints2d(1)); } return Standard_True; } - Standard_Boolean isDone = Standard_True; - // test if the curve 3d is a boundary of the surface - // (only for Bezier or BSpline surface) - Standard_Boolean isoParam, isoPar2d3d, isoTypeU, p1OnIso, p2OnIso, isoclosed; + Standard_Boolean isDone = Standard_True; + + // Test if the curve 3d is a boundary of the surface + Standard_Boolean isoParam, isoPar2d3d, isoTypeU, p1OnIso, p2OnIso, isoClosed; gp_Pnt2d valueP1, valueP2; Handle(Geom_Curve) cIso; Standard_Real t1, t2; @@ -892,14 +1260,18 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa Standard_Boolean isAnalytic = Standard_True; if (sType == STANDARD_TYPE(Geom_BezierSurface) || sType == STANDARD_TYPE(Geom_BSplineSurface)) isAnalytic = Standard_False; + Standard_Real uf, ul, vf, vl; mySurf->Surface()->Bounds(uf, ul, vf, vl); - isoclosed = Standard_False; - TColStd_Array1OfReal pout(1, nbrPnt); + isoClosed = Standard_False; - isoParam = IsAnIsoparametric(nbrPnt, - points, - params, + ArrayOfReal pout(1, theNbPnt); + for (Standard_Integer i = 1; i <= theNbPnt; i++) + pout.SetValue(i, 0.0); + + isoParam = isAnIsoparametric(theNbPnt, + thePoints, + theParams, isoTypeU, p1OnIso, valueP1, @@ -911,15 +1283,12 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa t2, pout); - // projection of the points on surfaces - + // Projection of the points on surfaces gp_Pnt p3d; gp_Pnt2d p2d; Standard_Real isoValue = 0., isoPar1 = 0., isoPar2 = 0., tPar = 0., tdeb, tfin; - Standard_Real Cf, Cl, parf, parl; // szv#4:S4163:12Mar99 dist not needed + Standard_Real Cf, Cl, parf, parl; - // Le calcul part-il dans le bon sens, c-a-d deb et fin dans le bon ordre ? - // Si uclosed et iso en V, attention isoPar1 ET/OU 2 peut toucher la fermeture if (isoParam) { if (isoTypeU) @@ -927,7 +1296,7 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa isoValue = valueP1.X(); isoPar1 = valueP1.Y(); isoPar2 = valueP2.Y(); - isoclosed = mySurf->IsVClosed(myPreci); // #78 rln 12.03.99 S4135 + isoClosed = mySurf->IsVClosed(myPreci); parf = vf; parl = vl; } @@ -936,7 +1305,7 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa isoValue = valueP1.Y(); isoPar1 = valueP1.X(); isoPar2 = valueP2.X(); - isoclosed = mySurf->IsUClosed(myPreci); // #78 rln 12.03.99 S4135 + isoClosed = mySurf->IsUClosed(myPreci); parf = uf; parl = ul; } @@ -948,11 +1317,10 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa Cf = -1000; if (Precision::IsInfinite(Cl)) Cl = +1000; - // pdn S4030 optimizing and fix isopar case on PRO41323 + tdeb = pout(2); - // dist = ShapeAnalysis_Curve().Project (cIso,points(2),myPreci,pt,tdeb,Cf,Cl); - // Chacun des par1 ou par2 est-il sur un bord. Attention first/last : recaler - if (isoclosed && (isoPar1 == parf || isoPar1 == parl)) + + if (isoClosed && (isoPar1 == parf || isoPar1 == parl)) { if (std::abs(tdeb - parf) < std::abs(tdeb - parl)) isoPar1 = parf; @@ -963,11 +1331,9 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa else valueP1.SetX(isoPar1); } - if (isoclosed && (isoPar2 == parf || isoPar2 == parl)) + if (isoClosed && (isoPar2 == parf || isoPar2 == parl)) { - // pdn S4030 optimizing and fix isopar case on PRO41323 - tfin = pout(nbrPnt - 1); - // dist = ShapeAnalysis_Curve().Project (cIso,points(nbrPnt-1),myPreci,pt,tfin,Cf,Cl); + tfin = pout(theNbPnt - 1); if (std::abs(tfin - parf) < std::abs(tfin - parl)) isoPar2 = parf; else @@ -978,16 +1344,10 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa valueP2.SetX(isoPar2); } - // Interversion Par1/Par2 (ne veut que si les 2 sont sur les bords ...) - // Est-ce encore necessaire apres ce qui vient d etre fait ? - - // PTV 05.02.02 fix for translation face from 12_hp_mouse (PARASOLID) face 24008 - // if curve is periodic do not change the points - // skl change "if" for pout(nbrPnt-1) 19.11.2003 - if (!isoclosed) + if (!isoClosed) { if ((std::abs(tdeb - isoPar1) > std::abs(tdeb - isoPar2)) - && (std::abs(pout(nbrPnt - 1) - isoPar2) > std::abs(pout(nbrPnt - 1) - isoPar1))) + && (std::abs(pout(theNbPnt - 1) - isoPar2) > std::abs(pout(theNbPnt - 1) - isoPar1))) { gp_Pnt2d valueTmp = valueP1; valueP1 = valueP2; @@ -1004,63 +1364,54 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa isoPar1 = valueP1.X(); isoPar2 = valueP2.X(); } - // Fin calcul sens de courbe iso } - } // end of fix check 05.02.02 + } } } - // Si pas isoParam, on a quand meme du p1OnIso/p2OnIso possible ... !!! - // (utile pour detromper bug de projection). Mais detromper aussi circularite - // else { - // if (p1OnIso) valueP1 = - // BestExtremum (valueP1,points(1),points(2)); - // if (p2OnIso) valueP2 = - // BestExtremum (valueP2,points(nbrPnt),points(nbrPnt-1)); - //} + const Standard_Real Up = ul - uf; + const Standard_Real Vp = vl - vf; + Standard_Real gap = myPreci; + Standard_Boolean aChangeCycle = Standard_False; - Standard_Real Up = ul - uf; - Standard_Real Vp = vl - vf; - Standard_Real gap = myPreci; //: q1 - Standard_Boolean ChangeCycle = Standard_False; // skl for OCC3430 - // auxiliaruy variables to shift 2dcurve, according to previous - Standard_Boolean isFromCashe = Standard_False; + Standard_Boolean isFromCache = Standard_False; gp_Pnt2d aSavedPoint; - if (myNbCashe > 0 && myCashe3d[0].Distance(points(1)) > myCashe3d[0].Distance(points(nbrPnt))) - // if(myCashe3d[0].Distance(points(nbrPnt)) myCache(0).first.Distance(thePoints(theNbPnt))) + { + if (myCache(0).first.Distance(thePoints(theNbPnt)) < Precision::Confusion()) + aChangeCycle = Standard_True; + } + Standard_Boolean needResolveUJump = Standard_False; Standard_Boolean needResolveVJump = Standard_False; gp_Pnt prevP3d; gp_Pnt2d prevP2d; - // for( i = 1; i <= nbrPnt; i ++) { - for (Standard_Integer ii = 1; ii <= nbrPnt; ii++) + + for (Standard_Integer ii = 1; ii <= theNbPnt; ii++) { - const Standard_Integer aPntIndex = ChangeCycle ? (nbrPnt - ii + 1) : ii; - p3d = points(aPntIndex); + const Standard_Integer aPntIndex = aChangeCycle ? (theNbPnt - ii + 1) : ii; + p3d = thePoints(aPntIndex); + if (isoParam) { - if (isoPar2d3d) { if (isoPar2 > isoPar1) - tPar = params(aPntIndex); + tPar = theParams(aPntIndex); else - tPar = t1 + t2 - params(aPntIndex); + tPar = t1 + t2 - theParams(aPntIndex); } else if (!isAnalytic) { - // projection to iso if (aPntIndex == 1) tPar = isoPar1; - else if (aPntIndex == nbrPnt) + else if (aPntIndex == theNbPnt) tPar = isoPar2; else { tPar = pout(aPntIndex); - //: S4030 ShapeAnalysis_Curve().Project (cIso,p3d,myPreci,pt,tPar,Cf,Cl); - //: //szv#4:S4163:12Mar99 `dist=` not needed } } @@ -1068,14 +1419,11 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa { if (aPntIndex == 1) p2d = valueP1; - else if (aPntIndex == nbrPnt) + else if (aPntIndex == theNbPnt) p2d = valueP2; else { - p2d = mySurf->NextValueOfUV(p2d, - p3d, - myPreci, //%12 pdn 15.02.99 optimizing - Precision::Confusion() + 1000 * gap); //: q1 + p2d = mySurf->NextValueOfUV(p2d, p3d, myPreci, Precision::Confusion() + 1000 * gap); gap = mySurf->Gap(); } } @@ -1093,48 +1441,49 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa } } } - else { if ((aPntIndex == 1) && p1OnIso) p2d = valueP1; - else if ((aPntIndex == nbrPnt) && p2OnIso) + else if ((aPntIndex == theNbPnt) && p2OnIso) p2d = valueP2; else - { // general case (not an iso) mais attention aux singularites ! - // first and last points are already computed by getLine() - if (aPntIndex == 1 || aPntIndex == nbrPnt) + { + if (aPntIndex == 1 || aPntIndex == theNbPnt) { if (!isRecompute) { - p2d = pnt2d(aPntIndex); + p2d = thePoints2d(aPntIndex); gap = mySurf->Gap(); if (aPntIndex == 1) { - isFromCashe = isFromCasheLine; + isFromCache = isFromCacheLine; aSavedPoint = p2d; } continue; } else { - //: q9 abv 23 Mar 99: use cache as 1st approach - Standard_Integer j; // svv #1 - for (j = 0; j < myNbCashe; ++j) + Standard_Integer j = 0; + const Standard_Integer aNbCache = myCache.Length(); + for (; j < aNbCache; ++j) { - if (myCashe3d[j].SquareDistance(p3d) < myPreci * myPreci) + const CachePoint& aCachePnt = myCache(j); + if (aCachePnt.first.SquareDistance(p3d) < myPreci * myPreci) { - p2d = - mySurf->NextValueOfUV(myCashe2d[j], p3d, myPreci, Precision::Confusion() + gap); + p2d = mySurf->NextValueOfUV(aCachePnt.second, + p3d, + myPreci, + Precision::Confusion() + gap); if (aPntIndex == 1) { - isFromCashe = Standard_True; - aSavedPoint = myCashe2d[j]; + isFromCache = Standard_True; + aSavedPoint = aCachePnt.second; } break; } } - if (j >= myNbCashe) + if (j >= aNbCache) { p2d = mySurf->ValueOfUV(p3d, myPreci); } @@ -1142,18 +1491,15 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa } else { - p2d = mySurf->NextValueOfUV(p2d, - p3d, - myPreci, //: S4030: optimizing - Precision::Confusion() + 1000 * gap); //: q1 + p2d = mySurf->NextValueOfUV(p2d, p3d, myPreci, Precision::Confusion() + 1000 * gap); } gap = mySurf->Gap(); } } - pnt2d(aPntIndex) = p2d; - if (nbrPnt > 23 && ii > 2 && ii < nbrPnt) + thePoints2d.SetValue(aPntIndex, p2d); + + if (theNbPnt > 23 && ii > 2 && ii < theNbPnt) { - // additional check for possible invalid jump by U or V parameter if (fabs(p2d.X() - prevP2d.X()) > 0.95 * Up && prevP3d.Distance(p3d) < myPreci && !mySurf->IsUClosed(myPreci) && mySurf->NbSingularities(myPreci) > 0 && mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_BSplineSurface))) @@ -1171,25 +1517,25 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa prevP2d = p2d; if (ii > 1) { - if (ChangeCycle) - p2d.SetXY(2. * p2d.XY() - pnt2d(aPntIndex + 1).XY()); + if (aChangeCycle) + p2d.SetXY(2. * p2d.XY() - thePoints2d(aPntIndex + 1).XY()); else - p2d.SetXY(2. * p2d.XY() - pnt2d(aPntIndex - 1).XY()); + p2d.SetXY(2. * p2d.XY() - thePoints2d(aPntIndex - 1).XY()); } } - // pdn %12 11.02.99 PRO9234 entity 15402 if (!isoPar2d3d) { - mySurf->ProjectDegenerated(nbrPnt, points, pnt2d, myPreci, Standard_True); - mySurf->ProjectDegenerated(nbrPnt, points, pnt2d, myPreci, Standard_False); + projectDegeneratedPoints(mySurf, theNbPnt, thePoints, thePoints2d, myPreci, Standard_True); + projectDegeneratedPoints(mySurf, theNbPnt, thePoints, thePoints2d, myPreci, Standard_False); } - // Check the extremities of 3d curve for coinciding with singularities of surf - // Standard_Integer NbSing = mySurf->NbSingularities(Precision::Confusion()); - gp_Pnt PointFirst = points.First(), PointLast = points.Last(); - Standard_Real aTolFirst = (TolFirst == -1) ? Precision::Confusion() : TolFirst; - Standard_Real aTolLast = (TolLast == -1) ? Precision::Confusion() : TolLast; + // Check extremities for singularities + const gp_Pnt aPointFirst = thePoints.First(); + const gp_Pnt aPointLast = thePoints.Last(); + const Standard_Real aTolFirst = (theTolFirst < 0) ? Precision::Confusion() : theTolFirst; + const Standard_Real aTolLast = (theTolLast < 0) ? Precision::Confusion() : theTolLast; + for (Standard_Integer i = 1;; i++) { Standard_Real aPreci, aFirstPar, aLastPar; @@ -1198,70 +1544,39 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa Standard_Boolean IsUiso; if (!mySurf->Singularity(i, aPreci, aP3d, aFirstP2d, aLastP2d, aFirstPar, aLastPar, IsUiso)) break; - if (aPreci <= Precision::Confusion() && PointFirst.Distance(aP3d) <= aTolFirst) + if (aPreci <= Precision::Confusion() && aPointFirst.Distance(aP3d) <= aTolFirst) { - CorrectExtremity(c3d, - params, - pnt2d, - Standard_True, // first point - aFirstP2d, - IsUiso); + correctExtremity(theC3D, theParams, thePoints2d, Standard_True, aFirstP2d, IsUiso); } - if (aPreci <= Precision::Confusion() && PointLast.Distance(aP3d) <= aTolLast) + if (aPreci <= Precision::Confusion() && aPointLast.Distance(aP3d) <= aTolLast) { - CorrectExtremity(c3d, - params, - pnt2d, - Standard_False, // last point - aFirstP2d, - IsUiso); + correctExtremity(theC3D, theParams, thePoints2d, Standard_False, aFirstP2d, IsUiso); } } - // attention aux singularites ... (hors cas iso qui les traite deja) - // if (!isoParam) { - // p2d = pnt2d (1); - // if (mySurf->ProjectDegenerated (points(1),myPreci,pnt2d (2),p2d)) - // pnt2d (1) = p2d; - // p2d = pnt2d (nbrPnt); - // if (mySurf->ProjectDegenerated (points(nbrPnt),myPreci,pnt2d (nbrPnt-1),p2d)) - // pnt2d (nbrPnt) = p2d; - // } - - // Si la surface est UCLosed et VClosed, on recadre les points - // algo un peu complique, on retarde l implementation - // Standard_Real Up = ul - uf; - // Standard_Real Vp = vl - vf; + // Handle U-closed surfaces Standard_Real dist2d; const Standard_Real TolOnUPeriod = Precision::Confusion() * Up; const Standard_Real TolOnVPeriod = Precision::Confusion() * Vp; -#ifdef OCCT_DEBUG - if (mySurf->IsUClosed(myPreci) && mySurf->IsVClosed(myPreci)) - { // #78 rln 12.03.99 S4135 - std::cout << "WARNING : Recadrage incertain sur U & VClosed" << std::endl; - } -#endif - // Si la surface est UCLosed, on recadre les points + if (mySurf->IsUClosed(myPreci) || needResolveUJump) - { // #78 rln 12.03.99 S4135 - // Premier point dans le domain [uf, ul] - Standard_Real prevX, firstX = pnt2d(1).X(); - if (!isFromCashe) + { + Standard_Real prevX, firstX = thePoints2d(1).X(); + if (!isFromCache) { - // do not shift 2dcurve, if it connects to previous while (firstX < uf) { firstX += Up; - pnt2d(1).SetX(firstX); + thePoints2d.ChangeValue(1).SetX(firstX); } while (firstX > ul) { firstX -= Up; - pnt2d(1).SetX(firstX); + thePoints2d.ChangeValue(1).SetX(firstX); } } - // shift first point, according to cache - if (mySurf->Surface()->IsUPeriodic() && isFromCashe) + + if (mySurf->Surface()->IsUPeriodic() && isFromCache) { Standard_Real aMinParam = uf, aMaxParam = ul; while (aMinParam > aSavedPoint.X()) @@ -1276,45 +1591,40 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa } Standard_Real aShift = ShapeAnalysis::AdjustToPeriod(firstX, aMinParam, aMaxParam); firstX += aShift; - pnt2d(1).SetX(firstX); + thePoints2d.ChangeValue(1).SetX(firstX); } prevX = firstX; - //: 97 abv 1 Feb 98: treat case when curve is whole out of surface bounds Standard_Real minX = firstX, maxX = firstX; Standard_Boolean ToAdjust = Standard_False; - // On decalle toujours le suivant - for (Standard_Integer aPntIter = 2; aPntIter <= pnt2d.Length(); ++aPntIter) + for (Standard_Integer aPntIter = 2; aPntIter <= thePoints2d.Length(); ++aPntIter) { - // dist2d = pnt2d (aPntIter - 1).Distance(pnt2d (aPntIter)); - Standard_Real CurX = pnt2d(aPntIter).X(); + Standard_Real CurX = thePoints2d(aPntIter).X(); dist2d = std::abs(CurX - prevX); if (dist2d > (Up / 2)) { - InsertAdditionalPointOrAdjust(ToAdjust, + insertAdditionalPointOrAdjust(ToAdjust, 1, Up, TolOnUPeriod, CurX, prevX, - c3d, + theC3D, aPntIter, - points, - params, - pnt2d); + thePoints, + theParams, + thePoints2d); } prevX = CurX; if (minX > CurX) - minX = CurX; //: 97 + minX = CurX; else if (maxX < CurX) - maxX = CurX; //: 97 + maxX = CurX; } - //: 97 - if (!isFromCashe) + if (!isFromCache) { - // do not shift 2dcurve, if it connects to previous Standard_Real midX = 0.5 * (minX + maxX); Standard_Real shiftX = 0.; if (midX > ul) @@ -1322,38 +1632,31 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa else if (midX < uf) shiftX = Up; if (shiftX != 0.) - for (Standard_Integer aPntIter = 1; aPntIter <= pnt2d.Length(); ++aPntIter) - pnt2d(aPntIter).SetX(pnt2d(aPntIter).X() + shiftX); + for (Standard_Integer aPntIter = 1; aPntIter <= thePoints2d.Length(); ++aPntIter) + thePoints2d.ChangeValue(aPntIter).SetX(thePoints2d(aPntIter).X() + shiftX); } } - // Si la surface est VCLosed, on recadre les points - // Same code as UClosed : optimisation souhaitable !! - // CKY : d abord un code IDENTIQUE A UClosed; PUIS le special Seam ... - // Si la surface est UCLosed, on recadre les points - // - // #69 rln 01.03.99 S4135 bm2_sd_t4-A.stp entity 30 - // #78 rln 12.03.99 S4135 + + // Handle V-closed surfaces if (mySurf->IsVClosed(myPreci) || needResolveVJump || mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface))) { - // Premier point dans le domain [vf, vl] - Standard_Real prevY, firstY = pnt2d(1).Y(); - if (!isFromCashe) + Standard_Real prevY, firstY = thePoints2d(1).Y(); + if (!isFromCache) { - // do not shift 2dcurve, if it connects to previous while (firstY < vf) { firstY += Vp; - pnt2d(1).SetY(firstY); + thePoints2d.ChangeValue(1).SetY(firstY); } while (firstY > vl) { firstY -= Vp; - pnt2d(1).SetY(firstY); + thePoints2d.ChangeValue(1).SetY(firstY); } } - // shift first point, according to cache - if (mySurf->Surface()->IsVPeriodic() && isFromCashe) + + if (mySurf->Surface()->IsVPeriodic() && isFromCache) { Standard_Real aMinParam = vf, aMaxParam = vl; while (aMinParam > aSavedPoint.Y()) @@ -1368,45 +1671,40 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa } Standard_Real aShift = ShapeAnalysis::AdjustToPeriod(firstY, aMinParam, aMaxParam); firstY += aShift; - pnt2d(1).SetY(firstY); + thePoints2d.ChangeValue(1).SetY(firstY); } prevY = firstY; - //: 97 abv 1 Feb 98: treat case when curve is whole out of surface bounds Standard_Real minY = firstY, maxY = firstY; Standard_Boolean ToAdjust = Standard_False; - // On decalle toujours le suivant - for (Standard_Integer aPntIter = 2; aPntIter <= pnt2d.Length(); ++aPntIter) + for (Standard_Integer aPntIter = 2; aPntIter <= thePoints2d.Length(); ++aPntIter) { - // dist2d = pnt2d (i-1).Distance(pnt2d (i)); - Standard_Real CurY = pnt2d(aPntIter).Y(); + Standard_Real CurY = thePoints2d(aPntIter).Y(); dist2d = std::abs(CurY - prevY); if (dist2d > (Vp / 2)) { - InsertAdditionalPointOrAdjust(ToAdjust, + insertAdditionalPointOrAdjust(ToAdjust, 2, Vp, TolOnVPeriod, CurY, prevY, - c3d, + theC3D, aPntIter, - points, - params, - pnt2d); + thePoints, + theParams, + thePoints2d); } prevY = CurY; if (minY > CurY) - minY = CurY; //: 97 + minY = CurY; else if (maxY < CurY) - maxY = CurY; //: 97 + maxY = CurY; } - //: 97 - if (!isFromCashe) + if (!isFromCache) { - // do not shift 2dcurve, if it connects to previous Standard_Real midY = 0.5 * (minY + maxY); Standard_Real shiftY = 0.; if (midY > vl) @@ -1414,39 +1712,28 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa else if (midY < vf) shiftY = Vp; if (shiftY != 0.) - for (Standard_Integer aPntIter = 1; aPntIter <= pnt2d.Length(); ++aPntIter) - pnt2d(aPntIter).SetY(pnt2d(aPntIter).Y() + shiftY); + for (Standard_Integer aPntIter = 1; aPntIter <= thePoints2d.Length(); ++aPntIter) + thePoints2d.ChangeValue(aPntIter).SetY(thePoints2d(aPntIter).Y() + shiftY); } } - // #69 rln 01.03.99 S4135 bm2_sd_t4-A.stp entity 30 - // #78 rln 12.03.99 S4135 + // Handle V-closed seam adjustment if (mySurf->IsVClosed(myPreci) || mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface))) { - for (Standard_Integer aPntIter = 2; aPntIter <= pnt2d.Length(); ++aPntIter) + for (Standard_Integer aPntIter = 2; aPntIter <= thePoints2d.Length(); ++aPntIter) { - // #1 rln 11/02/98 ca_exhaust.stp entity #9869 dist2d = pnt2d (i-1).Distance(pnt2d (i)); - dist2d = std::abs(pnt2d(aPntIter).Y() - pnt2d(aPntIter - 1).Y()); + dist2d = std::abs(thePoints2d(aPntIter).Y() - thePoints2d(aPntIter - 1).Y()); if (dist2d > (Vp / 2)) { - // ATTENTION : il faut regarder ou le decalage se fait. - // si plusieurs points sont decalles, il faut plusieurs passes - // pour obtenir un resultat correct. - // NOT YET IMPLEMENTED - - // one of those point is incorrectly placed - // i.e on the wrong side of the "seam" - // on prend le point le plus pres des bords vf ou vl Standard_Boolean prevOnFirst = Standard_False; Standard_Boolean prevOnLast = Standard_False; Standard_Boolean currOnFirst = Standard_False; Standard_Boolean currOnLast = Standard_False; - // .X ? plutot .Y , non ? - Standard_Real distPrevVF = std::abs(pnt2d(aPntIter - 1).Y() - vf); - Standard_Real distPrevVL = std::abs(pnt2d(aPntIter - 1).Y() - vl); - Standard_Real distCurrVF = std::abs(pnt2d(aPntIter).Y() - vf); - Standard_Real distCurrVL = std::abs(pnt2d(aPntIter).Y() - vl); + Standard_Real distPrevVF = std::abs(thePoints2d(aPntIter - 1).Y() - vf); + Standard_Real distPrevVL = std::abs(thePoints2d(aPntIter - 1).Y() - vl); + Standard_Real distCurrVF = std::abs(thePoints2d(aPntIter).Y() - vf); + Standard_Real distCurrVL = std::abs(thePoints2d(aPntIter).Y() - vl); Standard_Real theMin = distPrevVF; prevOnFirst = Standard_True; @@ -1465,82 +1752,48 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa } if (distCurrVL < theMin) { - theMin = distCurrVL; prevOnFirst = Standard_False; prevOnLast = Standard_False; currOnFirst = Standard_False; currOnLast = Standard_True; } - // Modifs RLN/Nijni 3-DEC-1997 + if (prevOnFirst) { - // on decalle le point (aPntIter - 1) en V Last - gp_Pnt2d newPrev(pnt2d(aPntIter - 1).X(), vf); // instead of vl RLN/Nijni - pnt2d(aPntIter - 1) = newPrev; + thePoints2d.ChangeValue(aPntIter - 1).SetY(vf); } else if (prevOnLast) { - // on decalle le point (aPntIter - 1) en V first - gp_Pnt2d newPrev(pnt2d(aPntIter - 1).X(), vl); // instead of vf RLN/Nijni - pnt2d(aPntIter - 1) = newPrev; + thePoints2d.ChangeValue(aPntIter - 1).SetY(vl); } else if (currOnFirst) { - // on decalle le point (aPntIter) en V Last - gp_Pnt2d newCurr(pnt2d(aPntIter).X(), vf); // instead of vl RLN/Nijni - pnt2d(aPntIter) = newCurr; + thePoints2d.ChangeValue(aPntIter).SetY(vf); } else if (currOnLast) { - // on decalle le point (aPntIter) en V First - gp_Pnt2d newCurr(pnt2d(aPntIter).X(), vl); // instead of vf RLN/Nijni - pnt2d(aPntIter) = newCurr; + thePoints2d.ChangeValue(aPntIter).SetY(vl); } - // on verifie -#ifdef OCCT_DEBUG - dist2d = pnt2d(aPntIter - 1).Distance(pnt2d(aPntIter)); - if (dist2d > (Vp / 2)) - { - std::cout << "Echec dans le recadrage" << std::endl; - } -#endif } } } - //: c0 abv 20 Feb 98: treat very special case when 3d curve - // go over the pole of, e.g., sphere, and partly lies along seam. - // 2d representation of such a curve should consist of 3 parts - one on - // regular part of surface (interior), one part along degenerated boundary - // and one along seam. - // Since it cannot be adjusted later by arranging pcurves (curve is single), - // to fix it it's necessary to have a possibility of adjusting seam - // part of such curve either to left or right boundary of surface. - // Test is performed only if flag AdjustOverDegen is not -1. - // If AdjustOverDegen is True, seam part of curve is adjusted to - // the left, and if False - to the right parametric boundary - // If treated case is detected, flag DONE4 is set to status - // NOTE: currently, precision is Precision::PConfusion() since it - // is enough on encountered example - // (ug_turbine-A.stp from ProSTEP Benchmark #3, entities ##2470 & 5680) - // (r1001_ac.stp from Test Rally #10, face #35027 and others) + // Handle AdjustOverDegen if (myAdjustOverDegen != -1) { if (mySurf->IsUClosed(myPreci)) - { // #78 rln 12.03.99 S4135 - mySurf->IsDegenerated(gp_Pnt(0, 0, 0), myPreci); // pour calculer les dgnr + { + mySurf->IsDegenerated(gp_Pnt(0, 0, 0), myPreci); if (mySurf->NbSingularities(myPreci) > 0) - { // rln S4135 - // 1st, find gap point (degenerated pole) + { Standard_Real PrevX = 0.; Standard_Integer OnBound = 0, PrevOnBound = 0; - Standard_Integer ind; // svv #1 + Standard_Integer ind; Standard_Boolean start = Standard_True; - for (ind = 1; ind <= pnt2d.Length(); ind++) + for (ind = 1; ind <= thePoints2d.Length(); ind++) { - Standard_Real CurX = pnt2d(ind).X(); - // abv 16 Mar 00: trj3_s1-ug.stp #697: ignore points in singularity - if (mySurf->IsDegenerated(points(ind), Precision::Confusion())) + Standard_Real CurX = thePoints2d(ind).X(); + if (mySurf->IsDegenerated(thePoints(ind), Precision::Confusion())) continue; OnBound = (std::abs(std::abs(CurX - 0.5 * (ul + uf)) - Up / 2) <= Precision::PConfusion()); @@ -1550,33 +1803,44 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa PrevX = CurX; PrevOnBound = OnBound; } - // if found, adjust seam part - if (ind <= pnt2d.Length()) + if (ind <= thePoints2d.Length()) { PrevX = (myAdjustOverDegen ? uf : ul); Standard_Real dU = Up / 2 + Precision::PConfusion(); if (PrevOnBound) { - pnt2d(ind - 1).SetX(PrevX); + thePoints2d.ChangeValue(ind - 1).SetX(PrevX); for (Standard_Integer j = ind - 2; j > 0; j--) { - Standard_Real CurX = pnt2d(j).X(); + Standard_Real CurX = thePoints2d(j).X(); while (CurX < PrevX - dU) - pnt2d(j).SetX(CurX += Up); + { + CurX += Up; + thePoints2d.ChangeValue(j).SetX(CurX); + } while (CurX > PrevX + dU) - pnt2d(j).SetX(CurX -= Up); + { + CurX -= Up; + thePoints2d.ChangeValue(j).SetX(CurX); + } } } else if (OnBound) { - pnt2d(ind).SetX(PrevX); - for (Standard_Integer j = ind + 1; j <= pnt2d.Length(); j++) + thePoints2d.ChangeValue(ind).SetX(PrevX); + for (Standard_Integer j = ind + 1; j <= thePoints2d.Length(); j++) { - Standard_Real CurX = pnt2d(j).X(); + Standard_Real CurX = thePoints2d(j).X(); while (CurX < PrevX - dU) - pnt2d(j).SetX(CurX += Up); + { + CurX += Up; + thePoints2d.ChangeValue(j).SetX(CurX); + } while (CurX > PrevX + dU) - pnt2d(j).SetX(CurX -= Up); + { + CurX -= Up; + thePoints2d.ChangeValue(j).SetX(CurX); + } } } myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE4); @@ -1584,20 +1848,18 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa } } else if (mySurf->IsVClosed(myPreci)) - { // #78 rln 12.03.99 S4135 - mySurf->IsDegenerated(gp_Pnt(0, 0, 0), myPreci); // pour calculer les dgnr + { + mySurf->IsDegenerated(gp_Pnt(0, 0, 0), myPreci); if (mySurf->NbSingularities(myPreci) > 0) - { // rln S4135 - // 1st, find gap point (degenerated pole) + { Standard_Real PrevY = 0.; Standard_Integer OnBound = 0, PrevOnBound = 0; - Standard_Integer ind; // svv #1 + Standard_Integer ind; Standard_Boolean start = Standard_True; - for (ind = 1; ind <= pnt2d.Length(); ind++) + for (ind = 1; ind <= thePoints2d.Length(); ind++) { - Standard_Real CurY = pnt2d(ind).Y(); - // abv 16 Mar 00: trj3_s1-ug.stp #697: ignore points in singularity - if (mySurf->IsDegenerated(points(ind), Precision::Confusion())) + Standard_Real CurY = thePoints2d(ind).Y(); + if (mySurf->IsDegenerated(thePoints(ind), Precision::Confusion())) continue; OnBound = (std::abs(std::abs(CurY - 0.5 * (vl + vf)) - Vp / 2) <= Precision::PConfusion()); @@ -1607,33 +1869,44 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa PrevY = CurY; PrevOnBound = OnBound; } - // if found, adjust seam part - if (ind <= pnt2d.Length()) + if (ind <= thePoints2d.Length()) { PrevY = (myAdjustOverDegen ? vf : vl); Standard_Real dV = Vp / 2 + Precision::PConfusion(); if (PrevOnBound) { - pnt2d(ind - 1).SetY(PrevY); + thePoints2d.ChangeValue(ind - 1).SetY(PrevY); for (Standard_Integer j = ind - 2; j > 0; j--) { - Standard_Real CurY = pnt2d(j).Y(); + Standard_Real CurY = thePoints2d(j).Y(); while (CurY < PrevY - dV) - pnt2d(j).SetY(CurY += Vp); + { + CurY += Vp; + thePoints2d.ChangeValue(j).SetY(CurY); + } while (CurY > PrevY + dV) - pnt2d(j).SetY(CurY -= Vp); + { + CurY -= Vp; + thePoints2d.ChangeValue(j).SetY(CurY); + } } } else if (OnBound) { - pnt2d(ind).SetY(PrevY); - for (Standard_Integer j = ind + 1; j <= pnt2d.Length(); j++) + thePoints2d.ChangeValue(ind).SetY(PrevY); + for (Standard_Integer j = ind + 1; j <= thePoints2d.Length(); j++) { - Standard_Real CurY = pnt2d(j).Y(); + Standard_Real CurY = thePoints2d(j).Y(); while (CurY < PrevY - dV) - pnt2d(j).SetY(CurY += Vp); + { + CurY += Vp; + thePoints2d.ChangeValue(j).SetY(CurY); + } while (CurY > PrevY + dV) - pnt2d(j).SetY(CurY -= Vp); + { + CurY -= Vp; + thePoints2d.ChangeValue(j).SetY(CurY); + } } } myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE4); @@ -1642,238 +1915,69 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::ApproxPCurve(const Standa } } - //: q9: fill cache - myNbCashe = 2; - if (ChangeCycle) - { // msv 10.08.04: avoid using of uninitialised field - // if(myCashe3d[0].Distance(points(1))>Precision::Confusion() && - // myCashe3d[1].Distance(points(1))>Precision::Confusion()) { - myCashe3d[0] = points(1); - myCashe3d[1] = points.Last(); - myCashe2d[0] = pnt2d(1); - myCashe2d[1] = pnt2d.Last(); + // Fill cache + myCache.Resize(0, 1, Standard_False); + if (aChangeCycle) + { + myCache(0) = CachePoint(thePoints(1), thePoints2d(1)); + myCache(1) = CachePoint(thePoints.Last(), thePoints2d.Last()); } else { - myCashe3d[1] = points(1); - myCashe3d[0] = points.Last(); - myCashe2d[1] = pnt2d(1); - myCashe2d[0] = pnt2d.Last(); + myCache(0) = CachePoint(thePoints.Last(), thePoints2d.Last()); + myCache(1) = CachePoint(thePoints(1), thePoints2d(1)); } + return isDone; } //================================================================================================= -Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::ApproximatePCurve( - const Standard_Integer /*nbrPnt*/, - Handle(TColgp_HArray1OfPnt2d)& points2d, - Handle(TColStd_HArray1OfReal)& params, - const Handle(Geom_Curve)& /*orig*/) const -{ - // Standard_Real resol = std::min(mySurf->Adaptor3d()->VResolution(myPreci), - // mySurf->Adaptor3d()->UResolution(myPreci)); - Standard_Real theTolerance2d = myPreci; // (100*nbrPnt);//resol; - Handle(Geom2d_Curve) C2d; - try - { - OCC_CATCH_SIGNALS - CheckPoints2d(points2d, params, theTolerance2d); - Standard_Integer numberPnt = points2d->Length(); - - TColgp_Array1OfPnt points3d(1, numberPnt); - gp_Pnt2d pnt2d; - gp_Pnt pnt; - Standard_Integer i; // svv #1 - for (i = 1; i <= numberPnt; i++) - { - pnt2d = points2d->Value(i); - pnt.SetCoord(pnt2d.X(), pnt2d.Y(), 0); - points3d(i) = pnt; - } - - GeomAPI_PointsToBSpline appr(points3d, params->Array1(), 1, 10, GeomAbs_C1, theTolerance2d); - const Handle(Geom_BSplineCurve)& crv3d = appr.Curve(); - Standard_Integer NbPoles = crv3d->NbPoles(); - TColgp_Array1OfPnt poles3d(1, NbPoles); - TColgp_Array1OfPnt2d poles2d(1, NbPoles); - crv3d->Poles(poles3d); - for (i = 1; i <= NbPoles; i++) - { - pnt2d.SetCoord(poles3d(i).X(), poles3d(i).Y()); - poles2d(i) = pnt2d; - } - TColStd_Array1OfReal weights(1, NbPoles); - TColStd_Array1OfInteger multiplicities(1, crv3d->NbKnots()); - TColStd_Array1OfReal knots(1, crv3d->NbKnots()); - crv3d->Knots(knots); - crv3d->Weights(weights); - crv3d->Multiplicities(multiplicities); - C2d = new Geom2d_BSplineCurve(poles2d, - weights, - knots, - multiplicities, - crv3d->Degree(), - crv3d->IsPeriodic()); - return C2d; - } - catch (Standard_Failure const& anException) - { -#ifdef OCCT_DEBUG - //: s5 - // debug ... - Standard_Integer nbp = params->Length(); - Standard_Integer nb2 = points2d->Length(); - std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::ApproximatePCurve(): Exception: "; - anException.Print(std::cout); - std::cout << "Pb Geom2dAPI_Approximate, tol2d=" << theTolerance2d << " NbParams=" << nbp - << " NbPnts=" << nb2 << std::endl; -// if (nb2 > nbp) nb2 = nbp; -// Standard_Real rbp,rb2; rbp = nbp; rb2 = nb2; -// // dbl.AddString ("NbP2d/NbParams puis X Y Param -> mini"); -// dbl.AddReals (rb2,rbp); -// for (Standard_Integer i = 1; i <= nb2; i ++) { -// gp_XYZ quoi (points2d->Value(i).X(),points2d->Value(i).Y(),params->Value(i) ); -// dbl.AddXYZ (quoi); -// } -#endif - (void)anException; - C2d.Nullify(); - } - return C2d; -} - -//================================================================================================= - -Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::InterpolatePCurve( - const Standard_Integer nbrPnt, - Handle(TColgp_HArray1OfPnt2d)& points2d, - Handle(TColStd_HArray1OfReal)& params, - const Handle(Geom_Curve)& /*orig*/) const -{ - Handle(Geom2d_Curve) C2d; // NULL si echec - Standard_Real theTolerance2d = myPreci / (100 * nbrPnt); - try - { - OCC_CATCH_SIGNALS - // on verifie d abord s il n y a pas de points confondus - // si besoin on retouche les valeurs ... - CheckPoints2d(points2d, params, theTolerance2d); - Geom2dAPI_Interpolate myInterPol2d(points2d, params, Standard_False, theTolerance2d); - myInterPol2d.Perform(); - if (myInterPol2d.IsDone()) - C2d = myInterPol2d.Curve(); - } - catch (Standard_Failure const& anException) - { -#ifdef OCCT_DEBUG - //: s5 - // // debug ... - Standard_Integer nbp = params->Length(); - Standard_Integer nb2 = points2d->Length(); - std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::InterpolatePCurve(): Exception: "; - anException.Print(std::cout); - std::cout << "Pb Geom2dAPI_Interpolate, tol2d=" << theTolerance2d << " NbParams=" << nbp - << " NbPnts=" << nb2 << std::endl; -// if (nb2 > nbp) nb2 = nbp; -// Standard_Real rbp,rb2; rbp = nbp; rb2 = nb2; -// // dbl.AddString ("NbP2d/NbParams puis X Y Param -> mini"); -// dbl.AddReals (rb2,rbp); -// for (Standard_Integer i = 1; i <= nb2; i ++) { -// gp_XYZ quoi (points2d->Value(i).X(),points2d->Value(i).Y(),params->Value(i) ); -// dbl.AddXYZ (quoi); -// } -#endif - (void)anException; - C2d.Nullify(); - } - return C2d; -} - -//================================================================================================= - -Handle(Geom_Curve) ShapeConstruct_ProjectCurveOnSurface::InterpolateCurve3d( - const Standard_Integer, - Handle(TColgp_HArray1OfPnt)& points, - Handle(TColStd_HArray1OfReal)& params, - const Handle(Geom_Curve)& /*orig*/) const -{ - Handle(Geom_Curve) C3d; // NULL si echec - try - { - OCC_CATCH_SIGNALS - Standard_Real Tol = myPreci; - CheckPoints(points, params, Tol); - GeomAPI_Interpolate myInterPol(points, params, Standard_False, Tol); - myInterPol.Perform(); - if (myInterPol.IsDone()) - C3d = myInterPol.Curve(); - } - catch (Standard_Failure const& anException) - { -#ifdef OCCT_DEBUG - //: s5 - std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::InterpolateCurve3d(): Exception: "; - anException.Print(std::cout); - std::cout << std::endl; -#endif - (void)anException; - C3d.Nullify(); - } - return C3d; -} - -//============================================================================================ -// function : CorrectExtremity -// purpose : corrects first or last 2d point of future curve -// in the case when it coincids with a singularity of surface -//============================================================================================ - -void ShapeConstruct_ProjectCurveOnSurface::CorrectExtremity(const Handle(Geom_Curve)& theC3d, - const TColStd_SequenceOfReal& theParams, - TColgp_SequenceOfPnt2d& thePnt2d, +void ShapeConstruct_ProjectCurveOnSurface::correctExtremity(const Handle(Geom_Curve)& theC3D, + const ArrayOfReal& theParams, + ArrayOfPnt2d& thePoints2d, const Standard_Boolean theIsFirstPoint, const gp_Pnt2d& thePointOnIsoLine, - const Standard_Boolean theIsUiso) + const Standard_Boolean theIsUIso) { - Standard_Integer NbPnt = thePnt2d.Length(); - Standard_Integer IndCoord = (theIsUiso) ? 2 : 1; - Standard_Real SingularityCoord = thePointOnIsoLine.Coord(3 - IndCoord); - gp_Pnt2d EndPoint = (theIsFirstPoint) ? thePnt2d(1) : thePnt2d(NbPnt); - Standard_Real FinishCoord = EndPoint.Coord(3 - IndCoord); // the constant coord of isoline + const Standard_Integer NbPnt = thePoints2d.Length(); + const Standard_Integer IndCoord = (theIsUIso) ? 2 : 1; + const Standard_Real SingularityCoord = thePointOnIsoLine.Coord(3 - IndCoord); + gp_Pnt2d EndPoint = (theIsFirstPoint) ? thePoints2d(1) : thePoints2d(NbPnt); + const Standard_Real FinishCoord = EndPoint.Coord(3 - IndCoord); - gp_Dir2d aDir = (theIsUiso) ? gp::DY2d() : gp::DX2d(); + gp_Dir2d aDir = (theIsUIso) ? gp::DY2d() : gp::DX2d(); gp_Lin2d anIsoLine(EndPoint, aDir); IntRes2d_Domain Dom1, Dom2; - Standard_Boolean IsPeriodic = - (theIsUiso) ? mySurf->Surface()->IsVPeriodic() : mySurf->Surface()->IsUPeriodic(); + const Standard_Boolean IsPeriodic = + (theIsUIso) ? mySurf->Surface()->IsVPeriodic() : mySurf->Surface()->IsUPeriodic(); gp_Pnt2d FirstPointOfLine, SecondPointOfLine; Standard_Real FinishParam, FirstParam, SecondParam; if (theIsFirstPoint) { - FirstPointOfLine = thePnt2d(3); - SecondPointOfLine = thePnt2d(2); + FirstPointOfLine = thePoints2d(3); + SecondPointOfLine = thePoints2d(2); FinishParam = theParams(1); FirstParam = theParams(3); SecondParam = theParams(2); } - else // last point + else { - FirstPointOfLine = thePnt2d(NbPnt - 2); - SecondPointOfLine = thePnt2d(NbPnt - 1); + FirstPointOfLine = thePoints2d(NbPnt - 2); + SecondPointOfLine = thePoints2d(NbPnt - 1); FinishParam = theParams(NbPnt); FirstParam = theParams(NbPnt - 2); SecondParam = theParams(NbPnt - 1); } if (SingularityCoord > FinishCoord && SecondPointOfLine.Coord(3 - IndCoord) > FinishCoord) - return; // the curve passes through the singularity, do nothing + return; if (SingularityCoord < FinishCoord && SecondPointOfLine.Coord(3 - IndCoord) < FinishCoord) - return; // the curve passes through the singularity, do nothing - // Check correctness of + return; + { const Standard_Real aPrevDist = std::abs(SecondPointOfLine.Coord(IndCoord) - FirstPointOfLine.Coord(IndCoord)); @@ -1883,10 +1987,8 @@ void ShapeConstruct_ProjectCurveOnSurface::CorrectExtremity(const Handle(Geom_Cu return; } - gp_Pnt2d FinishPoint = - (theIsUiso) - ? gp_Pnt2d(FinishCoord, SecondPointOfLine.Y()) - : gp_Pnt2d(SecondPointOfLine.X(), FinishCoord); // first approximation of + gp_Pnt2d FinishPoint = (theIsUIso) ? gp_Pnt2d(FinishCoord, SecondPointOfLine.Y()) + : gp_Pnt2d(SecondPointOfLine.X(), FinishCoord); for (;;) { @@ -1894,8 +1996,8 @@ void ShapeConstruct_ProjectCurveOnSurface::CorrectExtremity(const Handle(Geom_Cu <= 2 * Precision::PConfusion()) break; - gp_Vec2d aVec(FirstPointOfLine, SecondPointOfLine); - Standard_Real aSqMagnitude = aVec.SquareMagnitude(); + gp_Vec2d aVec(FirstPointOfLine, SecondPointOfLine); + const Standard_Real aSqMagnitude = aVec.SquareMagnitude(); if (aSqMagnitude <= 1.e-32) break; aDir.SetCoord(aVec.X(), aVec.Y()); @@ -1908,7 +2010,7 @@ void ShapeConstruct_ProjectCurveOnSurface::CorrectExtremity(const Handle(Geom_Cu FinishPoint = IntPoint.Value(); } else - FinishPoint = (theIsUiso) ? gp_Pnt2d(FinishCoord, SecondPointOfLine.Y()) + FinishPoint = (theIsUIso) ? gp_Pnt2d(FinishCoord, SecondPointOfLine.Y()) : gp_Pnt2d(SecondPointOfLine.X(), FinishCoord); gp_Pnt2d PrevPoint = FirstPointOfLine; @@ -1918,15 +2020,12 @@ void ShapeConstruct_ProjectCurveOnSurface::CorrectExtremity(const Handle(Geom_Cu if (std::abs(SecondParam - FirstParam) <= 2 * Precision::PConfusion()) break; gp_Pnt aP3d; - theC3d->D0(SecondParam, aP3d); + theC3D->D0(SecondParam, aP3d); SecondPointOfLine = mySurf->NextValueOfUV(FirstPointOfLine, aP3d, myPreci, Precision::Confusion()); if (IsPeriodic) - AdjustSecondPointToFirstPoint(FirstPointOfLine, SecondPointOfLine, mySurf->Surface()); + adjustSecondToFirstPoint(FirstPointOfLine, SecondPointOfLine, mySurf->Surface()); - // Check to be enough close to - // because when a projected point is too close to singularity, - // the non-constant coordinate becomes random. const Standard_Real aPrevDist = std::abs(FirstPointOfLine.Coord(IndCoord) - PrevPoint.Coord(IndCoord)); const Standard_Real aCurDist = @@ -1936,296 +2035,419 @@ void ShapeConstruct_ProjectCurveOnSurface::CorrectExtremity(const Handle(Geom_Cu } if (theIsFirstPoint) - thePnt2d(1) = FinishPoint; + thePoints2d.ChangeValue(1) = FinishPoint; else - thePnt2d(NbPnt) = FinishPoint; + thePoints2d.ChangeValue(NbPnt) = FinishPoint; } -//============================================================================================ -// function : InsertAdditionalPointOrAdjust -// purpose : If the current point is too far from the previous point -// (more than half-period of surface), it can happen in two cases: -// 1. Real current step on corresponding coordinate is small, all we need is adjust; -// 2. Current step on corresponding coordinate is really bigger than half-period of -// surface in this parametric direction, so we must add additional point to exclude -// such big intervals between points in 2d space. -//============================================================================================ +//================================================================================================= -void ShapeConstruct_ProjectCurveOnSurface::InsertAdditionalPointOrAdjust( - Standard_Boolean& ToAdjust, +void ShapeConstruct_ProjectCurveOnSurface::insertAdditionalPointOrAdjust( + Standard_Boolean& theToAdjust, const Standard_Integer theIndCoord, - const Standard_Real Period, - const Standard_Real TolOnPeriod, - Standard_Real& CurCoord, - const Standard_Real prevCoord, - const Handle(Geom_Curve)& c3d, + const Standard_Real thePeriod, + const Standard_Real theTolOnPeriod, + Standard_Real& theCurCoord, + const Standard_Real thePrevCoord, + const Handle(Geom_Curve)& theC3D, Standard_Integer& theIndex, - TColgp_SequenceOfPnt& points, - TColStd_SequenceOfReal& params, - TColgp_SequenceOfPnt2d& pnt2d) + ArrayOfPnt& thePoints, + ArrayOfReal& theParams, + ArrayOfPnt2d& thePoints2d) { - Standard_Real CorrectedCurCoord = - ElCLib::InPeriod(CurCoord, prevCoord - Period / 2, prevCoord + Period / 2); - if (!ToAdjust) + const Standard_Real CorrectedCurCoord = + ElCLib::InPeriod(theCurCoord, thePrevCoord - thePeriod / 2, thePrevCoord + thePeriod / 2); + + if (!theToAdjust) { - Standard_Real CurPar = params(theIndex); - Standard_Real PrevPar = params(theIndex - 1); - Standard_Real MidPar = (PrevPar + CurPar) / 2; - gp_Pnt MidP3d; - c3d->D0(MidPar, MidP3d); + const Standard_Real CurPar = theParams(theIndex); + const Standard_Real PrevPar = theParams(theIndex - 1); + Standard_Real MidPar = (PrevPar + CurPar) / 2; + gp_Pnt MidP3d; + theC3D->D0(MidPar, MidP3d); gp_Pnt2d MidP2d = mySurf->ValueOfUV(MidP3d, myPreci); Standard_Real MidCoord = MidP2d.Coord(theIndCoord); - MidCoord = ElCLib::InPeriod(MidCoord, prevCoord - Period / 2, prevCoord + Period / 2); - Standard_Real FirstCoord = prevCoord, LastCoord = CorrectedCurCoord; + MidCoord = + ElCLib::InPeriod(MidCoord, thePrevCoord - thePeriod / 2, thePrevCoord + thePeriod / 2); + Standard_Real FirstCoord = thePrevCoord, LastCoord = CorrectedCurCoord; if (LastCoord < FirstCoord) { Standard_Real tmp = FirstCoord; FirstCoord = LastCoord; LastCoord = tmp; } - if (LastCoord - FirstCoord <= TolOnPeriod) - ToAdjust = Standard_True; + if (LastCoord - FirstCoord <= theTolOnPeriod) + theToAdjust = Standard_True; else if (FirstCoord <= MidCoord && MidCoord <= LastCoord) - ToAdjust = Standard_True; - else // add mid point + theToAdjust = Standard_True; + else { - // Standard_Real RefU = prevX; Standard_Boolean Success = Standard_True; - Standard_Real FirstT = PrevPar; // params(i-1) - Standard_Real LastT = CurPar; // params(i) + Standard_Real FirstT = PrevPar; + Standard_Real LastT = CurPar; MidCoord = MidP2d.Coord(theIndCoord); - while (std::abs(MidCoord - prevCoord) >= Period / 2 - TolOnPeriod - || std::abs(CurCoord - MidCoord) >= Period / 2 - TolOnPeriod) + while (std::abs(MidCoord - thePrevCoord) >= thePeriod / 2 - theTolOnPeriod + || std::abs(theCurCoord - MidCoord) >= thePeriod / 2 - theTolOnPeriod) { if (MidPar - FirstT <= Precision::PConfusion() || LastT - MidPar <= Precision::PConfusion()) { Success = Standard_False; - break; // wrong choice + break; } - if (std::abs(MidCoord - prevCoord) >= Period / 2 - TolOnPeriod) + if (std::abs(MidCoord - thePrevCoord) >= thePeriod / 2 - theTolOnPeriod) LastT = (FirstT + LastT) / 2; else FirstT = (FirstT + LastT) / 2; MidPar = (FirstT + LastT) / 2; - c3d->D0(MidPar, MidP3d); + theC3D->D0(MidPar, MidP3d); MidP2d = mySurf->ValueOfUV(MidP3d, myPreci); MidCoord = MidP2d.Coord(theIndCoord); } if (Success) { - points.InsertBefore(theIndex, MidP3d); - params.InsertBefore(theIndex, MidPar); - pnt2d.InsertBefore(theIndex, MidP2d); + // Insert additional point - need to resize arrays + const Standard_Integer aNewLength = thePoints.Length() + 1; + ArrayOfPnt aNewPoints(1, aNewLength); + ArrayOfReal aNewParams(1, aNewLength); + ArrayOfPnt2d aNewPoints2d(1, aNewLength); + + for (Standard_Integer i = 1; i < theIndex; i++) + { + aNewPoints.SetValue(i, thePoints(i)); + aNewParams.SetValue(i, theParams(i)); + aNewPoints2d.SetValue(i, thePoints2d(i)); + } + aNewPoints.SetValue(theIndex, MidP3d); + aNewParams.SetValue(theIndex, MidPar); + aNewPoints2d.SetValue(theIndex, MidP2d); + for (Standard_Integer i = theIndex; i <= thePoints.Length(); i++) + { + aNewPoints.SetValue(i + 1, thePoints(i)); + aNewParams.SetValue(i + 1, theParams(i)); + aNewPoints2d.SetValue(i + 1, thePoints2d(i)); + } + + thePoints = aNewPoints; + theParams = aNewParams; + thePoints2d = aNewPoints2d; theIndex++; } else - ToAdjust = Standard_True; - } // add mid point - } // if (!ToAdjust) - if (ToAdjust) + theToAdjust = Standard_True; + } + } + if (theToAdjust) { - CurCoord = CorrectedCurCoord; - pnt2d(theIndex).SetCoord(theIndCoord, CurCoord); + theCurCoord = CorrectedCurCoord; + thePoints2d.ChangeValue(theIndex).SetCoord(theIndCoord, theCurCoord); } } //================================================================================================= -void ShapeConstruct_ProjectCurveOnSurface::CheckPoints(Handle(TColgp_HArray1OfPnt)& points, - Handle(TColStd_HArray1OfReal)& params, - Standard_Real& preci) const +Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::interpolatePCurve( + const Standard_Integer theNbPnt, + const ArrayOfPnt2d& thePoints2d, + const ArrayOfReal& theParams) const { - Standard_Integer firstElem = points->Lower(); - Standard_Integer lastElem = points->Upper(); - Standard_Integer i; - Standard_Integer nbPntDropped = 0; - Standard_Integer lastValid = firstElem; // indice of last undropped point + Handle(Geom2d_Curve) aC2D; + const Standard_Real theTolerance2d = myPreci / (100 * theNbPnt); - // will store 0 when the point is to be removed, 1 otherwise - TColStd_Array1OfInteger tmpParam(firstElem, lastElem); - for (i = firstElem; i <= lastElem; i++) - tmpParam.SetValue(i, 1); - Standard_Real DistMin2 = RealLast(); - gp_Pnt Prev = points->Value(lastValid); - gp_Pnt Curr; - for (i = firstElem + 1; i <= lastElem; i++) - { - Curr = points->Value(i); - Standard_Real CurDist2 = Prev.SquareDistance(Curr); - if (CurDist2 < gp::Resolution()) - { // test 0 - nbPntDropped++; - if (i == lastElem) - tmpParam.SetValue(lastValid, 0); // last point kept - else - tmpParam.SetValue(i, 0); // current dropped, lastValid unchanged - } - else - { - if (CurDist2 < DistMin2) - DistMin2 = CurDist2; - // lastValid becomes the current (i.e. i) - lastValid = i; - Prev = Curr; - } - } - if (DistMin2 < RealLast()) - // clang-format off - preci = 0.9 * std::sqrt(DistMin2); // preci est la distance min entre les points on la reduit un peu - // clang-format on - if (nbPntDropped == 0) - return; - -#ifdef OCCT_DEBUG - std::cout << "Warning : removing 3d points for interpolation" << std::endl; -#endif - // Build new HArrays - Standard_Integer newLast = lastElem - nbPntDropped; - if ((newLast - firstElem + 1) < 2) - { -#ifdef OCCT_DEBUG - std::cout << "Too many degenerated points for 3D interpolation" << std::endl; -#endif - return; - } - Handle(TColgp_HArray1OfPnt) newPnts = new TColgp_HArray1OfPnt(firstElem, newLast); - Handle(TColStd_HArray1OfReal) newParams = new TColStd_HArray1OfReal(firstElem, newLast); - Standard_Integer newCurr = 1; - for (i = firstElem; i <= lastElem; i++) - { - if (tmpParam.Value(i) == 1) - { - newPnts->SetValue(newCurr, points->Value(i)); - newParams->SetValue(newCurr, params->Value(i)); - newCurr++; - } - } - points = newPnts; - params = newParams; - // on la reduit un peu -} - -//================================================================================================= - -void ShapeConstruct_ProjectCurveOnSurface::CheckPoints2d(Handle(TColgp_HArray1OfPnt2d)& points, - Handle(TColStd_HArray1OfReal)& params, - Standard_Real& preci) const -{ - Standard_Integer firstElem = points->Lower(); - Standard_Integer lastElem = points->Upper(); - Standard_Integer i; - Standard_Integer nbPntDropped = 0; - Standard_Integer lastValid = firstElem; // indice of last undropped point - - // will store 0 when the point is to be removed, 1 otherwise - TColStd_Array1OfInteger tmpParam(firstElem, lastElem); - for (i = firstElem; i <= lastElem; i++) - { - tmpParam.SetValue(i, 1); - } - Standard_Real DistMin2 = RealLast(); - gp_Pnt2d Prev = points->Value(lastValid); - gp_Pnt2d Curr; - for (i = firstElem + 1; i <= lastElem; i++) - { - Curr = points->Value(i); - Standard_Real CurDist2 = Prev.SquareDistance(Curr); - if (CurDist2 < gp::Resolution()) - { // test 0 - nbPntDropped++; - if (i == lastElem) - tmpParam.SetValue(lastValid, 0); // last point kept - else - tmpParam.SetValue(i, 0); // current dropped, lastValid unchanged - } - else - { - if (CurDist2 < DistMin2) - DistMin2 = CurDist2; - // lastValid becomes the current (i.e. i) - lastValid = i; - Prev = Curr; - } - } - if (DistMin2 < RealLast()) - preci = 0.9 * std::sqrt(DistMin2); - if (nbPntDropped == 0) - return; - -#ifdef OCCT_DEBUG - std::cout << "Warning : removing 2d points for interpolation" << std::endl; -#endif - // Build new HArrays - Standard_Integer newLast = lastElem - nbPntDropped; - if ((newLast - firstElem + 1) < 2) - { -#ifdef OCCT_DEBUG - std::cout << "Too many degenerated points for 2D interpolation" << std::endl; -#endif - // pdn 12.02.99 S4135 Creating pcurve with minimal length. - tmpParam.SetValue(firstElem, 1); - tmpParam.SetValue(lastElem, 1); - gp_XY lastPnt = points->Value(lastElem).XY(); - lastPnt.Add(gp_XY(preci, preci)); - points->SetValue(lastElem, lastPnt); - newLast = firstElem + 1; - // return; - } - Handle(TColgp_HArray1OfPnt2d) newPnts = new TColgp_HArray1OfPnt2d(firstElem, newLast); - Handle(TColStd_HArray1OfReal) newParams = new TColStd_HArray1OfReal(firstElem, newLast); - Standard_Integer newCurr = 1; - for (i = firstElem; i <= lastElem; i++) - { - if (tmpParam.Value(i) == 1) - { -#ifdef OCCT_DEBUG - std::cout << "Point " << i << " : " << points->Value(i).X() << " " << points->Value(i).Y() - << " at param " << params->Value(i) << std::endl; -#endif - newPnts->SetValue(newCurr, points->Value(i)); - newParams->SetValue(newCurr, params->Value(i)); - newCurr++; - } - else - { -#ifdef OCCT_DEBUG - std::cout << "Removed " << i << " : " << points->Value(i).X() << " " << points->Value(i).Y() - << " at param " << params->Value(i) << std::endl; -#endif - } - } - points = newPnts; - params = newParams; -} - -//================================================================================================= - -//: S4030: modified for optimization -//: p9 abv 11 Mar 99: PRO7226 #489490: find nearest boundary instead of first one - -Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( - const Standard_Integer nbrPnt, - const TColgp_SequenceOfPnt& points, - const TColStd_SequenceOfReal& params, - Standard_Boolean& isoTypeU, - Standard_Boolean& p1OnIso, - gp_Pnt2d& valueP1, - Standard_Boolean& p2OnIso, - gp_Pnt2d& valueP2, - Standard_Boolean& isoPar2d3d, - Handle(Geom_Curve)& cIso, - Standard_Real& t1, - Standard_Real& t2, - TColStd_Array1OfReal& pout) const -{ try - { // RAJOUT + { OCC_CATCH_SIGNALS - constexpr Standard_Real prec = Precision::Confusion(); // myPreci; + // Convert to HArrays for Geom2dAPI_Interpolate + Handle(TColgp_HArray1OfPnt2d) aPnts2d = new TColgp_HArray1OfPnt2d(1, theNbPnt); + Handle(TColStd_HArray1OfReal) aParams = new TColStd_HArray1OfReal(1, theNbPnt); + + for (Standard_Integer i = 1; i <= theNbPnt; i++) + { + aPnts2d->SetValue(i, thePoints2d(i)); + aParams->SetValue(i, theParams(i)); + } + + // Check for coincident points + ArrayOfPnt2d aTmpPnts2d = thePoints2d; + ArrayOfReal aTmpParams = theParams; + Standard_Real aPreci = theTolerance2d; + checkPoints2d(aTmpPnts2d, aTmpParams, aPreci); + + if (aTmpPnts2d.Length() < 2) + return aC2D; + + // Rebuild HArrays if points were removed + if (aTmpPnts2d.Length() != theNbPnt) + { + aPnts2d = new TColgp_HArray1OfPnt2d(1, aTmpPnts2d.Length()); + aParams = new TColStd_HArray1OfReal(1, aTmpParams.Length()); + for (Standard_Integer i = 1; i <= aTmpPnts2d.Length(); i++) + { + aPnts2d->SetValue(i, aTmpPnts2d(i)); + aParams->SetValue(i, aTmpParams(i)); + } + } + + Geom2dAPI_Interpolate myInterPol2d(aPnts2d, aParams, Standard_False, aPreci); + myInterPol2d.Perform(); + if (myInterPol2d.IsDone()) + aC2D = myInterPol2d.Curve(); + } + catch (Standard_Failure const& anException) + { +#ifdef OCCT_DEBUG + std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::interpolatePCurve(): Exception: "; + anException.Print(std::cout); + std::cout << std::endl; +#endif + (void)anException; + aC2D.Nullify(); + } + return aC2D; +} + +//================================================================================================= + +Handle(Geom2d_Curve) ShapeConstruct_ProjectCurveOnSurface::approximatePCurve( + const ArrayOfPnt2d& thePoints2d, + const ArrayOfReal& theParams) const +{ + const Standard_Real theTolerance2d = myPreci; + Handle(Geom2d_Curve) aC2D; + + try + { + OCC_CATCH_SIGNALS + + ArrayOfPnt2d aTmpPnts2d = thePoints2d; + ArrayOfReal aTmpParams = theParams; + Standard_Real aPreci = theTolerance2d; + checkPoints2d(aTmpPnts2d, aTmpParams, aPreci); + + const Standard_Integer numberPnt = aTmpPnts2d.Length(); + if (numberPnt < 2) + return aC2D; + + TColgp_Array1OfPnt points3d(1, numberPnt); + TColStd_Array1OfReal params(1, numberPnt); + + for (Standard_Integer i = 1; i <= numberPnt; i++) + { + const gp_Pnt2d& pnt2d = aTmpPnts2d(i); + points3d(i).SetCoord(pnt2d.X(), pnt2d.Y(), 0); + params(i) = aTmpParams(i); + } + + GeomAPI_PointsToBSpline appr(points3d, params, 1, 10, GeomAbs_C1, theTolerance2d); + const Handle(Geom_BSplineCurve)& crv3d = appr.Curve(); + + const Standard_Integer NbPoles = crv3d->NbPoles(); + TColgp_Array1OfPnt poles3d(1, NbPoles); + TColgp_Array1OfPnt2d poles2d(1, NbPoles); + crv3d->Poles(poles3d); + + for (Standard_Integer i = 1; i <= NbPoles; i++) + { + poles2d(i).SetCoord(poles3d(i).X(), poles3d(i).Y()); + } + + TColStd_Array1OfReal weights(1, NbPoles); + TColStd_Array1OfInteger multiplicities(1, crv3d->NbKnots()); + TColStd_Array1OfReal knots(1, crv3d->NbKnots()); + crv3d->Knots(knots); + crv3d->Weights(weights); + crv3d->Multiplicities(multiplicities); + + aC2D = new Geom2d_BSplineCurve(poles2d, + weights, + knots, + multiplicities, + crv3d->Degree(), + crv3d->IsPeriodic()); + return aC2D; + } + catch (Standard_Failure const& anException) + { +#ifdef OCCT_DEBUG + std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::approximatePCurve(): Exception: "; + anException.Print(std::cout); + std::cout << std::endl; +#endif + (void)anException; + aC2D.Nullify(); + } + return aC2D; +} + +//================================================================================================= + +void ShapeConstruct_ProjectCurveOnSurface::checkPoints(ArrayOfPnt& thePoints, + ArrayOfReal& theParams, + Standard_Real& thePreci) const +{ + const Standard_Integer firstElem = thePoints.Lower(); + const Standard_Integer lastElem = thePoints.Upper(); + + Standard_Integer nbPntDropped = 0; + Standard_Integer lastValid = firstElem; + + NCollection_Array1 tmpParam(firstElem, lastElem); + for (Standard_Integer i = firstElem; i <= lastElem; i++) + tmpParam.SetValue(i, 1); + + Standard_Real DistMin2 = RealLast(); + gp_Pnt Prev = thePoints.Value(lastValid); + + for (Standard_Integer i = firstElem + 1; i <= lastElem; i++) + { + const gp_Pnt& Curr = thePoints.Value(i); + const Standard_Real CurDist2 = Prev.SquareDistance(Curr); + if (CurDist2 < gp::Resolution()) + { + nbPntDropped++; + if (i == lastElem) + tmpParam.SetValue(lastValid, 0); + else + tmpParam.SetValue(i, 0); + } + else + { + if (CurDist2 < DistMin2) + DistMin2 = CurDist2; + lastValid = i; + Prev = Curr; + } + } + + if (DistMin2 < RealLast()) + thePreci = 0.9 * std::sqrt(DistMin2); + + if (nbPntDropped == 0) + return; + + const Standard_Integer newLast = lastElem - nbPntDropped; + if ((newLast - firstElem + 1) < 2) + return; + + ArrayOfPnt newPnts(firstElem, newLast); + ArrayOfReal newParams(firstElem, newLast); + Standard_Integer newCurr = firstElem; + + for (Standard_Integer i = firstElem; i <= lastElem; i++) + { + if (tmpParam.Value(i) == 1) + { + newPnts.SetValue(newCurr, thePoints.Value(i)); + newParams.SetValue(newCurr, theParams.Value(i)); + newCurr++; + } + } + + thePoints = newPnts; + theParams = newParams; +} + +//================================================================================================= + +void ShapeConstruct_ProjectCurveOnSurface::checkPoints2d(ArrayOfPnt2d& thePoints2d, + ArrayOfReal& theParams, + Standard_Real& thePreci) const +{ + const Standard_Integer firstElem = thePoints2d.Lower(); + const Standard_Integer lastElem = thePoints2d.Upper(); + + Standard_Integer nbPntDropped = 0; + Standard_Integer lastValid = firstElem; + + NCollection_Array1 tmpParam(firstElem, lastElem); + for (Standard_Integer i = firstElem; i <= lastElem; i++) + tmpParam.SetValue(i, 1); + + Standard_Real DistMin2 = RealLast(); + gp_Pnt2d Prev = thePoints2d.Value(lastValid); + + for (Standard_Integer i = firstElem + 1; i <= lastElem; i++) + { + const gp_Pnt2d& Curr = thePoints2d.Value(i); + const Standard_Real CurDist2 = Prev.SquareDistance(Curr); + if (CurDist2 < gp::Resolution()) + { + nbPntDropped++; + if (i == lastElem) + tmpParam.SetValue(lastValid, 0); + else + tmpParam.SetValue(i, 0); + } + else + { + if (CurDist2 < DistMin2) + DistMin2 = CurDist2; + lastValid = i; + Prev = Curr; + } + } + + if (DistMin2 < RealLast()) + thePreci = 0.9 * std::sqrt(DistMin2); + + if (nbPntDropped == 0) + return; + + Standard_Integer newLast = lastElem - nbPntDropped; + if ((newLast - firstElem + 1) < 2) + { + // Create minimal length pcurve + tmpParam.SetValue(firstElem, 1); + tmpParam.SetValue(lastElem, 1); + gp_XY lastPnt = thePoints2d.Value(lastElem).XY(); + lastPnt.Add(gp_XY(thePreci, thePreci)); + thePoints2d.ChangeValue(lastElem).SetXY(lastPnt); + newLast = firstElem + 1; + } + + ArrayOfPnt2d newPnts(firstElem, newLast); + ArrayOfReal newParams(firstElem, newLast); + Standard_Integer newCurr = firstElem; + + for (Standard_Integer i = firstElem; i <= lastElem; i++) + { + if (tmpParam.Value(i) == 1) + { + newPnts.SetValue(newCurr, thePoints2d.Value(i)); + newParams.SetValue(newCurr, theParams.Value(i)); + newCurr++; + } + } + + thePoints2d = std::move(newPnts); + theParams = std::move(newParams); +} + +//================================================================================================= + +Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::isAnIsoparametric( + const Standard_Integer theNbPnt, + const ArrayOfPnt& thePoints, + const ArrayOfReal& theParams, + Standard_Boolean& theIsTypeU, + Standard_Boolean& theP1OnIso, + gp_Pnt2d& theValueP1, + Standard_Boolean& theP2OnIso, + gp_Pnt2d& theValueP2, + Standard_Boolean& theIsoPar2d3d, + Handle(Geom_Curve)& theCIso, + Standard_Real& theT1, + Standard_Real& theT2, + ArrayOfReal& theParamsOut) const +{ + try + { + OCC_CATCH_SIGNALS + + constexpr Standard_Real prec = Precision::Confusion(); Standard_Boolean isoParam = Standard_False; - isoPar2d3d = Standard_False; + theIsoPar2d3d = Standard_False; Standard_Real U1, U2, V1, V2; mySurf->Bounds(U1, U2, V1, V2); @@ -2245,14 +2467,14 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( Standard_Real mind2[2]; mindist2 = mind2[0] = mind2[1] = 4 * prec * prec; - p1OnIso = Standard_False; - p2OnIso = Standard_False; + theP1OnIso = Standard_False; + theP2OnIso = Standard_False; const Bnd_Box* aBox = 0; - for (Standard_Integer j = 1; (j <= 4) /*&& !isoParam*/; j++) + for (Standard_Integer j = 1; j <= 4; j++) { - Standard_Real isoVal = 0.; - Standard_Boolean isoU = Standard_False; // szv#4:S4163:12Mar99 `isoU` must be Standard_Boolean + Standard_Real isoVal = 0.; + Standard_Boolean isoU = Standard_False; Handle(Geom_Curve) cI; Standard_Real tt1, tt2; @@ -2310,7 +2532,6 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( cI->D0(tt1, ext1); cI->D0(tt2, ext2); - // PATCH CKY 9-JUL-1998 : protection contre singularite gp_Pnt extmi; cI->D0((tt1 + tt2) / 2, extmi); if (ext1.IsEqual(ext2, prec) && ext1.IsEqual(extmi, prec)) @@ -2324,11 +2545,10 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( for (Standard_Integer i = 0; i < 2; i++) { - mp[i] = 0; - Standard_Integer k = (i == 0 ? 1 : nbrPnt); + mp[i] = 0; + const Standard_Integer k = (i == 0 ? 1 : theNbPnt); - // si ext1 == ext2 => valueP1 == valueP2 => vect null plus tard - currd2[i] = points(k).SquareDistance(ext1); + currd2[i] = thePoints(k).SquareDistance(ext1); if (currd2[i] <= prec * prec && !PtEQext1) { mp[i] = 1; @@ -2337,7 +2557,7 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( continue; } - currd2[i] = points(k).SquareDistance(ext2); + currd2[i] = thePoints(k).SquareDistance(ext2); if (currd2[i] <= prec * prec && !PtEQext2) { mp[i] = 2; @@ -2346,14 +2566,12 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( continue; } - // On evite de projecter sur un iso degenere - // on doit egalement le faire pour l apex du cone if (mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface)) && !isoU) { continue; } - if (aBox->IsOut(points(k))) + if (aBox->IsOut(thePoints(k))) continue; Standard_Real Cf = cI->FirstParameter(); @@ -2364,7 +2582,7 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( Cl = +1000; ShapeAnalysis_Curve sac; - Standard_Real dist = sac.Project(cI, points(k), prec, pt, t, Cf, Cl); + const Standard_Real dist = sac.Project(cI, thePoints(k), prec, pt, t, Cf, Cl); currd2[i] = dist * dist; if ((dist <= prec) && (t >= Cf) && (t <= Cl)) { @@ -2373,114 +2591,98 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( } } - //: e7 abv 21 Apr 98: ProSTEP TR8, r0501_pe #56679: - // avoid possible null-length curves if (mp[0] > 0 && mp[1] > 0 && std::abs(tp[0] - tp[1]) < Precision::PConfusion()) continue; - if (mp[0] > 0 && (!p1OnIso || currd2[0] < mind2[0])) + if (mp[0] > 0 && (!theP1OnIso || currd2[0] < mind2[0])) { - p1OnIso = Standard_True; - // clang-format off - mind2[0] = currd2[0]; // LP2.stp #105899: FLT_INVALID_OPERATION on Windows 7 VC 9 Release mode on the whole file - // clang-format on + theP1OnIso = Standard_True; + mind2[0] = currd2[0]; if (isoU) - valueP1.SetCoord(isoVal, tp[0]); + theValueP1.SetCoord(isoVal, tp[0]); else - valueP1.SetCoord(tp[0], isoVal); + theValueP1.SetCoord(tp[0], isoVal); } - if (mp[1] > 0 && (!p2OnIso || currd2[1] < mind2[1])) + if (mp[1] > 0 && (!theP2OnIso || currd2[1] < mind2[1])) { - p2OnIso = Standard_True; - mind2[1] = currd2[1]; + theP2OnIso = Standard_True; + mind2[1] = currd2[1]; if (isoU) - valueP2.SetCoord(isoVal, tp[1]); + theValueP2.SetCoord(isoVal, tp[1]); else - valueP2.SetCoord(tp[1], isoVal); + theValueP2.SetCoord(tp[1], isoVal); } if (mp[0] <= 0 || mp[1] <= 0) continue; - Standard_Real md2 = currd2[0] + currd2[1]; + const Standard_Real md2 = currd2[0] + currd2[1]; if (mindist2 <= md2) continue; - mindist2 = md2; - mpt[0] = mp[0]; - mpt[1] = mp[1]; - tpar[0] = tp[0]; - tpar[1] = tp[1]; - isoTypeU = isoU; - isoValue = isoVal; - cIso = cI; - t1 = tt1; - t2 = tt2; + mindist2 = md2; + mpt[0] = mp[0]; + mpt[1] = mp[1]; + tpar[0] = tp[0]; + tpar[1] = tp[1]; + theIsTypeU = isoU; + isoValue = isoVal; + theCIso = cI; + theT1 = tt1; + theT2 = tt2; } - // probablely it concerns an isoparametrics if (mpt[0] > 0 && mpt[1] > 0) { - - p1OnIso = p2OnIso = Standard_True; - if (isoTypeU) + theP1OnIso = theP2OnIso = Standard_True; + if (theIsTypeU) { - valueP1.SetCoord(isoValue, tpar[0]); - valueP2.SetCoord(isoValue, tpar[1]); + theValueP1.SetCoord(isoValue, tpar[0]); + theValueP2.SetCoord(isoValue, tpar[1]); } else { - valueP1.SetCoord(tpar[0], isoValue); - valueP2.SetCoord(tpar[1], isoValue); + theValueP1.SetCoord(tpar[0], isoValue); + theValueP2.SetCoord(tpar[1], isoValue); } if (mpt[0] != 3 && mpt[1] != 3) { - isoPar2d3d = Standard_True; - for (Standard_Integer i = 2; i < nbrPnt && isoPar2d3d; i++) + theIsoPar2d3d = Standard_True; + for (Standard_Integer i = 2; i < theNbPnt && theIsoPar2d3d; i++) { if (tpar[1] > tpar[0]) - t = params(i); + t = theParams(i); else - t = t1 + t2 - params(i); - cIso->D0(t, pt); - if (!points(i).IsEqual(pt, prec)) - isoPar2d3d = Standard_False; + t = theT1 + theT2 - theParams(i); + theCIso->D0(t, pt); + if (!thePoints(i).IsEqual(pt, prec)) + theIsoPar2d3d = Standard_False; } } - if (isoPar2d3d) + if (theIsoPar2d3d) isoParam = Standard_True; else { Standard_Real prevParam = tpar[0]; Standard_Real Cf, Cl; Standard_Boolean isoByDistance = Standard_True; - Cf = cIso->FirstParameter(); - Cl = cIso->LastParameter(); + Cf = theCIso->FirstParameter(); + Cl = theCIso->LastParameter(); if (Precision::IsInfinite(Cf)) Cf = -1000; if (Precision::IsInfinite(Cl)) Cl = +1000; ShapeAnalysis_Curve sac; - for (Standard_Integer i = 2; i < nbrPnt && isoByDistance; i++) + for (Standard_Integer i = 2; i < theNbPnt && isoByDistance; i++) { - Standard_Real dist = sac.NextProject( - prevParam, - cIso, - points(i), - prec, - pt, - t, - Cf, - Cl, - // clang-format off - Standard_False); //:j8 abv 10.12.98: TR10 r0501_db.stp #9423: avoid adjusting to ends - // clang-format on - prevParam = t; - pout(i) = t; + const Standard_Real dist = + sac.NextProject(prevParam, theCIso, thePoints(i), prec, pt, t, Cf, Cl, Standard_False); + prevParam = t; + theParamsOut(i) = t; if ((dist > prec) || (t < Cf) || (t > Cl)) isoByDistance = Standard_False; } @@ -2488,22 +2690,12 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( isoParam = Standard_True; } } - /* if (!isoParam) { CKY 29-mai-1997 : garder tout ce qu on peut ? - p1OnIso = Standard_False; - p2OnIso = Standard_False; - } */ return isoParam; - } // RAJOUT + } catch (Standard_Failure const& anException) { #ifdef OCCT_DEBUG - // pb : on affiche ce qu on peut - for (Standard_Integer numpnt = 1; numpnt <= nbrPnt; numpnt++) - { - std::cout << "[" << numpnt << "]param=" << params(numpnt) << " point=(" << points(numpnt).X() - << " " << points(numpnt).Y() << " " << points(numpnt).Z() << ")" << std::endl; - } - std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric(): Exception: "; + std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::isAnIsoparametric(): Exception: "; anException.Print(std::cout); std::cout << std::endl; #endif @@ -2511,36 +2703,3 @@ Standard_Boolean ShapeConstruct_ProjectCurveOnSurface::IsAnIsoparametric( return Standard_False; } } - -/* S4135 : BestExtremum is commented after IsAnIsoparametric works with Precision::Confusion() -//======================================================================= -//function : BestExtremum -//purpose : auxiliaire prenant le meilleur extremum si ISO car doute possible -//======================================================================= - - gp_Pnt2d ShapeConstruct_ProjectCurveOnSurface::BestExtremum(const gp_Pnt2d& P2iso,const gp_Pnt& -P3ext,const gp_Pnt& P3next) const -{ -// P2iso a ete calcule depuis P3ext sur une iso externe de la surface -// En principe bon mais circularite possible ... et IsU/VClosed faillible -// (si baillement 1e-4 ou 1e-5, on est dedans !). DONC -// 1/ on privilegie l iso mais a tout hasard on verifie si Surf meilleur -// 2/ si iso, attention a la circularite (cas limite) - -// NB : si isoParam, on suppose que P2iso est bon (car il y en a 2). A voir... - -// D abord, calcul p2ext depuis la surface. choix surface/iso - return P2iso; - Standard_Real prec = Precision::Confusion();//myPreci; - gp_Pnt2d P2cal = mySurf->ValueOfUV(P3ext, prec); - gp_Pnt P3cal = mySurf->Value (P2cal); - Standard_Real dcal = P3ext.Distance (P3cal); - Standard_Real dnxt = P3ext.Distance (P3next); - if (dcal > dnxt) return P2iso; // en fait protection sur BUG (PRO8468) - -// On choisit entre P2iso et P2cal, le plus proche de P2next ... !!! - gp_Pnt2d P2next = mySurf->ValueOfUV(P3next, prec); - if (P2next.Distance(P2cal) < P2next.Distance(P2iso)) return P2cal; - return P2iso; -} -*/ diff --git a/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.hxx b/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.hxx index 637cfa51b2..f016c75a0a 100644 --- a/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.hxx +++ b/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.hxx @@ -20,24 +20,21 @@ #include #include -#include #include #include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -class ShapeAnalysis_Surface; -class Geom_Surface; -class Geom_Curve; -class Geom2d_Curve; +#include -// resolve name collisions with X11 headers +#include + +class Geom2d_Curve; +class Geom_Curve; +class Geom_Surface; +class ShapeAnalysis_Surface; + +// Resolve name collisions with X11 headers #ifdef Status #undef Status #endif @@ -57,6 +54,14 @@ DEFINE_STANDARD_HANDLE(ShapeConstruct_ProjectCurveOnSurface, Standard_Transient) //! the surface) are recognized with the given precision. class ShapeConstruct_ProjectCurveOnSurface : public Standard_Transient { +public: + // Type aliases for internal containers (1-based indexing for geometry arrays) + using ArrayOfPnt = NCollection_Array1; + using ArrayOfPnt2d = NCollection_Array1; + using ArrayOfReal = NCollection_Array1; + // Cache uses 0-based indexing (simple 2-element array) + using CachePoint = std::pair; + using CacheArray = NCollection_Array1; public: //! Empty constructor. @@ -64,168 +69,235 @@ public: //! Initializes the object with all necessary parameters, //! i.e. surface and precision - Standard_EXPORT virtual void Init(const Handle(Geom_Surface)& surf, const Standard_Real preci); + //! @param[in] theSurf the surface to project on + //! @param[in] thePreci the precision for projection + Standard_EXPORT virtual void Init(const Handle(Geom_Surface)& theSurf, + const Standard_Real thePreci); //! Initializes the object with all necessary parameters, //! i.e. surface and precision - Standard_EXPORT virtual void Init(const Handle(ShapeAnalysis_Surface)& surf, - const Standard_Real preci); + //! @param[in] theSurf the surface to project on (ShapeAnalysis_Surface) + //! @param[in] thePreci the precision for projection + Standard_EXPORT virtual void Init(const Handle(ShapeAnalysis_Surface)& theSurf, + const Standard_Real thePreci); //! Loads a surface (in the form of Geom_Surface) to project on - Standard_EXPORT void SetSurface(const Handle(Geom_Surface)& surf); + //! @param[in] theSurf the surface to project on + Standard_EXPORT void SetSurface(const Handle(Geom_Surface)& theSurf); //! Loads a surface (in the form of ShapeAnalysis_Surface) to project on - Standard_EXPORT void SetSurface(const Handle(ShapeAnalysis_Surface)& surf); + //! @param[in] theSurf the surface to project on + Standard_EXPORT void SetSurface(const Handle(ShapeAnalysis_Surface)& theSurf); //! Sets value for current precision - Standard_EXPORT void SetPrecision(const Standard_Real preci); - - //! Returns (modifiable) the build-curve-3d mode, by default False - //! If True, if the projected curve has been recomputed by - //! interpolation, the 3d curve is also rebuild by interpolation - Standard_EXPORT Standard_Boolean& BuildCurveMode(); + //! @param[in] thePreci the precision value + Standard_EXPORT void SetPrecision(const Standard_Real thePreci); //! Returns (modifiable) the flag specifying to which side of //! parametrical space adjust part of pcurve which lies on seam. //! This is required in very rare case when 3d curve which is //! to be projected goes partly along the seam on the closed //! surface with singularity (e.g. sphere), goes through the - //! degenerated point and paerly lies on internal area of surface. + //! degenerated point and partly lies on internal area of surface. //! //! If this flag is True, the seam part of such curve will be //! adjusted to the left side of parametric space (on sphere U=0), //! else to the right side (on sphere U=2*PI) //! Default value is True + //! @return modifiable reference to the adjustment flag Standard_EXPORT Standard_Integer& AdjustOverDegenMode(); //! Returns the status of last Perform + //! @param[in] theStatus the status to query + //! @return true if the specified status is set Standard_EXPORT Standard_Boolean Status(const ShapeExtend_Status theStatus) const; //! Computes the projection of 3d curve onto a surface using the //! specialized algorithm. Returns False if projector fails, //! otherwise, if pcurve computed successfully, returns True. //! The output curve 2D is guaranteed to be same-parameter - //! with input curve 3D on the interval [First, Last]. If the output curve + //! with input curve 3D on the interval [theFirst, theLast]. If the output curve //! lies on a direct line the infinite line is returned, in the case //! same-parameter condition is satisfied. - //! TolFirst and TolLast are the tolerances at the ends of input curve 3D. - Standard_EXPORT virtual Standard_Boolean Perform(Handle(Geom_Curve)& c3d, - const Standard_Real First, - const Standard_Real Last, - Handle(Geom2d_Curve)& c2d, - const Standard_Real TolFirst = -1, - const Standard_Real TolLast = -1); + //! @param[in] theC3D the 3D curve to project + //! @param[in] theFirst the first parameter of the curve + //! @param[in] theLast the last parameter of the curve + //! @param[out] theC2D the resulting 2D curve + //! @param[in] theTolFirst the tolerance at the first point (default: Precision::Confusion()) + //! @param[in] theTolLast the tolerance at the last point (default: Precision::Confusion()) + //! @return true if projection succeeded + Standard_EXPORT virtual Standard_Boolean Perform( + const Handle(Geom_Curve)& theC3D, + const Standard_Real theFirst, + const Standard_Real theLast, + Handle(Geom2d_Curve)& theC2D, + const Standard_Real theTolFirst = Precision::Confusion(), + const Standard_Real theTolLast = Precision::Confusion()); + + DEFINE_STANDARD_RTTIEXT(ShapeConstruct_ProjectCurveOnSurface, Standard_Transient) + +protected: + //! Try to approximate 3D curve by Geom2d_Line + //! or Geom2d_BSplineCurve with degree 1 with specified tolerance. + //! @param[in] thePoints points obtained from 3d curve + //! @param[in] theParams parameters corresponding points on 3d curve + //! @param[out] thePoints2d 2d points lies on line in parametric space + //! @param[in] theTol tolerance used for compare initial points 3d and + //! 3d points obtained from line lying in parametric space of surface + //! @param[out] theIsRecompute flag indicating if recomputation is needed + //! @param[out] theIsFromCache flag indicating if result is from cache + //! @return the resulting 2D curve or null if line fitting failed + Standard_EXPORT Handle(Geom2d_Curve) getLine(const ArrayOfPnt& thePoints, + const ArrayOfReal& theParams, + ArrayOfPnt2d& thePoints2d, + const Standard_Real theTol, + Standard_Boolean& theIsRecompute, + Standard_Boolean& theIsFromCache) const; //! Computes the projection of 3d curve onto a surface using the //! standard algorithm from ProjLib. Returns False if standard //! projector fails or raises an exception or cuts the curve by //! parametrical bounds of the surface. Else, if pcurve computed //! successfully, returns True. - //! The continuity, maxdeg and nbinterval are parameters of call - //! to Approx_CurveOnSurface. If nbinterval is equal to -1 - //! (default), this value is computed depending on source 3d curve - //! and surface. - Standard_EXPORT Standard_Boolean PerformByProjLib(Handle(Geom_Curve)& c3d, - const Standard_Real First, - const Standard_Real Last, - Handle(Geom2d_Curve)& c2d, - const GeomAbs_Shape continuity = GeomAbs_C1, - const Standard_Integer maxdeg = 12, - const Standard_Integer nbinterval = -1); + //! @param[in] theC3D the 3D curve to project + //! @param[in] theFirst the first parameter of the curve + //! @param[in] theLast the last parameter of the curve + //! @param[out] theC2D the resulting 2D curve + //! @return true if projection succeeded + Standard_EXPORT Standard_Boolean PerformByProjLib(const Handle(Geom_Curve)& theC3D, + const Standard_Real theFirst, + const Standard_Real theLast, + Handle(Geom2d_Curve)& theC2D); - DEFINE_STANDARD_RTTIEXT(ShapeConstruct_ProjectCurveOnSurface, Standard_Transient) + //! Performs analytical projection for special cases (plane surfaces) + //! @param[in] theC3D the 3D curve to project + //! @return the resulting 2D curve or null if analytical projection not applicable + Standard_EXPORT Handle(Geom2d_Curve) projectAnalytic(const Handle(Geom_Curve)& theC3D) const; -protected: - //! Try to approximate 3D curve by Geom2d_Line - //! or Geom2d_BsplineCurve with degree 1 with specified tolerance. - //! points - points obtained from 3d curve. - //! params - parameters corresponding points on 3d curves - //! points2d - 2d points lies on line in parametric space - //! theTol - tolerance used for compare initial points 3d and - //! 3d points obtained from line lying in parameric space of surface - Standard_EXPORT Handle(Geom2d_Curve) getLine(const TColgp_SequenceOfPnt& points, - const TColStd_SequenceOfReal& params, - TColgp_SequenceOfPnt2d& points2d, - const Standard_Real theTol, - Standard_Boolean& IsRecompute, - Standard_Boolean& isFromCashe) const; + //! Main approximation routine for pcurve computation + //! @param[in] theNbPnt number of points + //! @param[in] theC3D the 3D curve + //! @param[in] theTolFirst tolerance at first point + //! @param[in] theTolLast tolerance at last point + //! @param[in,out] thePoints array of 3D points + //! @param[in,out] theParams array of parameters + //! @param[out] thePoints2d array of 2D points + //! @param[out] theC2D resulting 2D curve + //! @return true if approximation succeeded + Standard_EXPORT Standard_Boolean approxPCurve(const Standard_Integer theNbPnt, + const Handle(Geom_Curve)& theC3D, + const Standard_Real theTolFirst, + const Standard_Real theTolLast, + ArrayOfPnt& thePoints, + ArrayOfReal& theParams, + ArrayOfPnt2d& thePoints2d, + Handle(Geom2d_Curve)& theC2D); - Handle(ShapeAnalysis_Surface) mySurf; - Standard_Real myPreci; - Standard_Boolean myBuild; - Standard_Integer myStatus; - Standard_Integer myAdjustOverDegen; - Standard_Integer myNbCashe; - gp_Pnt myCashe3d[2]; - gp_Pnt2d myCashe2d[2]; + //! Corrects extremity point near singularity + //! @param[in] theC3D the 3D curve + //! @param[in] theParams array of parameters + //! @param[in,out] thePoints2d array of 2D points + //! @param[in] theIsFirstPoint true if correcting first point, false for last + //! @param[in] thePointOnIsoLine point on isoline + //! @param[in] theIsUIso true if U-isoline, false if V-isoline + Standard_EXPORT void correctExtremity(const Handle(Geom_Curve)& theC3D, + const ArrayOfReal& theParams, + ArrayOfPnt2d& thePoints2d, + const Standard_Boolean theIsFirstPoint, + const gp_Pnt2d& thePointOnIsoLine, + const Standard_Boolean theIsUIso); + + //! Inserts additional point or adjusts coordinate to handle period jumps + //! @param[in,out] theToAdjust flag indicating if adjustment should be done + //! @param[in] theIndCoord coordinate index (1=U, 2=V) + //! @param[in] thePeriod the period value + //! @param[in] theTolOnPeriod tolerance on period + //! @param[in,out] theCurCoord current coordinate value + //! @param[in] thePrevCoord previous coordinate value + //! @param[in] theC3D the 3D curve + //! @param[in,out] theIndex current point index + //! @param[in,out] thePoints array of 3D points + //! @param[in,out] theParams array of parameters + //! @param[in,out] thePoints2d array of 2D points + Standard_EXPORT void insertAdditionalPointOrAdjust(Standard_Boolean& theToAdjust, + const Standard_Integer theIndCoord, + const Standard_Real thePeriod, + const Standard_Real theTolOnPeriod, + Standard_Real& theCurCoord, + const Standard_Real thePrevCoord, + const Handle(Geom_Curve)& theC3D, + Standard_Integer& theIndex, + ArrayOfPnt& thePoints, + ArrayOfReal& theParams, + ArrayOfPnt2d& thePoints2d); + + //! Interpolates 2D curve from points + //! @param[in] theNbPnt number of points + //! @param[in] thePoints2d array of 2D points + //! @param[in] theParams array of parameters + //! @return the interpolated 2D curve or null on failure + Standard_EXPORT Handle(Geom2d_Curve) interpolatePCurve(const Standard_Integer theNbPnt, + const ArrayOfPnt2d& thePoints2d, + const ArrayOfReal& theParams) const; + + //! Approximates 2D curve from points + //! @param[in] thePoints2d array of 2D points + //! @param[in] theParams array of parameters + //! @return the approximated 2D curve or null on failure + Standard_EXPORT Handle(Geom2d_Curve) approximatePCurve(const ArrayOfPnt2d& thePoints2d, + const ArrayOfReal& theParams) const; + + //! Checks and removes coincident 3D points + //! @param[in,out] thePoints array of 3D points + //! @param[in,out] theParams array of parameters + //! @param[in,out] thePreci precision (may be adjusted) + Standard_EXPORT void checkPoints(ArrayOfPnt& thePoints, + ArrayOfReal& theParams, + Standard_Real& thePreci) const; + + //! Checks and removes coincident 2D points + //! @param[in,out] thePoints2d array of 2D points + //! @param[in,out] theParams array of parameters + //! @param[in,out] thePreci precision (may be adjusted) + Standard_EXPORT void checkPoints2d(ArrayOfPnt2d& thePoints2d, + ArrayOfReal& theParams, + Standard_Real& thePreci) const; + + //! Detects if curve is isoparametric (U=const or V=const) + //! @param[in] theNbPnt number of points + //! @param[in] thePoints array of 3D points + //! @param[in] theParams array of parameters + //! @param[out] theIsTypeU true if U-iso, false if V-iso + //! @param[out] theP1OnIso true if first point is on iso + //! @param[out] theValueP1 2D value of first point on iso + //! @param[out] theP2OnIso true if last point is on iso + //! @param[out] theValueP2 2D value of last point on iso + //! @param[out] theIsoPar2d3d true if parametrization matches + //! @param[out] theCIso the isoline curve + //! @param[out] theT1 first iso parameter + //! @param[out] theT2 last iso parameter + //! @param[out] theParamsOut output parameters on isoline + //! @return true if curve is isoparametric + Standard_EXPORT Standard_Boolean isAnIsoparametric(const Standard_Integer theNbPnt, + const ArrayOfPnt& thePoints, + const ArrayOfReal& theParams, + Standard_Boolean& theIsTypeU, + Standard_Boolean& theP1OnIso, + gp_Pnt2d& theValueP1, + Standard_Boolean& theP2OnIso, + gp_Pnt2d& theValueP2, + Standard_Boolean& theIsoPar2d3d, + Handle(Geom_Curve)& theCIso, + Standard_Real& theT1, + Standard_Real& theT2, + ArrayOfReal& theParamsOut) const; private: - Standard_EXPORT Handle(Geom2d_Curve) ProjectAnalytic(const Handle(Geom_Curve)& c3d) const; - - Standard_EXPORT Standard_Boolean ApproxPCurve(const Standard_Integer nbrPnt, - const Handle(Geom_Curve)& c3d, - const Standard_Real TolFirst, - const Standard_Real TolLast, - TColgp_SequenceOfPnt& points, - TColStd_SequenceOfReal& params, - TColgp_SequenceOfPnt2d& points2d, - Handle(Geom2d_Curve)& c2d); - - Standard_EXPORT void CorrectExtremity(const Handle(Geom_Curve)& theC3d, - const TColStd_SequenceOfReal& theParams, - TColgp_SequenceOfPnt2d& thePnt2d, - const Standard_Boolean theIsFirstPoint, - const gp_Pnt2d& thePointOnIsoLine, - const Standard_Boolean theIsUiso); - - Standard_EXPORT void InsertAdditionalPointOrAdjust(Standard_Boolean& ToAdjust, - const Standard_Integer theIndCoord, - const Standard_Real Period, - const Standard_Real TolOnPeriod, - Standard_Real& CurCoord, - const Standard_Real prevCoord, - const Handle(Geom_Curve)& c3d, - Standard_Integer& theIndex, - TColgp_SequenceOfPnt& points, - TColStd_SequenceOfReal& params, - TColgp_SequenceOfPnt2d& pnt2d); - - Standard_EXPORT Handle(Geom2d_Curve) InterpolatePCurve(const Standard_Integer nbrPnt, - Handle(TColgp_HArray1OfPnt2d)& points2d, - Handle(TColStd_HArray1OfReal)& params, - const Handle(Geom_Curve)& orig) const; - - Standard_EXPORT Handle(Geom2d_Curve) ApproximatePCurve(const Standard_Integer nbrPnt, - Handle(TColgp_HArray1OfPnt2d)& points2d, - Handle(TColStd_HArray1OfReal)& params, - const Handle(Geom_Curve)& orig) const; - - Standard_EXPORT Handle(Geom_Curve) InterpolateCurve3d(const Standard_Integer nbrPnt, - Handle(TColgp_HArray1OfPnt)& points, - Handle(TColStd_HArray1OfReal)& params, - const Handle(Geom_Curve)& orig) const; - - Standard_EXPORT void CheckPoints(Handle(TColgp_HArray1OfPnt)& points, - Handle(TColStd_HArray1OfReal)& params, - Standard_Real& preci) const; - - Standard_EXPORT void CheckPoints2d(Handle(TColgp_HArray1OfPnt2d)& points, - Handle(TColStd_HArray1OfReal)& params, - Standard_Real& preci) const; - - Standard_EXPORT Standard_Boolean IsAnIsoparametric(const Standard_Integer nbrPnt, - const TColgp_SequenceOfPnt& points, - const TColStd_SequenceOfReal& params, - Standard_Boolean& isoTypeU, - Standard_Boolean& p1OnIso, - gp_Pnt2d& valueP1, - Standard_Boolean& p2OnIso, - gp_Pnt2d& valueP2, - Standard_Boolean& isoPar2d3d, - Handle(Geom_Curve)& cIso, - Standard_Real& t1, - Standard_Real& t2, - TColStd_Array1OfReal& pout) const; + Handle(ShapeAnalysis_Surface) mySurf; //!< Surface to project on + Standard_Real myPreci; //!< Current precision + Standard_Integer myStatus; //!< Operation status + Standard_Integer myAdjustOverDegen; //!< Seam adjustment flag + CacheArray myCache; //!< Cached 3D/2D point pairs for projection optimization }; #endif // _ShapeConstruct_ProjectCurveOnSurface_HeaderFile diff --git a/tests/offset/bugs/bug5806 b/tests/offset/bugs/bug5806 index 9d2f897435..dfea689bd8 100644 --- a/tests/offset/bugs/bug5806 +++ b/tests/offset/bugs/bug5806 @@ -1,6 +1,3 @@ -puts "TODO OCC25925 ALL: ERROR. Mixed connectivity of faces." -puts "TODO OCC25925 ALL: Error : The offset cannot be built." - puts "============" puts "OCC5806" puts "============"