From cf0c71b982a747692076d68774d2c24db853b8bd Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Tue, 7 Apr 2026 18:14:35 +0100 Subject: [PATCH] Modeling Data - Fixed possible issue with Explorer (#1195) Add tests for TopExp_Explorer nested iteration and reinitialization --- .../TKBRep/GTests/TopExp_Test.cxx | 51 +++++++++++++++++++ .../TKBRep/TopExp/TopExp_Explorer.cxx | 7 +-- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/ModelingData/TKBRep/GTests/TopExp_Test.cxx b/src/ModelingData/TKBRep/GTests/TopExp_Test.cxx index 5e90d72ca9..faaaf61f86 100644 --- a/src/ModelingData/TKBRep/GTests/TopExp_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/TopExp_Test.cxx @@ -131,6 +131,57 @@ TEST(TopExp_Test, CommonVertex) EXPECT_TRUE(isFound) << "Should find at least one pair of adjacent edges in a box"; } +TEST(TopExp_Test, Explorer_NestedFaceEdge_Terminates) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + ASSERT_TRUE(aBoxMaker.IsDone()); + + // Reproduce the pattern from issue #1194: nested TopExp_Explorer + // iterating edges within faces must terminate. + int aTotalEdgeRefs = 0; + int aFaceCount = 0; + for (TopExp_Explorer aFaceExp(aBox, TopAbs_FACE); aFaceExp.More(); aFaceExp.Next()) + { + ++aFaceCount; + int anEdgeCount = 0; + for (TopExp_Explorer anEdgeExp(TopoDS::Face(aFaceExp.Current()), TopAbs_EDGE); anEdgeExp.More(); + anEdgeExp.Next()) + { + ASSERT_FALSE(anEdgeExp.Current().IsNull()); + ++anEdgeCount; + // Guard against infinite loop + ASSERT_LT(anEdgeCount, 100) << "Edge iteration appears infinite on face " << aFaceCount; + } + EXPECT_EQ(anEdgeCount, 4) << "Each box face should reference exactly 4 edges"; + aTotalEdgeRefs += anEdgeCount; + } + EXPECT_EQ(aFaceCount, 6); + EXPECT_EQ(aTotalEdgeRefs, 24) << "6 faces * 4 edges each = 24 edge references"; +} + +TEST(TopExp_Test, Explorer_ReInit_Terminates) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + ASSERT_TRUE(aBoxMaker.IsDone()); + + // Test that re-initializing an explorer (Clear + Init cycle) works correctly. + TopExp_Explorer anExp; + for (TopExp_Explorer aFaceExp(aBox, TopAbs_FACE); aFaceExp.More(); aFaceExp.Next()) + { + const TopoDS_Face& aFace = TopoDS::Face(aFaceExp.Current()); + anExp.Init(aFace, TopAbs_EDGE); + int anEdgeCount = 0; + for (; anExp.More(); anExp.Next()) + { + ++anEdgeCount; + ASSERT_LT(anEdgeCount, 100) << "Edge iteration appears infinite (reused explorer)"; + } + EXPECT_EQ(anEdgeCount, 4); + } +} + TEST(TopExp_Test, MapShapesAndAncestors) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); diff --git a/src/ModelingData/TKBRep/TopExp/TopExp_Explorer.cxx b/src/ModelingData/TKBRep/TopExp/TopExp_Explorer.cxx index b308202790..846ea67e10 100644 --- a/src/ModelingData/TKBRep/TopExp/TopExp_Explorer.cxx +++ b/src/ModelingData/TKBRep/TopExp/TopExp_Explorer.cxx @@ -182,9 +182,10 @@ int TopExp_Explorer::Depth() const noexcept void TopExp_Explorer::Clear() { - // Shrink to 0 - NCollection_LocalArray destroys all live elements. - if (myStack.Size() > 0) - myStack.Reallocate(0); + // Reset live iterators to release shape references immediately, + // but keep the array allocation to avoid Reallocate(0)/Reallocate(N) cycles. + for (int i = myStackTop; i >= 0; --i) + myStack[i] = TopoDS_Iterator(); myStackTop = -1; hasMore = false; }