diff --git a/src/ModelingAlgorithms/TKMesh/BRepMesh/BRepMesh_BaseMeshAlgo.cxx b/src/ModelingAlgorithms/TKMesh/BRepMesh/BRepMesh_BaseMeshAlgo.cxx index 471d2de7f7..97b0eaefe2 100644 --- a/src/ModelingAlgorithms/TKMesh/BRepMesh/BRepMesh_BaseMeshAlgo.cxx +++ b/src/ModelingAlgorithms/TKMesh/BRepMesh/BRepMesh_BaseMeshAlgo.cxx @@ -86,40 +86,59 @@ bool BRepMesh_BaseMeshAlgo::initDataStructure() for (int aEdgeIt = 0; aEdgeIt < aDWire->EdgesNb(); ++aEdgeIt) { - const IMeshData::IEdgeHandle aDEdge = aDWire->GetEdge(aEdgeIt); - const IMeshData::ICurveHandle& aCurve = aDEdge->GetCurve(); - const IMeshData::ListOfInteger& aListOfPCurves = aDEdge->GetPCurves(myDFace.get()); + const IMeshData::IEdgeHandle aDEdge = aDWire->GetEdge(aEdgeIt); + const IMeshData::ICurveHandle& aCurve = aDEdge->GetCurve(); + // A wire defines one topological occurrence of an edge. Registering every + // pcurve attached to its face would create duplicate constraints at a periodic seam. + const IMeshData::IPCurveHandle& aPCurve = + aDEdge->GetPCurve(myDFace.get(), aDWire->GetEdgeOrientation(aEdgeIt)); + const TopAbs_Orientation aOri = fixSeamEdgeOrientation(aDEdge, aPCurve); + int aPrevNodeIndex = -1; + const int aLastPoint = aPCurve->ParametersNb() - 1; + for (int aPointIndex = 0; aPointIndex <= aLastPoint; ++aPointIndex) + { + const int aNodeIndex = registerNode(aCurve->GetPoint(aPointIndex), + aPCurve->GetPoint(aPointIndex), + BRepMesh_Frontier, + false); + + aPCurve->GetIndex(aPointIndex) = aNodeIndex; + + if (aPrevNodeIndex != -1 && aPrevNodeIndex != aNodeIndex) + { + const int aLinksNb = myStructure->NbLinks(); + const int aLinkIndex = addLinkToMesh(aPrevNodeIndex, aNodeIndex, aOri); + if (aWireIt != 0 && aLinkIndex <= aLinksNb) + { + // Prevent holes around wire of zero area. + BRepMesh_Edge& aLink = const_cast(myStructure->GetLink(aLinkIndex)); + aLink.SetMovability(BRepMesh_Fixed); + } + } + + aPrevNodeIndex = aNodeIndex; + } + + // Every pcurve needs node indices for Poly_PolygonOnTriangulation, but only + // the pcurve belonging to this wire occurrence defines a mesh constraint. + const IMeshData::ListOfInteger& aListOfPCurves = aDEdge->GetPCurves(myDFace.get()); for (IMeshData::ListOfInteger::Iterator aPCurveIt(aListOfPCurves); aPCurveIt.More(); aPCurveIt.Next()) { - const IMeshData::IPCurveHandle& aPCurve = aDEdge->GetPCurve(aPCurveIt.Value()); - const TopAbs_Orientation aOri = fixSeamEdgeOrientation(aDEdge, aPCurve); - - int aPrevNodeIndex = -1; - const int aLastPoint = aPCurve->ParametersNb() - 1; - for (int aPointIndex = 0; aPointIndex <= aLastPoint; ++aPointIndex) + const IMeshData::IPCurveHandle& anOtherPCurve = aDEdge->GetPCurve(aPCurveIt.Value()); + if (anOtherPCurve == aPCurve) { - const int aNodeIndex = registerNode(aCurve->GetPoint(aPointIndex), - aPCurve->GetPoint(aPointIndex), - BRepMesh_Frontier, - false); + continue; + } - aPCurve->GetIndex(aPointIndex) = aNodeIndex; - - if (aPrevNodeIndex != -1 && aPrevNodeIndex != aNodeIndex) - { - const int aLinksNb = myStructure->NbLinks(); - const int aLinkIndex = addLinkToMesh(aPrevNodeIndex, aNodeIndex, aOri); - if (aWireIt != 0 && aLinkIndex <= aLinksNb) - { - // Prevent holes around wire of zero area. - BRepMesh_Edge& aLink = const_cast(myStructure->GetLink(aLinkIndex)); - aLink.SetMovability(BRepMesh_Fixed); - } - } - - aPrevNodeIndex = aNodeIndex; + const int anOtherLastPoint = anOtherPCurve->ParametersNb() - 1; + for (int aPointIndex = 0; aPointIndex <= anOtherLastPoint; ++aPointIndex) + { + anOtherPCurve->GetIndex(aPointIndex) = registerNode(aCurve->GetPoint(aPointIndex), + anOtherPCurve->GetPoint(aPointIndex), + BRepMesh_Frontier, + false); } } } diff --git a/src/ModelingAlgorithms/TKMesh/GTests/BRepMesh_IncrementalMesh_Test.cxx b/src/ModelingAlgorithms/TKMesh/GTests/BRepMesh_IncrementalMesh_Test.cxx index 210893f72b..ba587d429b 100644 --- a/src/ModelingAlgorithms/TKMesh/GTests/BRepMesh_IncrementalMesh_Test.cxx +++ b/src/ModelingAlgorithms/TKMesh/GTests/BRepMesh_IncrementalMesh_Test.cxx @@ -11,18 +11,37 @@ // 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 #include #include #include +#include #include #include @@ -71,3 +90,184 @@ TEST(BRepMesh_IncrementalMeshTest, OCC26407_PlanarPolygonMeshStatus) EXPECT_EQ(aMesher.GetStatusFlags(), 0) << "Meshing of the planar polygon face should succeed (status 0)"; } + +TEST(BRepMesh_IncrementalMeshTest, TrimmedCylinder_RespectsUVRangeAndDeflection) +{ + constexpr double THE_RADIUS = 5.0; + constexpr double THE_FIRST_U = 0.4; + constexpr double THE_LAST_U = 5.7; + constexpr double THE_FIRST_V = -2.0; + constexpr double THE_LAST_V = 7.0; + constexpr double THE_DEFLECTION = 0.05; + + occ::handle aCylinder = + new Geom_CylindricalSurface(gp_Ax3(gp::Origin(), gp::DZ()), THE_RADIUS); + BRepBuilderAPI_MakeFace aFaceBuilder(aCylinder, + THE_FIRST_U, + THE_LAST_U, + THE_FIRST_V, + THE_LAST_V, + Precision::Confusion()); + ASSERT_TRUE(aFaceBuilder.IsDone()); + const TopoDS_Face aFace = aFaceBuilder.Face(); + + BRepMesh_IncrementalMesh aMesher(aFace, THE_DEFLECTION); + ASSERT_TRUE(aMesher.IsDone()); + + TopLoc_Location aLocation; + const occ::handle aTriangulation = BRep_Tool::Triangulation(aFace, aLocation); + ASSERT_FALSE(aTriangulation.IsNull()); + ASSERT_TRUE(aTriangulation->HasUVNodes()); + ASSERT_GT(aTriangulation->NbTriangles(), 0); + + for (int aNodeIdx = 1; aNodeIdx <= aTriangulation->NbNodes(); ++aNodeIdx) + { + const gp_Pnt2d aUV = aTriangulation->UVNode(aNodeIdx); + EXPECT_GE(aUV.X(), THE_FIRST_U - Precision::PConfusion()); + EXPECT_LE(aUV.X(), THE_LAST_U + Precision::PConfusion()); + EXPECT_GE(aUV.Y(), THE_FIRST_V - Precision::PConfusion()); + EXPECT_LE(aUV.Y(), THE_LAST_V + Precision::PConfusion()); + + const gp_Pnt anExpectedPoint = aCylinder->Value(aUV.X(), aUV.Y()); + const gp_Pnt anActualPoint = aTriangulation->Node(aNodeIdx).Transformed(aLocation); + EXPECT_LE(anActualPoint.Distance(anExpectedPoint), Precision::Confusion()); + } + + for (int aTriangleIdx = 1; aTriangleIdx <= aTriangulation->NbTriangles(); ++aTriangleIdx) + { + int aNode1 = 0; + int aNode2 = 0; + int aNode3 = 0; + aTriangulation->Triangle(aTriangleIdx).Get(aNode1, aNode2, aNode3); + + const gp_Pnt2d aUV1 = aTriangulation->UVNode(aNode1); + const gp_Pnt2d aUV2 = aTriangulation->UVNode(aNode2); + const gp_Pnt2d aUV3 = aTriangulation->UVNode(aNode3); + const gp_Pnt anExpectedPoint = aCylinder->Value((aUV1.X() + aUV2.X() + aUV3.X()) / 3.0, + (aUV1.Y() + aUV2.Y() + aUV3.Y()) / 3.0); + const gp_Pnt aPoint1 = aTriangulation->Node(aNode1).Transformed(aLocation); + const gp_Pnt aPoint2 = aTriangulation->Node(aNode2).Transformed(aLocation); + const gp_Pnt aPoint3 = aTriangulation->Node(aNode3).Transformed(aLocation); + const gp_Pnt anActualPoint((aPoint1.XYZ() + aPoint2.XYZ() + aPoint3.XYZ()) / 3.0); + EXPECT_LE(anActualPoint.Distance(anExpectedPoint), THE_DEFLECTION); + } +} + +TEST(BRepMesh_IncrementalMeshTest, BooleanTrimmedTorus_CrossingSeamProducesDenseMesh) +{ + BRepPrimAPI_MakeBox aBoxBuilder(gp_Pnt(-187.0, -20.0, -67.5), gp_Pnt(187.0, 0.0, 67.5)); + const TopoDS_Shape aBox = aBoxBuilder.Shape(); + ASSERT_TRUE(aBoxBuilder.IsDone()); + + const gp_Circ aCircle(gp_Ax2(gp_Pnt(200.0, -20.0, 0.0), gp_Dir(0.0, -1.0, 0.0)), 17.5); + BRepBuilderAPI_MakeEdge anEdgeBuilder(aCircle); + const TopoDS_Edge anEdge = anEdgeBuilder.Edge(); + ASSERT_TRUE(anEdgeBuilder.IsDone()); + + BRepBuilderAPI_MakeWire aWireBuilder(anEdge); + const TopoDS_Wire aWire = aWireBuilder.Wire(); + ASSERT_TRUE(aWireBuilder.IsDone()); + + BRepBuilderAPI_MakeFace aFaceBuilder(aWire, true); + const TopoDS_Face aProfile = aFaceBuilder.Face(); + ASSERT_TRUE(aFaceBuilder.IsDone()); + + const gp_Ax1 anAxis(gp_Pnt(0.0, -20.0, 0.0), gp_Dir(0.0, 0.0, -1.0)); + BRepPrimAPI_MakeRevol aRingBuilder(aProfile, anAxis, 2.0 * M_PI); + const TopoDS_Shape aRing = aRingBuilder.Shape(); + ASSERT_TRUE(aRingBuilder.IsDone()); + + BRepAlgoAPI_Fuse aFuse(aBox, aRing); + aFuse.Build(); + ASSERT_TRUE(aFuse.IsDone()); + + const TopoDS_Shape aFused = aFuse.Shape(); + ASSERT_FALSE(aFused.IsNull()); + EXPECT_TRUE(BRepCheck_Analyzer(aFused).IsValid()); + + IMeshTools_Parameters aParameters; + aParameters.Deflection = 0.1; + aParameters.Angle = 0.4; + BRepMesh_IncrementalMesh aMesher(aFused, aParameters); + ASSERT_TRUE(aMesher.IsDone()); + + int aTorusFaces = 0; + int aTorusNodes = 0; + int aTorusTriangles = 0; + for (TopExp_Explorer anExplorer(aFused, TopAbs_FACE); anExplorer.More(); anExplorer.Next()) + { + const TopoDS_Face& aFace = TopoDS::Face(anExplorer.Current()); + BRepAdaptor_Surface aSurface(aFace); + if (aSurface.GetType() != GeomAbs_Torus) + { + continue; + } + + ++aTorusFaces; + TopLoc_Location aLocation; + const occ::handle aTriangulation = + BRep_Tool::Triangulation(aFace, aLocation); + ASSERT_FALSE(aTriangulation.IsNull()); + aTorusNodes += aTriangulation->NbNodes(); + aTorusTriangles += aTriangulation->NbTriangles(); + } + + ASSERT_GT(aTorusFaces, 0); + // The regression produced fewer than 450 triangles for the whole fused shape. + EXPECT_GT(aTorusNodes, 1000); + EXPECT_GT(aTorusTriangles, 1000); +} + +TEST(BRepMesh_IncrementalMeshTest, ClosedCylinder_SeamPolygonsReferenceValidMeshNodes) +{ + BRepPrimAPI_MakeCylinder aCylinderBuilder(5.0, 10.0); + const TopoDS_Shape aCylinder = aCylinderBuilder.Shape(); + ASSERT_TRUE(aCylinderBuilder.IsDone()); + + BRepMesh_IncrementalMesh aMesher(aCylinder, 0.1); + ASSERT_TRUE(aMesher.IsDone()); + + int aSeamEdges = 0; + for (TopExp_Explorer aFaceExplorer(aCylinder, TopAbs_FACE); aFaceExplorer.More(); + aFaceExplorer.Next()) + { + const TopoDS_Face& aFace = TopoDS::Face(aFaceExplorer.Current()); + TopLoc_Location aLocation; + const occ::handle aTriangulation = + BRep_Tool::Triangulation(aFace, aLocation); + ASSERT_FALSE(aTriangulation.IsNull()); + + for (TopExp_Explorer anEdgeExplorer(aFace, TopAbs_EDGE); anEdgeExplorer.More(); + anEdgeExplorer.Next()) + { + const TopoDS_Edge& anEdge = TopoDS::Edge(anEdgeExplorer.Current()); + if (!BRep_Tool::IsClosed(anEdge, aFace)) + { + continue; + } + + const occ::handle& aForwardPolygon = + BRep_Tool::PolygonOnTriangulation(anEdge, aTriangulation, aLocation); + const TopoDS_Edge aReversedEdge = TopoDS::Edge(anEdge.Reversed()); + const occ::handle& aReversedPolygon = + BRep_Tool::PolygonOnTriangulation(aReversedEdge, aTriangulation, aLocation); + ASSERT_FALSE(aForwardPolygon.IsNull()); + ASSERT_FALSE(aReversedPolygon.IsNull()); + + for (int aNodeIndex = 1; aNodeIndex <= aForwardPolygon->NbNodes(); ++aNodeIndex) + { + EXPECT_GE(aForwardPolygon->Node(aNodeIndex), 1); + EXPECT_LE(aForwardPolygon->Node(aNodeIndex), aTriangulation->NbNodes()); + } + for (int aNodeIndex = 1; aNodeIndex <= aReversedPolygon->NbNodes(); ++aNodeIndex) + { + EXPECT_GE(aReversedPolygon->Node(aNodeIndex), 1); + EXPECT_LE(aReversedPolygon->Node(aNodeIndex), aTriangulation->NbNodes()); + } + + ++aSeamEdges; + } + } + + EXPECT_GT(aSeamEdges, 0); +} diff --git a/tests/hlr/poly_hlr/bug25813_2 b/tests/hlr/poly_hlr/bug25813_2 index 0c6d438e3e..801f410ad0 100644 --- a/tests/hlr/poly_hlr/bug25813_2 +++ b/tests/hlr/poly_hlr/bug25813_2 @@ -1,4 +1,4 @@ -puts "TODO OCC30286 Linux: Error : The length of result shape is 3025.32, expected 3025.36" +puts "TODO OCC30286 Linux: Error : The length of result shape is 3025.4" puts "===========================================" puts "OCC25813: regression in Hidden Line Removal" @@ -6,7 +6,7 @@ puts "===========================================" puts "" set viewname "" -set length 3025.36 +set length 3025.49 restore [locate_data_file bug25813_hlr-bus1-draw-Fusion001.brep] a COMPUTE_HLR $viewname $algotype diff --git a/tests/hlr/poly_hlr/bug25813_3 b/tests/hlr/poly_hlr/bug25813_3 index ef2a510b8c..7bbf08698c 100644 --- a/tests/hlr/poly_hlr/bug25813_3 +++ b/tests/hlr/poly_hlr/bug25813_3 @@ -1,12 +1,14 @@ -puts "TODO OCC30286 Linux: Error : The length of result shape is 302.183, expected 302.238" +puts "TODO OCC30286 Linux: Error : The length of result shape is 302" puts "===========================================" puts "OCC25813: regression in Hidden Line Removal" puts "===========================================" puts "" + + set viewname "" -set length 302.238 +set length 301.999 ptorus a 30 10 diff --git a/tests/hlr/poly_hlr/bug25813_4 b/tests/hlr/poly_hlr/bug25813_4 index dbfca2240d..4dcd33347f 100644 --- a/tests/hlr/poly_hlr/bug25813_4 +++ b/tests/hlr/poly_hlr/bug25813_4 @@ -4,7 +4,7 @@ puts "===========================================" puts "" set viewname "" -set length 1196.25 +set length 1196.29 restore [locate_data_file bug25813_hlr-test_normandc1m1-draw-Cut001.brep] a COMPUTE_HLR $viewname $algotype