Foundation Classes - Refactor TShape hierarchy for performance and memory efficiency (#1027)

- Made ShapeType() non-virtual by embedding the shape type in a compact uint16_t state field alongside flags
- Replaced int myFlags with uint16_t myState using a BitLayout enum for compact storage
- Moved Compose/Reverse/Complement operations from TopAbs.cxx to inline implementations in TopAbs.hxx
- Updated all TShape derived class constructors to pass their type to the base class
- Refactored TopoDS_Iterator to use index-based iteration with updateCurrentShape() helper
This commit is contained in:
Pasukhin Dmitry
2026-01-23 21:08:39 +00:00
committed by GitHub
parent 09996b852b
commit 3aeb4668f5
28 changed files with 1494 additions and 272 deletions
@@ -3,6 +3,8 @@ set(OCCT_TKBRep_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
set(OCCT_TKBRep_GTests_FILES
BRepAdaptor_CompCurve_Test.cxx
TopoDS_Builder_Test.cxx
TopoDS_Edge_Test.cxx
TopoDS_Iterator_Test.cxx
TopoDS_TShape_Test.cxx
)
@@ -0,0 +1,547 @@
// Copyright (c) 2026 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 <gtest/gtest.h>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Builder.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_CompSolid.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Solid.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <gp_Pnt.hxx>
//==================================================================================================
// Test TopoDS_Builder::MakeWire
//==================================================================================================
TEST(TopoDS_Builder_Test, MakeWire)
{
TopoDS_Builder aBuilder;
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
EXPECT_FALSE(aWire.IsNull()) << "Wire should not be null after MakeWire";
EXPECT_EQ(aWire.ShapeType(), TopAbs_WIRE) << "Shape type should be WIRE";
EXPECT_TRUE(aWire.Free()) << "Newly created wire should be free";
}
//==================================================================================================
// Test TopoDS_Builder::MakeShell
//==================================================================================================
TEST(TopoDS_Builder_Test, MakeShell)
{
TopoDS_Builder aBuilder;
TopoDS_Shell aShell;
aBuilder.MakeShell(aShell);
EXPECT_FALSE(aShell.IsNull()) << "Shell should not be null";
EXPECT_EQ(aShell.ShapeType(), TopAbs_SHELL);
}
//==================================================================================================
// Test TopoDS_Builder::MakeSolid
//==================================================================================================
TEST(TopoDS_Builder_Test, MakeSolid)
{
TopoDS_Builder aBuilder;
TopoDS_Solid aSolid;
aBuilder.MakeSolid(aSolid);
EXPECT_FALSE(aSolid.IsNull()) << "Solid should not be null";
EXPECT_EQ(aSolid.ShapeType(), TopAbs_SOLID);
}
//==================================================================================================
// Test TopoDS_Builder::MakeCompSolid
//==================================================================================================
TEST(TopoDS_Builder_Test, MakeCompSolid)
{
TopoDS_Builder aBuilder;
TopoDS_CompSolid aCompSolid;
aBuilder.MakeCompSolid(aCompSolid);
EXPECT_FALSE(aCompSolid.IsNull()) << "CompSolid should not be null";
EXPECT_EQ(aCompSolid.ShapeType(), TopAbs_COMPSOLID);
}
//==================================================================================================
// Test TopoDS_Builder::MakeCompound
//==================================================================================================
TEST(TopoDS_Builder_Test, MakeCompound)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
EXPECT_FALSE(aCompound.IsNull()) << "Compound should not be null";
EXPECT_EQ(aCompound.ShapeType(), TopAbs_COMPOUND);
}
//==================================================================================================
// Test TopoDS_Builder::Add - add vertices to compound
//==================================================================================================
TEST(TopoDS_Builder_Test, Add_VerticesToCompound)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
// Create and add vertices
TopoDS_Vertex aV1 = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0));
TopoDS_Vertex aV2 = BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0));
TopoDS_Vertex aV3 = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 1, 0));
aBuilder.Add(aCompound, aV1);
aBuilder.Add(aCompound, aV2);
aBuilder.Add(aCompound, aV3);
// Count children using iterator
int aCount = 0;
for (TopoDS_Iterator anIt(aCompound); anIt.More(); anIt.Next())
{
++aCount;
}
EXPECT_EQ(aCount, 3) << "Compound should have 3 vertices";
}
//==================================================================================================
// Test TopoDS_Builder::Add - add edges to wire
//==================================================================================================
TEST(TopoDS_Builder_Test, Add_EdgesToWire)
{
TopoDS_Builder aBuilder;
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
// Create edges forming a triangle
TopoDS_Edge aE1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0));
TopoDS_Edge aE2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1, 0, 0), gp_Pnt(0.5, 1, 0));
TopoDS_Edge aE3 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.5, 1, 0), gp_Pnt(0, 0, 0));
aBuilder.Add(aWire, aE1);
aBuilder.Add(aWire, aE2);
aBuilder.Add(aWire, aE3);
// Count children
int aCount = 0;
for (TopoDS_Iterator anIt(aWire); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_EDGE);
++aCount;
}
EXPECT_EQ(aCount, 3) << "Wire should have 3 edges";
}
//==================================================================================================
// Test TopoDS_Builder::Remove
//==================================================================================================
TEST(TopoDS_Builder_Test, Remove_FromCompound)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
// Add vertices
TopoDS_Vertex aV1 = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0));
TopoDS_Vertex aV2 = BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0));
TopoDS_Vertex aV3 = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 1, 0));
aBuilder.Add(aCompound, aV1);
aBuilder.Add(aCompound, aV2);
aBuilder.Add(aCompound, aV3);
// Remove middle vertex
aBuilder.Remove(aCompound, aV2);
// Count remaining children
int aCount = 0;
for (TopoDS_Iterator anIt(aCompound); anIt.More(); anIt.Next())
{
++aCount;
}
EXPECT_EQ(aCount, 2) << "Compound should have 2 vertices after removal";
}
//==================================================================================================
// Test adding many shapes to compound
//==================================================================================================
TEST(TopoDS_Builder_Test, Add_ManyShapes)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
// Add 500 vertices
for (int i = 0; i < 500; ++i)
{
TopoDS_Vertex aV = BRepBuilderAPI_MakeVertex(gp_Pnt(i, 0, 0));
aBuilder.Add(aCompound, aV);
}
// Verify count
int aCount = 0;
for (TopoDS_Iterator anIt(aCompound); anIt.More(); anIt.Next())
{
++aCount;
}
EXPECT_EQ(aCount, 500) << "Compound should have 500 vertices";
}
//==================================================================================================
// Test TShape flags through shape interface
//==================================================================================================
TEST(TopoDS_Builder_Test, TShapeFlags)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
// Test initial flags
EXPECT_TRUE(aCompound.Free()) << "New shape should be free";
EXPECT_TRUE(aCompound.Modified()) << "New shape should be modified";
// Test setting flags
aCompound.Checked(true);
EXPECT_TRUE(aCompound.Checked());
aCompound.Closed(true);
EXPECT_TRUE(aCompound.Closed());
aCompound.Infinite(true);
EXPECT_TRUE(aCompound.Infinite());
aCompound.Convex(true);
EXPECT_TRUE(aCompound.Convex());
}
//==================================================================================================
// Test TopoDS_Builder::Remove first child
//==================================================================================================
TEST(TopoDS_Builder_Test, Remove_FirstChild)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Vertex aV1 = BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0));
TopoDS_Vertex aV2 = BRepBuilderAPI_MakeVertex(gp_Pnt(2, 0, 0));
TopoDS_Vertex aV3 = BRepBuilderAPI_MakeVertex(gp_Pnt(3, 0, 0));
aBuilder.Add(aCompound, aV1);
aBuilder.Add(aCompound, aV2);
aBuilder.Add(aCompound, aV3);
// Remove first vertex
aBuilder.Remove(aCompound, aV1);
// Should have 2 remaining
int aCount = 0;
for (TopoDS_Iterator anIt(aCompound); anIt.More(); anIt.Next())
{
++aCount;
}
EXPECT_EQ(aCount, 2);
}
//==================================================================================================
// Test TopoDS_Builder::Remove last child
//==================================================================================================
TEST(TopoDS_Builder_Test, Remove_LastChild)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Vertex aV1 = BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0));
TopoDS_Vertex aV2 = BRepBuilderAPI_MakeVertex(gp_Pnt(2, 0, 0));
TopoDS_Vertex aV3 = BRepBuilderAPI_MakeVertex(gp_Pnt(3, 0, 0));
aBuilder.Add(aCompound, aV1);
aBuilder.Add(aCompound, aV2);
aBuilder.Add(aCompound, aV3);
// Remove last vertex
aBuilder.Remove(aCompound, aV3);
// Should have 2 remaining
int aCount = 0;
for (TopoDS_Iterator anIt(aCompound); anIt.More(); anIt.Next())
{
++aCount;
}
EXPECT_EQ(aCount, 2);
}
//==================================================================================================
// Test TopoDS_Builder::Remove all children one by one
//==================================================================================================
TEST(TopoDS_Builder_Test, Remove_AllChildren)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Vertex aV1 = BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0));
TopoDS_Vertex aV2 = BRepBuilderAPI_MakeVertex(gp_Pnt(2, 0, 0));
TopoDS_Vertex aV3 = BRepBuilderAPI_MakeVertex(gp_Pnt(3, 0, 0));
aBuilder.Add(aCompound, aV1);
aBuilder.Add(aCompound, aV2);
aBuilder.Add(aCompound, aV3);
// Remove all
aBuilder.Remove(aCompound, aV1);
aBuilder.Remove(aCompound, aV2);
aBuilder.Remove(aCompound, aV3);
// Should be empty
TopoDS_Iterator anIt(aCompound);
EXPECT_FALSE(anIt.More()) << "Compound should be empty after removing all children";
}
//==================================================================================================
// Test TopoDS_Builder::Remove from wire
//==================================================================================================
TEST(TopoDS_Builder_Test, Remove_FromWire)
{
TopoDS_Builder aBuilder;
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
TopoDS_Edge aE1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0));
TopoDS_Edge aE2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1, 0, 0), gp_Pnt(1, 1, 0));
TopoDS_Edge aE3 = BRepBuilderAPI_MakeEdge(gp_Pnt(1, 1, 0), gp_Pnt(0, 0, 0));
aBuilder.Add(aWire, aE1);
aBuilder.Add(aWire, aE2);
aBuilder.Add(aWire, aE3);
// Remove middle edge
aBuilder.Remove(aWire, aE2);
// Should have 2 edges
int aCount = 0;
for (TopoDS_Iterator anIt(aWire); anIt.More(); anIt.Next())
{
++aCount;
}
EXPECT_EQ(aCount, 2);
}
//==================================================================================================
// Test TopoDS_Builder with nested compounds
//==================================================================================================
TEST(TopoDS_Builder_Test, NestedCompounds)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aOuter, aInner;
aBuilder.MakeCompound(aOuter);
aBuilder.MakeCompound(aInner);
// Add vertices to inner
aBuilder.Add(aInner, BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0)));
aBuilder.Add(aInner, BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0)));
// Add inner to outer
aBuilder.Add(aOuter, aInner);
// Outer should have 1 child (the inner compound)
int aOuterCount = 0;
for (TopoDS_Iterator anIt(aOuter); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_COMPOUND);
++aOuterCount;
}
EXPECT_EQ(aOuterCount, 1);
// Inner should still have 2 vertices
EXPECT_EQ(aInner.TShape()->NbChildren(), 2);
}
//==================================================================================================
// Test TopoDS_Builder::Add sets Modified flag
//==================================================================================================
TEST(TopoDS_Builder_Test, Add_SetsModifiedFlag)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
// Clear modified flag
aCompound.TShape()->Modified(false);
EXPECT_FALSE(aCompound.Modified());
// Add should set modified
aBuilder.Add(aCompound, BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0)));
EXPECT_TRUE(aCompound.Modified()) << "Add should set Modified flag";
}
//==================================================================================================
// Test TopoDS_Builder::Remove sets Modified flag
//==================================================================================================
TEST(TopoDS_Builder_Test, Remove_SetsModifiedFlag)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Vertex aV = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0));
aBuilder.Add(aCompound, aV);
// Clear modified flag
aCompound.TShape()->Modified(false);
EXPECT_FALSE(aCompound.Modified());
// Remove should set modified
aBuilder.Remove(aCompound, aV);
EXPECT_TRUE(aCompound.Modified()) << "Remove should set Modified flag";
}
//==================================================================================================
// Test all shape types
//==================================================================================================
TEST(TopoDS_Builder_Test, AllTypes)
{
TopoDS_Builder aBuilder;
// Wire
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
EXPECT_FALSE(aWire.IsNull());
EXPECT_EQ(aWire.ShapeType(), TopAbs_WIRE);
EXPECT_TRUE(aWire.Free());
// Shell
TopoDS_Shell aShell;
aBuilder.MakeShell(aShell);
EXPECT_FALSE(aShell.IsNull());
EXPECT_EQ(aShell.ShapeType(), TopAbs_SHELL);
EXPECT_TRUE(aShell.Free());
// Solid
TopoDS_Solid aSolid;
aBuilder.MakeSolid(aSolid);
EXPECT_FALSE(aSolid.IsNull());
EXPECT_EQ(aSolid.ShapeType(), TopAbs_SOLID);
EXPECT_TRUE(aSolid.Free());
// CompSolid
TopoDS_CompSolid aCompSolid;
aBuilder.MakeCompSolid(aCompSolid);
EXPECT_FALSE(aCompSolid.IsNull());
EXPECT_EQ(aCompSolid.ShapeType(), TopAbs_COMPSOLID);
EXPECT_TRUE(aCompSolid.Free());
// Compound
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
EXPECT_FALSE(aCompound.IsNull());
EXPECT_EQ(aCompound.ShapeType(), TopAbs_COMPOUND);
EXPECT_TRUE(aCompound.Free());
}
//==================================================================================================
// Test mixed shapes in compound
//==================================================================================================
TEST(TopoDS_Builder_Test, MixedShapesInCompound)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
// Add different shape types
aBuilder.Add(aCompound, BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0)));
aBuilder.Add(aCompound, BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0)));
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
aBuilder.Add(aCompound, aWire);
TopoDS_Shell aShell;
aBuilder.MakeShell(aShell);
aBuilder.Add(aCompound, aShell);
TopoDS_Compound aInner;
aBuilder.MakeCompound(aInner);
aBuilder.Add(aCompound, aInner);
EXPECT_EQ(aCompound.TShape()->NbChildren(), 5);
// Verify types via iteration
int aVertexCount = 0, aEdgeCount = 0, aWireCount = 0, aShellCount = 0, aCompoundCount = 0;
for (TopoDS_Iterator anIt(aCompound); anIt.More(); anIt.Next())
{
switch (anIt.Value().ShapeType())
{
case TopAbs_VERTEX:
++aVertexCount;
break;
case TopAbs_EDGE:
++aEdgeCount;
break;
case TopAbs_WIRE:
++aWireCount;
break;
case TopAbs_SHELL:
++aShellCount;
break;
case TopAbs_COMPOUND:
++aCompoundCount;
break;
default:
break;
}
}
EXPECT_EQ(aVertexCount, 1);
EXPECT_EQ(aEdgeCount, 1);
EXPECT_EQ(aWireCount, 1);
EXPECT_EQ(aShellCount, 1);
EXPECT_EQ(aCompoundCount, 1);
}
@@ -11,10 +11,33 @@
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <gtest/gtest.h>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <gp_Pnt.hxx>
#include <gp_Trsf.hxx>
#include <gp_Vec.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Builder.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <TopExp_Explorer.hxx>
#include <TopLoc_Location.hxx>
#include <gtest/gtest.h>
//=================================================================================================
// Test OCC30708_1: Initialize with null shape
//=================================================================================================
TEST(TopoDS_Iterator_Test, OCC30708_1_InitializeWithNullShape)
{
@@ -27,3 +50,418 @@ TEST(TopoDS_Iterator_Test, OCC30708_1_InitializeWithNullShape)
// More() should return false on null shape
EXPECT_FALSE(it.More());
}
//=================================================================================================
// Test iterator on empty compound
//=================================================================================================
TEST(TopoDS_Iterator_Test, EmptyCompound)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Iterator anIt(aCompound);
EXPECT_FALSE(anIt.More()) << "Empty compound should have no children";
}
//=================================================================================================
// Test iterator on compound with vertices
//=================================================================================================
TEST(TopoDS_Iterator_Test, CompoundWithVertices)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
// Add 5 vertices
for (int i = 0; i < 5; ++i)
{
TopoDS_Vertex aV = BRepBuilderAPI_MakeVertex(gp_Pnt(i, 0, 0));
aBuilder.Add(aCompound, aV);
}
// Iterate and count
int aCount = 0;
for (TopoDS_Iterator anIt(aCompound); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_VERTEX);
++aCount;
}
EXPECT_EQ(aCount, 5) << "Should iterate over 5 vertices";
}
//=================================================================================================
// Test iterator on wire with edges
//=================================================================================================
TEST(TopoDS_Iterator_Test, WireWithEdges)
{
TopoDS_Builder aBuilder;
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
// Create a rectangular wire (4 edges)
TopoDS_Edge aE1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0));
TopoDS_Edge aE2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1, 0, 0), gp_Pnt(1, 1, 0));
TopoDS_Edge aE3 = BRepBuilderAPI_MakeEdge(gp_Pnt(1, 1, 0), gp_Pnt(0, 1, 0));
TopoDS_Edge aE4 = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 1, 0), gp_Pnt(0, 0, 0));
aBuilder.Add(aWire, aE1);
aBuilder.Add(aWire, aE2);
aBuilder.Add(aWire, aE3);
aBuilder.Add(aWire, aE4);
// Iterate and verify
int aCount = 0;
for (TopoDS_Iterator anIt(aWire); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_EDGE);
++aCount;
}
EXPECT_EQ(aCount, 4) << "Wire should have 4 edges";
}
//=================================================================================================
// Test iterator on edge with vertices
//=================================================================================================
TEST(TopoDS_Iterator_Test, EdgeWithVertices)
{
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 1, 1));
int aCount = 0;
for (TopoDS_Iterator anIt(anEdge); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_VERTEX);
++aCount;
}
EXPECT_EQ(aCount, 2) << "Edge should have 2 vertices";
}
//=================================================================================================
// Test iterator on vertex (no children)
//=================================================================================================
TEST(TopoDS_Iterator_Test, VertexNoChildren)
{
TopoDS_Vertex aVertex = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0));
TopoDS_Iterator anIt(aVertex);
EXPECT_FALSE(anIt.More()) << "Vertex should have no children";
}
//=================================================================================================
// Test iterator cumulative orientation
//=================================================================================================
TEST(TopoDS_Iterator_Test, CumulativeOrientation)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Vertex aV = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0));
aBuilder.Add(aCompound, aV);
// Reverse the compound
aCompound.Reverse();
// With cumOri=true (default), child orientation should be composed
TopoDS_Iterator anIt1(aCompound, true, true);
EXPECT_TRUE(anIt1.More());
TopAbs_Orientation anOri1 = anIt1.Value().Orientation();
// With cumOri=false, child orientation should be original
TopoDS_Iterator anIt2(aCompound, false, true);
EXPECT_TRUE(anIt2.More());
TopAbs_Orientation anOri2 = anIt2.Value().Orientation();
// Orientations should differ when compound is reversed
EXPECT_NE(anOri1, anOri2) << "Cumulative orientation should affect result";
}
//=================================================================================================
// Test iterator re-initialization
//=================================================================================================
TEST(TopoDS_Iterator_Test, ReInitialization)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound1, aCompound2;
aBuilder.MakeCompound(aCompound1);
aBuilder.MakeCompound(aCompound2);
// Add different number of vertices
for (int i = 0; i < 3; ++i)
{
aBuilder.Add(aCompound1, BRepBuilderAPI_MakeVertex(gp_Pnt(i, 0, 0)));
}
for (int i = 0; i < 5; ++i)
{
aBuilder.Add(aCompound2, BRepBuilderAPI_MakeVertex(gp_Pnt(i, 1, 0)));
}
TopoDS_Iterator anIt(aCompound1);
// Count first compound
int aCount1 = 0;
for (; anIt.More(); anIt.Next())
++aCount1;
EXPECT_EQ(aCount1, 3);
// Re-initialize with second compound
anIt.Initialize(aCompound2);
// Count second compound
int aCount2 = 0;
for (; anIt.More(); anIt.Next())
++aCount2;
EXPECT_EQ(aCount2, 5);
}
//=================================================================================================
// Test iterator on box solid (shell -> faces)
//=================================================================================================
TEST(TopoDS_Iterator_Test, BoxSolidIteration)
{
// Create a box
TopoDS_Shape aBox = BRepPrimAPI_MakeBox(1.0, 2.0, 3.0).Shape();
// Iterate over solid's children (should be 1 shell)
int aShellCount = 0;
for (TopoDS_Iterator anIt(aBox); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_SHELL);
++aShellCount;
// Iterate over shell's children (should be 6 faces)
int aFaceCount = 0;
for (TopoDS_Iterator anIt2(anIt.Value()); anIt2.More(); anIt2.Next())
{
EXPECT_EQ(anIt2.Value().ShapeType(), TopAbs_FACE);
++aFaceCount;
}
EXPECT_EQ(aFaceCount, 6) << "Box shell should have 6 faces";
}
EXPECT_EQ(aShellCount, 1) << "Box solid should have 1 shell";
}
//=================================================================================================
// Test iterator with many children (performance/stress test)
//=================================================================================================
TEST(TopoDS_Iterator_Test, ManyChildren)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
const int aNbVertices = 1000;
// Add many vertices
for (int i = 0; i < aNbVertices; ++i)
{
aBuilder.Add(aCompound, BRepBuilderAPI_MakeVertex(gp_Pnt(i, 0, 0)));
}
// Iterate and count
int aCount = 0;
for (TopoDS_Iterator anIt(aCompound); anIt.More(); anIt.Next())
{
++aCount;
}
EXPECT_EQ(aCount, aNbVertices) << "Should iterate over all vertices";
}
//=================================================================================================
// Test default constructor followed by Initialize
//=================================================================================================
TEST(TopoDS_Iterator_Test, DefaultConstructorThenInitialize)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
aBuilder.Add(aCompound, BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0)));
// Default construct, then initialize
TopoDS_Iterator anIt;
EXPECT_FALSE(anIt.More()) << "Default constructed iterator should have More()=false";
anIt.Initialize(aCompound);
EXPECT_TRUE(anIt.More()) << "After Initialize, More() should be true";
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_VERTEX);
}
//=================================================================================================
// Test Value() throws when not More() (only in debug builds with exceptions enabled)
//=================================================================================================
#ifndef No_Exception
TEST(TopoDS_Iterator_Test, ValueThrowsWhenNotMore)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Iterator anIt(aCompound);
EXPECT_FALSE(anIt.More());
// Value() should throw Standard_NoSuchObject when !More()
EXPECT_THROW(anIt.Value(), Standard_NoSuchObject);
}
#endif
//=================================================================================================
// Test iterator with cumulative location
//=================================================================================================
TEST(TopoDS_Iterator_Test, CumulativeLocation)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Vertex aV = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0));
aBuilder.Add(aCompound, aV);
// Apply a translation to the compound
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(10, 20, 30));
TopLoc_Location aLoc(aTrsf);
aCompound.Location(aLoc);
// With cumLoc=true (default), child should have composed location
TopoDS_Iterator anIt1(aCompound, true, true);
EXPECT_TRUE(anIt1.More());
TopLoc_Location aChildLoc1 = anIt1.Value().Location();
// With cumLoc=false, child should have original location
TopoDS_Iterator anIt2(aCompound, true, false);
EXPECT_TRUE(anIt2.More());
TopLoc_Location aChildLoc2 = anIt2.Value().Location();
// Locations should differ
EXPECT_NE(aChildLoc1.IsIdentity(), aChildLoc2.IsIdentity())
<< "Cumulative location should affect child location";
}
//=================================================================================================
// Test iterator on nested compounds
//=================================================================================================
TEST(TopoDS_Iterator_Test, NestedCompounds)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aOuter, aInner1, aInner2;
aBuilder.MakeCompound(aOuter);
aBuilder.MakeCompound(aInner1);
aBuilder.MakeCompound(aInner2);
// Add vertices to inner compounds
aBuilder.Add(aInner1, BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0)));
aBuilder.Add(aInner1, BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0)));
aBuilder.Add(aInner2, BRepBuilderAPI_MakeVertex(gp_Pnt(2, 0, 0)));
// Add inner compounds to outer
aBuilder.Add(aOuter, aInner1);
aBuilder.Add(aOuter, aInner2);
// Outer should have 2 children (both compounds)
int aOuterCount = 0;
for (TopoDS_Iterator anIt(aOuter); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_COMPOUND);
++aOuterCount;
}
EXPECT_EQ(aOuterCount, 2);
// First inner should have 2 children
TopoDS_Iterator anIt(aOuter);
int aInner1Count = 0;
for (TopoDS_Iterator anIt2(anIt.Value()); anIt2.More(); anIt2.Next())
{
++aInner1Count;
}
EXPECT_EQ(aInner1Count, 2);
}
//=================================================================================================
// Test iterator on face with wires (from box)
//=================================================================================================
TEST(TopoDS_Iterator_Test, FaceWithWires)
{
TopoDS_Shape aBox = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape();
// Get first face
TopExp_Explorer anExp(aBox, TopAbs_FACE);
ASSERT_TRUE(anExp.More());
const TopoDS_Face& aFace = TopoDS::Face(anExp.Current());
// Iterate over face's children (should be wires)
int aWireCount = 0;
for (TopoDS_Iterator anIt(aFace); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_WIRE);
++aWireCount;
}
EXPECT_GE(aWireCount, 1) << "Face should have at least 1 wire";
}
//=================================================================================================
// Test iterator preserves shape identity
//=================================================================================================
TEST(TopoDS_Iterator_Test, ShapeIdentity)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_Vertex aV1 = BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0));
TopoDS_Vertex aV2 = BRepBuilderAPI_MakeVertex(gp_Pnt(2, 0, 0));
aBuilder.Add(aCompound, aV1);
aBuilder.Add(aCompound, aV2);
// Iterate and check that TShape pointers match
TopoDS_Iterator anIt(aCompound);
EXPECT_TRUE(anIt.Value().TShape() == aV1.TShape() || anIt.Value().TShape() == aV2.TShape());
anIt.Next();
EXPECT_TRUE(anIt.Value().TShape() == aV1.TShape() || anIt.Value().TShape() == aV2.TShape());
}
//=================================================================================================
// Test iterator on shell with faces
//=================================================================================================
TEST(TopoDS_Iterator_Test, ShellWithFaces)
{
TopoDS_Shape aBox = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape();
// Get shell from box
TopExp_Explorer anExp(aBox, TopAbs_SHELL);
ASSERT_TRUE(anExp.More());
const TopoDS_Shell& aShell = TopoDS::Shell(anExp.Current());
// Iterate over shell's faces
int aFaceCount = 0;
for (TopoDS_Iterator anIt(aShell); anIt.More(); anIt.Next())
{
EXPECT_EQ(anIt.Value().ShapeType(), TopAbs_FACE);
++aFaceCount;
}
EXPECT_EQ(aFaceCount, 6) << "Box shell should have 6 faces";
}
@@ -0,0 +1,313 @@
// Copyright (c) 2026 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 <gtest/gtest.h>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Builder.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Solid.hxx>
#include <TopoDS_TShape.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <TopExp_Explorer.hxx>
#include <gp_Pnt.hxx>
//==================================================================================================
// Test TShape ShapeType() returns correct type for each shape kind
//==================================================================================================
TEST(TopoDS_TShape_Test, ShapeType_AllTypes)
{
TopoDS_Builder aBuilder;
// Create various shapes and verify ShapeType
TopoDS_Vertex aVertex = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0));
EXPECT_EQ(aVertex.TShape()->ShapeType(), TopAbs_VERTEX);
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0));
EXPECT_EQ(anEdge.TShape()->ShapeType(), TopAbs_EDGE);
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
EXPECT_EQ(aWire.TShape()->ShapeType(), TopAbs_WIRE);
TopoDS_Shell aShell;
aBuilder.MakeShell(aShell);
EXPECT_EQ(aShell.TShape()->ShapeType(), TopAbs_SHELL);
TopoDS_Solid aSolid;
aBuilder.MakeSolid(aSolid);
EXPECT_EQ(aSolid.TShape()->ShapeType(), TopAbs_SOLID);
TopoDS_CompSolid aCompSolid;
aBuilder.MakeCompSolid(aCompSolid);
EXPECT_EQ(aCompSolid.TShape()->ShapeType(), TopAbs_COMPSOLID);
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
EXPECT_EQ(aCompound.TShape()->ShapeType(), TopAbs_COMPOUND);
}
//==================================================================================================
// Test TShape flag setters and getters
//==================================================================================================
TEST(TopoDS_TShape_Test, FlagSettersGetters)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_TShape* aTShape = aCompound.TShape().get();
// Test Free flag
EXPECT_TRUE(aTShape->Free());
aTShape->Free(false);
EXPECT_FALSE(aTShape->Free());
aTShape->Free(true);
EXPECT_TRUE(aTShape->Free());
// Test Modified flag
EXPECT_TRUE(aTShape->Modified());
aTShape->Modified(false);
EXPECT_FALSE(aTShape->Modified());
// Test Checked flag
EXPECT_FALSE(aTShape->Checked());
aTShape->Checked(true);
EXPECT_TRUE(aTShape->Checked());
// Test that setting Modified(true) clears Checked
aTShape->Modified(true);
EXPECT_FALSE(aTShape->Checked()) << "Modified(true) should clear Checked flag";
// Test Orientable flag
EXPECT_FALSE(aTShape->Orientable()) << "Compound should not be orientable";
// Test Closed flag
EXPECT_FALSE(aTShape->Closed());
aTShape->Closed(true);
EXPECT_TRUE(aTShape->Closed());
// Test Infinite flag
EXPECT_FALSE(aTShape->Infinite());
aTShape->Infinite(true);
EXPECT_TRUE(aTShape->Infinite());
// Test Convex flag
EXPECT_FALSE(aTShape->Convex());
aTShape->Convex(true);
EXPECT_TRUE(aTShape->Convex());
// Test Locked flag
EXPECT_FALSE(aTShape->Locked());
aTShape->Locked(true);
EXPECT_TRUE(aTShape->Locked());
}
//==================================================================================================
// Test TShape NbChildren() for various shapes
//==================================================================================================
TEST(TopoDS_TShape_Test, NbChildren)
{
TopoDS_Builder aBuilder;
// Vertex has 0 children
TopoDS_Vertex aVertex = BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0));
EXPECT_EQ(aVertex.TShape()->NbChildren(), 0);
// Edge has 2 children (vertices)
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0));
EXPECT_EQ(anEdge.TShape()->NbChildren(), 2);
// Empty wire has 0 children
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
EXPECT_EQ(aWire.TShape()->NbChildren(), 0);
// Wire with edges
aBuilder.Add(aWire, anEdge);
EXPECT_EQ(aWire.TShape()->NbChildren(), 1);
// Compound with multiple children
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
for (int i = 0; i < 10; ++i)
{
aBuilder.Add(aCompound, BRepBuilderAPI_MakeVertex(gp_Pnt(i, 0, 0)));
}
EXPECT_EQ(aCompound.TShape()->NbChildren(), 10);
}
//==================================================================================================
// Test TShape EmptyCopy creates same type with no children
//==================================================================================================
TEST(TopoDS_TShape_Test, EmptyCopy)
{
TopoDS_Builder aBuilder;
// Create compound with children
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
aBuilder.Add(aCompound, BRepBuilderAPI_MakeVertex(gp_Pnt(0, 0, 0)));
aBuilder.Add(aCompound, BRepBuilderAPI_MakeVertex(gp_Pnt(1, 0, 0)));
EXPECT_EQ(aCompound.TShape()->NbChildren(), 2);
// EmptyCopy should create same type with no children
occ::handle<TopoDS_TShape> aCopy = aCompound.TShape()->EmptyCopy();
EXPECT_EQ(aCopy->ShapeType(), TopAbs_COMPOUND);
EXPECT_EQ(aCopy->NbChildren(), 0) << "EmptyCopy should have no children";
}
//==================================================================================================
// Test TShape EmptyCopy for all shape types
//==================================================================================================
TEST(TopoDS_TShape_Test, EmptyCopy_AllTypes)
{
TopoDS_Builder aBuilder;
// Test Edge EmptyCopy
{
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0));
occ::handle<TopoDS_TShape> aCopy = anEdge.TShape()->EmptyCopy();
EXPECT_EQ(aCopy->ShapeType(), TopAbs_EDGE);
EXPECT_EQ(aCopy->NbChildren(), 0);
}
// Test Wire EmptyCopy
{
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
occ::handle<TopoDS_TShape> aCopy = aWire.TShape()->EmptyCopy();
EXPECT_EQ(aCopy->ShapeType(), TopAbs_WIRE);
}
// Test Shell EmptyCopy
{
TopoDS_Shell aShell;
aBuilder.MakeShell(aShell);
occ::handle<TopoDS_TShape> aCopy = aShell.TShape()->EmptyCopy();
EXPECT_EQ(aCopy->ShapeType(), TopAbs_SHELL);
}
// Test Solid EmptyCopy
{
TopoDS_Solid aSolid;
aBuilder.MakeSolid(aSolid);
occ::handle<TopoDS_TShape> aCopy = aSolid.TShape()->EmptyCopy();
EXPECT_EQ(aCopy->ShapeType(), TopAbs_SOLID);
}
// Test CompSolid EmptyCopy
{
TopoDS_CompSolid aCompSolid;
aBuilder.MakeCompSolid(aCompSolid);
occ::handle<TopoDS_TShape> aCopy = aCompSolid.TShape()->EmptyCopy();
EXPECT_EQ(aCopy->ShapeType(), TopAbs_COMPSOLID);
}
}
//==================================================================================================
// Test TShape Orientable flag for different shape types
//==================================================================================================
TEST(TopoDS_TShape_Test, Orientable_DifferentTypes)
{
TopoDS_Builder aBuilder;
// Edge should be orientable
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0));
EXPECT_TRUE(anEdge.TShape()->Orientable()) << "Edge should be orientable";
// Compound should not be orientable
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
EXPECT_FALSE(aCompound.TShape()->Orientable()) << "Compound should not be orientable";
// Wire should be orientable
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
EXPECT_TRUE(aWire.TShape()->Orientable()) << "Wire should be orientable";
}
//==================================================================================================
// Test TShape flags are independent
//==================================================================================================
TEST(TopoDS_TShape_Test, FlagsIndependent)
{
TopoDS_Builder aBuilder;
TopoDS_Compound aCompound;
aBuilder.MakeCompound(aCompound);
TopoDS_TShape* aTShape = aCompound.TShape().get();
// Set all flags
aTShape->Closed(true);
aTShape->Infinite(true);
aTShape->Convex(true);
aTShape->Locked(true);
// Verify all are set
EXPECT_TRUE(aTShape->Closed());
EXPECT_TRUE(aTShape->Infinite());
EXPECT_TRUE(aTShape->Convex());
EXPECT_TRUE(aTShape->Locked());
// Clear one flag, others should remain
aTShape->Closed(false);
EXPECT_FALSE(aTShape->Closed());
EXPECT_TRUE(aTShape->Infinite()) << "Clearing Closed should not affect Infinite";
EXPECT_TRUE(aTShape->Convex()) << "Clearing Closed should not affect Convex";
EXPECT_TRUE(aTShape->Locked()) << "Clearing Closed should not affect Locked";
}
//==================================================================================================
// Test NbChildren consistency with iterator
//==================================================================================================
TEST(TopoDS_TShape_Test, NbChildren_ConsistencyWithIterator)
{
TopoDS_Builder aBuilder;
// Create wire with edge
TopoDS_Wire aWire;
aBuilder.MakeWire(aWire);
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 0, 0));
aBuilder.Add(aWire, anEdge);
// Get NbChildren via TShape
int aNbViaTShape = aWire.TShape()->NbChildren();
// Count via iterator
int aNbViaIterator = 0;
for (TopoDS_Iterator anIt(aWire); anIt.More(); anIt.Next())
{
++aNbViaIterator;
}
EXPECT_EQ(aNbViaTShape, aNbViaIterator) << "NbChildren should match iterator count";
EXPECT_EQ(aNbViaTShape, 1);
}