// 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 TEST(TopExp_Test, MapShapes_Faces) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); ASSERT_TRUE(aBoxMaker.IsDone()); NCollection_IndexedMap aFaceMap; TopExp::MapShapes(aBox, TopAbs_FACE, aFaceMap); EXPECT_EQ(aFaceMap.Extent(), 6) << "A box should have exactly 6 faces"; } TEST(TopExp_Test, MapShapes_Edges) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); ASSERT_TRUE(aBoxMaker.IsDone()); NCollection_IndexedMap anEdgeMap; TopExp::MapShapes(aBox, TopAbs_EDGE, anEdgeMap); EXPECT_EQ(anEdgeMap.Extent(), 12) << "A box should have exactly 12 edges"; } TEST(TopExp_Test, MapShapes_Vertices) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); ASSERT_TRUE(aBoxMaker.IsDone()); NCollection_IndexedMap aVertexMap; TopExp::MapShapes(aBox, TopAbs_VERTEX, aVertexMap); EXPECT_EQ(aVertexMap.Extent(), 8) << "A box should have exactly 8 vertices"; } TEST(TopExp_Test, FirstVertex_LastVertex) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); ASSERT_TRUE(aBoxMaker.IsDone()); TopExp_Explorer anExp(aBox, TopAbs_EDGE); ASSERT_TRUE(anExp.More()); const TopoDS_Edge& anEdge = TopoDS::Edge(anExp.Current()); TopoDS_Vertex aFirstVertex = TopExp::FirstVertex(anEdge); TopoDS_Vertex aLastVertex = TopExp::LastVertex(anEdge); EXPECT_FALSE(aFirstVertex.IsNull()) << "First vertex should not be null"; EXPECT_FALSE(aLastVertex.IsNull()) << "Last vertex should not be null"; EXPECT_FALSE(aFirstVertex.IsSame(aLastVertex)) << "First and last vertices of a box edge should be distinct"; } TEST(TopExp_Test, Vertices) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); ASSERT_TRUE(aBoxMaker.IsDone()); TopExp_Explorer anExp(aBox, TopAbs_EDGE); ASSERT_TRUE(anExp.More()); const TopoDS_Edge& anEdge = TopoDS::Edge(anExp.Current()); TopoDS_Vertex aV1, aV2; TopExp::Vertices(anEdge, aV1, aV2); EXPECT_FALSE(aV1.IsNull()) << "First vertex from Vertices() should not be null"; EXPECT_FALSE(aV2.IsNull()) << "Second vertex from Vertices() should not be null"; } TEST(TopExp_Test, CommonVertex) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); ASSERT_TRUE(aBoxMaker.IsDone()); NCollection_IndexedMap anEdgeMap; TopExp::MapShapes(aBox, TopAbs_EDGE, anEdgeMap); ASSERT_GE(anEdgeMap.Extent(), 2); // Find two adjacent edges (sharing a common vertex) bool isFound = false; for (int i = 1; i <= anEdgeMap.Extent() && !isFound; ++i) { const TopoDS_Edge& anEdge1 = TopoDS::Edge(anEdgeMap(i)); for (int j = i + 1; j <= anEdgeMap.Extent() && !isFound; ++j) { const TopoDS_Edge& anEdge2 = TopoDS::Edge(anEdgeMap(j)); TopoDS_Vertex aCommonVertex; if (TopExp::CommonVertex(anEdge1, anEdge2, aCommonVertex)) { EXPECT_FALSE(aCommonVertex.IsNull()) << "Common vertex should not be null"; isFound = true; } } } 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); const TopoDS_Shape& aBox = aBoxMaker.Shape(); ASSERT_TRUE(aBoxMaker.IsDone()); NCollection_IndexedDataMap, TopTools_ShapeMapHasher> anEdgeFaceMap; TopExp::MapShapesAndAncestors(aBox, TopAbs_EDGE, TopAbs_FACE, anEdgeFaceMap); EXPECT_EQ(anEdgeFaceMap.Extent(), 12) << "A box should have 12 edges in the ancestor map"; // Each edge of a box is shared by exactly 2 faces for (int i = 1; i <= anEdgeFaceMap.Extent(); ++i) { const NCollection_List& aFaceList = anEdgeFaceMap(i); EXPECT_EQ(aFaceList.Extent(), 2) << "Each box edge should be shared by exactly 2 faces"; } }