diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_LinearVector_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_LinearVector_Test.cxx index db190e8126..323d3d2477 100644 --- a/src/FoundationClasses/TKernel/GTests/NCollection_LinearVector_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/NCollection_LinearVector_Test.cxx @@ -827,8 +827,8 @@ static_assert(std::is_same_v "Mutable NCollection_LinearVector should expose mutable Array1 view"); static_assert( std::is_same_v&>().ToArray1()), - NCollection_Array1>, - "Const NCollection_LinearVector should expose read-only Array1 view"); + NCollection_Array1>, + "Const NCollection_LinearVector should expose Array1 view"); // Verify that ToArray1() returns an Array1 sharing the same memory buffer. TEST(NCollection_LinearVectorTest, ToArray1_SamePointer) diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_LocalArray_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_LocalArray_Test.cxx index 3e9047b19e..f03e606882 100644 --- a/src/FoundationClasses/TKernel/GTests/NCollection_LocalArray_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/NCollection_LocalArray_Test.cxx @@ -628,8 +628,8 @@ static_assert(std::is_same_v&> "Mutable NCollection_LocalArray should expose mutable Array1 view"); static_assert( std::is_same_v&>().ToArray1()), - NCollection_Array1>, - "Const NCollection_LocalArray should expose read-only Array1 view"); + NCollection_Array1>, + "Const NCollection_LocalArray should expose Array1 view"); // Verify that ToArray1() returns an Array1 sharing the same memory buffer. TEST(NCollection_LocalArrayTest, ToArray1_SamePointer) diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_LinearVector.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_LinearVector.hxx index a5df22a749..2685fc8251 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_LinearVector.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_LinearVector.hxx @@ -491,19 +491,11 @@ public: //! Returns a span as Array1 with shared memory. //! Modifying the vector or the array may invalidate the shared buffer. //! @return array view of the vector data - NCollection_Array1 ToArray1() + NCollection_Array1 ToArray1() const { return NCollection_Array1(myData, mySize); } - //! Returns a read-only span as Array1 with shared memory. - //! Modifying the vector may invalidate the shared buffer. - //! @return const array view of the vector data - NCollection_Array1 ToArray1() const - { - return NCollection_Array1(myData, mySize); - } - //! @return iterator to the first element. iterator begin() noexcept { return myData; } diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_LocalArray.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_LocalArray.hxx index d4b16ef514..656bc5d97f 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_LocalArray.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_LocalArray.hxx @@ -305,14 +305,9 @@ public: //! Returns a span as Array1 with shared memory. //! Modifying the local array or the array view may invalidate the shared buffer. //! @return array view of the local array data - NCollection_Array1 ToArray1() { return NCollection_Array1(myPtr, mySize); } - - //! Returns a read-only span as Array1 with shared memory. - //! Modifying the local array may invalidate the shared buffer. - //! @return const array view of the array data - NCollection_Array1 ToArray1() const + NCollection_Array1 ToArray1() const { - return NCollection_Array1(myPtr, mySize); + return NCollection_Array1(myPtr, mySize); } NCollection_LocalArray(const NCollection_LocalArray&) = delete; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx index 1541649be4..a28875042e 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.cxx @@ -12,18 +12,17 @@ // commercial license or contractual agreement. #include + #include #include #include #include #include - -#include -#include +#include #include - #include #include +#include #include #include #include @@ -55,38 +54,30 @@ Standard_GUID generateRandomGUID() //================================================================================================= -void BRepGraph::initViews() -{ - if (myData) - { - myData->myTopoView = TopoView(this); - myData->myUIDsView = UIDsView(this); - myData->myCacheView = CacheView(this); - myData->myRefsView = RefsView(this); - myData->myShapesView = ShapesView(this); - myData->myEditorView = EditorView(this); - myData->myMeshView = MeshView(this); - } - myLayerRegistry.SetOwningGraph(this); -} - BRepGraph::BRepGraph() : myData(std::make_unique()) { - initViews(); + initViewsAndRegistries(); + (void)myData->myLayerRegistry.Ensure(); } //================================================================================================= -BRepGraph::BRepGraph(const occ::handle& theAlloc) - : myData(std::make_unique(theAlloc)) +BRepGraph::~BRepGraph() { - initViews(); + if (myData != nullptr) + { + myData->myLayerRegistry.Detach(); + myData->myCacheRegistry.Detach(); + } } //================================================================================================= -BRepGraph::~BRepGraph() = default; +bool BRepGraph::IsValid() const noexcept +{ + return myData != nullptr; +} //================================================================================================= @@ -104,20 +95,6 @@ const BRepGraph::UIDsView& BRepGraph::UIDs() const //================================================================================================= -BRepGraph::CacheView& BRepGraph::Cache() -{ - return myData->myCacheView; -} - -//================================================================================================= - -const BRepGraph::CacheView& BRepGraph::Cache() const -{ - return myData->myCacheView; -} - -//================================================================================================= - const BRepGraph::RefsView& BRepGraph::Refs() const { return myData->myRefsView; @@ -125,6 +102,13 @@ const BRepGraph::RefsView& BRepGraph::Refs() const //================================================================================================= +BRepGraph::ShapesView& BRepGraph::Shapes() +{ + return myData->myShapesView; +} + +//================================================================================================= + const BRepGraph::ShapesView& BRepGraph::Shapes() const { return myData->myShapesView; @@ -153,14 +137,17 @@ const BRepGraph::MeshView& BRepGraph::Mesh() const //================================================================================================= -BRepGraph::BRepGraph(BRepGraph&& theOther) noexcept - : myData(std::move(theOther.myData)), - myLayerRegistry(std::move(theOther.myLayerRegistry)), - myTransientCache(std::move(theOther.myTransientCache)) +BRepGraph::MeshView& BRepGraph::Mesh() { - // View objects store a back-pointer to the owning BRepGraph; after move, - // they must point to the new owner (`this`), not the moved-from object. - initViews(); + return myData->myMeshView; +} + +//================================================================================================= + +BRepGraph::BRepGraph(BRepGraph&& theOther) noexcept + : myData(std::move(theOther.myData)) +{ + initViewsAndRegistries(); } //================================================================================================= @@ -169,12 +156,13 @@ BRepGraph& BRepGraph::operator=(BRepGraph&& theOther) noexcept { if (this != &theOther) { - myData = std::move(theOther.myData); - myLayerRegistry = std::move(theOther.myLayerRegistry); - myTransientCache = std::move(theOther.myTransientCache); - // View objects store a back-pointer to the owning BRepGraph; after move, - // they must point to the new owner (`this`), not the moved-from object. - initViews(); + if (myData != nullptr) + { + myData->myLayerRegistry.Detach(); + myData->myCacheRegistry.Detach(); + } + myData = std::move(theOther.myData); + initViewsAndRegistries(); } return *this; } @@ -183,104 +171,51 @@ BRepGraph& BRepGraph::operator=(BRepGraph&& theOther) noexcept BRepGraph_UID BRepGraph::allocateUID(const BRepGraph_NodeId theNodeId) { - // Load counter before append: if Append() throws, no counter is consumed. - // Single-threaded precondition: UID allocation is only called from Editor - // methods which are externally serialized, so load-then-increment is safe. - const size_t aCounter = myData->myNextUIDCounter.load(std::memory_order_relaxed); - const uint32_t aGeneration = myData->myGeneration.load(std::memory_order_relaxed); - BRepGraph_UID aUID(theNodeId.NodeKind, aCounter, aGeneration); - myData->myIncStorage.ChangeUIDs(theNodeId.NodeKind).Append(aUID); - { - std::unique_lock aLock(myData->myUIDToNodeIdMutex); - if (myData->myUIDToNodeIdGeneration != aGeneration) - { - myData->myUIDToNodeId.Clear(); - myData->myUIDToNodeIdGeneration = aGeneration; - myData->myUIDToNodeIdDirty = false; - } - if (!myData->myUIDToNodeIdDirty) - { - myData->myUIDToNodeId.Bind(aUID, theNodeId); - } - } - myData->myNextUIDCounter.fetch_add(1, std::memory_order_relaxed); - return aUID; + return myData->myIncStorage.AllocateNodeUID(theNodeId); } //================================================================================================= BRepGraph_RefUID BRepGraph::allocateRefUID(const BRepGraph_RefId theRefId) { - // Load counter before append: if Append() throws, no counter is consumed. - // Single-threaded precondition: see allocateUID() comment. - const size_t aCounter = myData->myNextUIDCounter.load(std::memory_order_relaxed); - const uint32_t aGeneration = myData->myGeneration.load(std::memory_order_relaxed); - const BRepGraph_RefUID aUID(theRefId.RefKind, aCounter, aGeneration); - myData->myIncStorage.ChangeRefUIDs(theRefId.RefKind).Append(aUID); - { - std::unique_lock aLock(myData->myRefUIDToRefIdMutex); - if (myData->myRefUIDToRefIdGeneration != aGeneration) - { - myData->myRefUIDToRefId.Clear(); - myData->myRefUIDToRefIdGeneration = aGeneration; - myData->myRefUIDToRefIdDirty = false; - } - if (!myData->myRefUIDToRefIdDirty) - { - myData->myRefUIDToRefId.Bind(aUID, theRefId); - } - } - myData->myNextUIDCounter.fetch_add(1, std::memory_order_relaxed); - return aUID; + return myData->myIncStorage.AllocateRefUID(theRefId); } //================================================================================================= +//================================================================================================= + void BRepGraph::Clear() { + Standard_ASSERT_RAISE(!myData->myIncStorage.HasAnyGuard(), + "BRepGraph::Clear(): guards still active"); myData->myIncStorage.Clear(); - myData->myHistoryLog.Clear(); - myData->myCurrentShapes.Clear(); - myData->myRootProductIds.Clear(); - myTransientCache.Clear(); - { - std::unique_lock aUIDLock(myData->myUIDToNodeIdMutex); - myData->myUIDToNodeId.Clear(); - myData->myUIDToNodeIdDirty = true; - myData->myUIDToNodeIdGeneration = myData->myGeneration.load(); - } - { - std::unique_lock aRefUIDLock(myData->myRefUIDToRefIdMutex); - myData->myRefUIDToRefId.Clear(); - myData->myRefUIDToRefIdDirty = true; - myData->myRefUIDToRefIdGeneration = myData->myGeneration.load(); - } - ++myData->myGeneration; - myData->myGraphGUID = generateRandomGUID(); - myData->myIsDone = false; + myData->myCacheRegistry.ClearAll(); + myData->myIncStorage.IncrementGeneration(); + myData->myIncStorage.SetGraphGUID(generateRandomGUID()); - myLayerRegistry.ClearAll(); + myData->myLayerRegistry.ClearAll(); } //================================================================================================= -bool BRepGraph::IsDone() const +bool BRepGraph::IsEmpty() const { - return myData->myIsDone; + return myData->myIncStorage.IsEmpty(); } //================================================================================================= -bool BRepGraph::ValidateReverseIndex() const +bool BRepGraph::ValidateRelations() const { - return myData->myIncStorage.ValidateReverseIndex(); + return myData->myIncStorage.ValidateRelations(); } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::RootProductIds() const +const NCollection_LinearVector& BRepGraph::RootProductIds() const { - return myData->myRootProductIds; + return myData->myIncStorage.RootProductIds(); } //================================================================================================= @@ -405,6 +340,167 @@ BRepGraphInc::BaseDef* BRepGraph::changeTopoEntity(const BRepGraph_NodeId theId) //================================================================================================= +bool BRepGraph_NodeId::IsRemoved(const BRepGraph& theGraph) const +{ + if (!IsValid()) + { + return false; + } + const BRepGraphInc_Storage& aS = theGraph.myData->myIncStorage; + switch (NodeKind) + { + case Kind::Vertex: + return aS.IsRemoved(BRepGraph_VertexId(*this)); + case Kind::Edge: + return aS.IsRemoved(BRepGraph_EdgeId(*this)); + case Kind::CoEdge: + return aS.IsRemoved(BRepGraph_CoEdgeId(*this)); + case Kind::Wire: + return aS.IsRemoved(BRepGraph_WireId(*this)); + case Kind::Face: + return aS.IsRemoved(BRepGraph_FaceId(*this)); + case Kind::Shell: + return aS.IsRemoved(BRepGraph_ShellId(*this)); + case Kind::Solid: + return aS.IsRemoved(BRepGraph_SolidId(*this)); + case Kind::Compound: + return aS.IsRemoved(BRepGraph_CompoundId(*this)); + case Kind::CompSolid: + return aS.IsRemoved(BRepGraph_CompSolidId(*this)); + case Kind::Product: + return aS.IsRemoved(BRepGraph_ProductId(*this)); + case Kind::Occurrence: + return aS.IsRemoved(BRepGraph_OccurrenceId(*this)); + } + return false; +} + +//================================================================================================= + +bool BRepGraph_NodeId::IsOwned(const BRepGraph& theGraph) const +{ + if (!IsValid()) + { + return false; + } + const BRepGraphInc_Storage& aS = theGraph.myData->myIncStorage; + switch (NodeKind) + { + case Kind::Vertex: + return aS.IsOwned(BRepGraph_VertexId(*this)); + case Kind::Edge: + return aS.IsOwned(BRepGraph_EdgeId(*this)); + case Kind::CoEdge: + return aS.IsOwned(BRepGraph_CoEdgeId(*this)); + case Kind::Wire: + return aS.IsOwned(BRepGraph_WireId(*this)); + case Kind::Face: + return aS.IsOwned(BRepGraph_FaceId(*this)); + case Kind::Shell: + return aS.IsOwned(BRepGraph_ShellId(*this)); + case Kind::Solid: + return aS.IsOwned(BRepGraph_SolidId(*this)); + case Kind::Compound: + return aS.IsOwned(BRepGraph_CompoundId(*this)); + case Kind::CompSolid: + return aS.IsOwned(BRepGraph_CompSolidId(*this)); + case Kind::Product: + return aS.IsOwned(BRepGraph_ProductId(*this)); + case Kind::Occurrence: + return aS.IsOwned(BRepGraph_OccurrenceId(*this)); + } + return false; +} + +//================================================================================================= + +bool BRepGraph_RefId::IsRemoved(const BRepGraph& theGraph) const +{ + if (!IsValid()) + { + return false; + } + const BRepGraphInc_Storage& aS = theGraph.myData->myIncStorage; + switch (RefKind) + { + case Kind::Shell: + return aS.IsRemoved(BRepGraph_ShellRefId(*this)); + case Kind::Face: + return aS.IsRemoved(BRepGraph_FaceRefId(*this)); + case Kind::Wire: + return aS.IsRemoved(BRepGraph_WireRefId(*this)); + case Kind::Vertex: + return aS.IsRemoved(BRepGraph_VertexRefId(*this)); + case Kind::Solid: + return aS.IsRemoved(BRepGraph_SolidRefId(*this)); + case Kind::Child: + return aS.IsRemoved(BRepGraph_ChildRefId(*this)); + case Kind::Occurrence: + return aS.IsRemoved(BRepGraph_OccurrenceRefId(*this)); + } + return false; +} + +//================================================================================================= + +bool BRepGraph_RepId::IsRemoved(const BRepGraph& theGraph) const +{ + if (!IsValid()) + { + return false; + } + const BRepGraphInc_Storage& aS = theGraph.myData->myIncStorage; + switch (RepKind) + { + case Kind::EdgeCurve3D: + return aS.IsRemoved(BRepGraph_EdgeCurve3DRepId(Index)); + case Kind::EdgePolygon3D: + return aS.IsRemoved(BRepGraph_EdgePolygon3DRepId(Index)); + case Kind::CoEdgeCurve2D: + return aS.IsRemoved(BRepGraph_CoEdgeCurve2DRepId(Index)); + case Kind::CoEdgePolygon2D: + return aS.IsRemoved(BRepGraph_CoEdgePolygon2DRepId(Index)); + case Kind::CoEdgePolygonOnTri: + return aS.IsRemoved(BRepGraph_CoEdgePolygonOnTriRepId(Index)); + case Kind::FaceSurface: + return aS.IsRemoved(BRepGraph_FaceSurfaceRepId(Index)); + case Kind::FaceTriangulation: + return aS.IsRemoved(BRepGraph_FaceTriangulationRepId(Index)); + } + return false; +} + +//================================================================================================= + +bool BRepGraph_RefId::IsOwned(const BRepGraph& theGraph) const +{ + if (!IsValid()) + { + return false; + } + const BRepGraphInc_Storage& aS = theGraph.myData->myIncStorage; + switch (RefKind) + { + case Kind::Shell: + return aS.IsOwned(BRepGraph_ShellRefId(*this)); + case Kind::Face: + return aS.IsOwned(BRepGraph_FaceRefId(*this)); + case Kind::Wire: + return aS.IsOwned(BRepGraph_WireRefId(*this)); + case Kind::Vertex: + return aS.IsOwned(BRepGraph_VertexRefId(*this)); + case Kind::Solid: + return aS.IsOwned(BRepGraph_SolidRefId(*this)); + case Kind::Child: + return aS.IsOwned(BRepGraph_ChildRefId(*this)); + case Kind::Occurrence: + return aS.IsOwned(BRepGraph_OccurrenceRefId(*this)); + } + return false; +} + +//================================================================================================= + void BRepGraph::invalidateSubgraphImpl(const BRepGraph_NodeId theNode) { if (!theNode.IsValid()) @@ -433,7 +529,7 @@ void BRepGraph::invalidateSubgraphImpl(const BRepGraph_NodeId theNode) + aStorage.NbProducts() + aStorage.NbOccurrences(); const uint32_t aMaxDepth = aNbNodes > 0 ? aNbNodes : 1; occ::handle anAlloc = new NCollection_IncAllocator(); - NCollection_DynamicArray aStack(64, anAlloc); + NCollection_LinearVector aStack(64); NCollection_Map aVisited(static_cast(aNbNodes), anAlloc); aStack.Append({theNode, 0}); @@ -492,31 +588,21 @@ void BRepGraph::invalidateSubgraphImpl(const BRepGraph_NodeId theNode) break; } case Kind::Solid: { - const BRepGraphInc::SolidDef& aSolidEnt = aStorage.Solid(BRepGraph_SolidId(aCurrent.Node)); for (BRepGraph_DefsShellOfSolid aChildIt(*this, BRepGraph_SolidId(aCurrent.Node)); aChildIt.More(); aChildIt.Next()) { aPushChild(aChildIt.CurrentId(), aNextDepth); } - for (const BRepGraph_ChildRefId& aChildRefId : aSolidEnt.AuxChildRefIds) - { - aPushChild(aStorage.ChildRef(aChildRefId).ChildDefId, aNextDepth); - } break; } case Kind::Shell: { - const BRepGraphInc::ShellDef& aShellEnt = aStorage.Shell(BRepGraph_ShellId(aCurrent.Node)); for (BRepGraph_DefsFaceOfShell aChildIt(*this, BRepGraph_ShellId(aCurrent.Node)); aChildIt.More(); aChildIt.Next()) { aPushChild(aChildIt.CurrentId(), aNextDepth); } - for (const BRepGraph_ChildRefId& aChildRefId : aShellEnt.AuxChildRefIds) - { - aPushChild(aStorage.ChildRef(aChildRefId).ChildDefId, aNextDepth); - } break; } case Kind::Face: { @@ -552,14 +638,14 @@ void BRepGraph::invalidateSubgraphImpl(const BRepGraph_NodeId theNode) aChildIt.More(); aChildIt.Next()) { - aPushChild(aStorage.OccurrenceRef(aChildIt.CurrentId()).OccurrenceDefId, aNextDepth); + aPushChild(aStorage.OccurrenceRef(aChildIt.CurrentId()).ChildOccurrenceId, aNextDepth); } break; } case Kind::Occurrence: { const BRepGraphInc::OccurrenceDef& anOcc = aStorage.Occurrence(BRepGraph_OccurrenceId(aCurrent.Node)); - aPushChild(anOcc.ChildDefId, aNextDepth); + aPushChild(anOcc.ChildNodeId, aNextDepth); break; } default: @@ -593,20 +679,20 @@ void BRepGraph::markModified(const BRepGraph_NodeId theNodeId, { ++theEntity.OwnGen; ++theEntity.SubtreeGen; - const uint32_t aWave = myData->myPropagationWave.fetch_add(1, std::memory_order_relaxed) + 1; + const uint32_t aWave = myData->myIncStorage.AdvancePropagationWave(); theEntity.LastPropWave = aWave; // In deferred mode: accumulate for batch processing. - if (myData->myDeferredMode.load(std::memory_order_relaxed)) + if (myData->myIncStorage.DeferredMode()) { - myData->myDeferredModified.Append(theNodeId); + myData->myIncStorage.ChangeDeferredModified().Append(theNodeId); return; } // Dispatch modification event for the directly mutated node. - if (myLayerRegistry.HasModificationSubscribers()) + if (myData->myLayerRegistry.HasModificationSubscribers()) { - myLayerRegistry.DispatchNodeModified(theNodeId); + myData->myLayerRegistry.DispatchNodeModified(theNodeId); } // Propagate SubtreeGen upward to parents (mutex-free). @@ -622,37 +708,129 @@ void BRepGraph::markRefModified(const BRepGraph_RefId theRefId) noexcept return; } - BRepGraphInc::BaseRef& aRef = myData->myIncStorage.ChangeBaseRef(theRefId); - markRefModified(theRefId, aRef); -} - -//================================================================================================= - -void BRepGraph::markRefModified(const BRepGraph_RefId theRefId, - BRepGraphInc::BaseRef& theRef) noexcept -{ - ++theRef.OwnGen; - myData->myPropagationWave.fetch_add(1, std::memory_order_relaxed); - // Only dispatch modification events for active (non-removed) refs. - if (!theRef.IsRemoved) + if (!theRefId.IsRemoved(*this)) { - if (myData->myDeferredMode.load(std::memory_order_relaxed)) + if (myData->myIncStorage.DeferredMode()) { - myData->myDeferredRefModified.Append(theRefId); + myData->myIncStorage.ChangeDeferredRefModified().Append(theRefId); } - else if (myLayerRegistry.HasRefModificationSubscribers()) + else if (myData->myLayerRegistry.HasRefModificationSubscribers()) { - myLayerRegistry.DispatchRefModified(theRefId); + myData->myLayerRegistry.DispatchRefModified(theRefId); } } - if (!theRef.ParentId.IsValid()) + const BRepGraphInc_Storage& aStorage = myData->myIncStorage; + switch (theRefId.RefKind) { - return; + case BRepGraph_RefId::Kind::Vertex: { + const BRepGraph_VertexRefId aRefId(theRefId); + if (!aRefId.IsValid(aStorage.NbVertexRefs())) + { + break; + } + const BRepGraph_VertexId aVertexId = aStorage.VertexRef(aRefId).ChildVertexId; + if (!aVertexId.IsValid(aStorage.NbVertices())) + { + break; + } + for (const BRepGraph_EdgeId& anEdgeId : aStorage.VertexRelations(aVertexId).EdgeIds) + { + if (!anEdgeId.IsValid(aStorage.NbEdges())) + { + continue; + } + const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(anEdgeId); + if (!anEdgeId.IsRemoved(*this) + && (anEdge.StartVertexRefId == aRefId || anEdge.EndVertexRefId == aRefId)) + { + markModified(anEdgeId); + } + } + break; + } + case BRepGraph_RefId::Kind::Wire: { + const BRepGraph_WireRefId aRefId(theRefId); + if (!aRefId.IsValid(aStorage.NbWireRefs())) + { + break; + } + const BRepGraph_FaceId aFaceId = aStorage.WireRef(aRefId).ParentFaceId; + if (aFaceId.IsValid(aStorage.NbFaces()) && !aFaceId.IsRemoved(*this)) + { + markModified(aFaceId); + } + break; + } + case BRepGraph_RefId::Kind::Face: { + const BRepGraph_FaceRefId aRefId(theRefId); + if (!aRefId.IsValid(aStorage.NbFaceRefs())) + { + break; + } + const BRepGraph_ShellId aShellId = aStorage.FaceRef(aRefId).ParentShellId; + if (aShellId.IsValid(aStorage.NbShells()) && !aShellId.IsRemoved(*this)) + { + markModified(aShellId); + } + break; + } + case BRepGraph_RefId::Kind::Shell: { + const BRepGraph_ShellRefId aRefId(theRefId); + if (!aRefId.IsValid(aStorage.NbShellRefs())) + { + break; + } + const BRepGraph_SolidId aSolidId = aStorage.ShellRef(aRefId).ParentSolidId; + if (aSolidId.IsValid(aStorage.NbSolids()) && !aSolidId.IsRemoved(*this)) + { + markModified(aSolidId); + } + break; + } + case BRepGraph_RefId::Kind::Solid: { + const BRepGraph_SolidRefId aRefId(theRefId); + if (!aRefId.IsValid(aStorage.NbSolidRefs())) + { + break; + } + const BRepGraph_CompSolidId aCompSolidId = aStorage.SolidRef(aRefId).ParentCompSolidId; + if (aCompSolidId.IsValid(aStorage.NbCompSolids()) && !aCompSolidId.IsRemoved(*this)) + { + markModified(aCompSolidId); + } + break; + } + case BRepGraph_RefId::Kind::Child: { + const BRepGraph_ChildRefId aRefId(theRefId); + if (!aRefId.IsValid(aStorage.NbChildRefs())) + { + break; + } + const BRepGraph_CompoundId aCompoundId = aStorage.ChildRef(aRefId).ParentCompoundId; + if (aCompoundId.IsValid(aStorage.NbCompounds()) && !aCompoundId.IsRemoved(*this)) + { + markModified(aCompoundId); + } + break; + } + case BRepGraph_RefId::Kind::Occurrence: { + const BRepGraph_OccurrenceRefId aRefId(theRefId); + if (!aRefId.IsValid(aStorage.NbOccurrenceRefs())) + { + break; + } + const BRepGraph_ProductId aProductId = aStorage.OccurrenceRef(aRefId).ParentProductId; + if (aProductId.IsValid(aStorage.NbProducts()) && !aProductId.IsRemoved(*this)) + { + markModified(aProductId); + } + break; + } + default: + break; } - - markParentSubtreeGen(theRef.ParentId); } //================================================================================================= @@ -665,7 +843,7 @@ void BRepGraph::markParentSubtreeGen(const BRepGraph_NodeId theParentId) noexcep return; } - const uint32_t aWave = myData->myPropagationWave.load(std::memory_order_relaxed); + const uint32_t aWave = myData->myIncStorage.PropagationWave(); // Re-visit guard: skip if this parent was already processed in the current // propagation wave. Prevents exponential blowup on diamond topologies. @@ -684,18 +862,39 @@ void BRepGraph::markParentSubtreeGen(const BRepGraph_NodeId theParentId) noexcep void BRepGraph::propagateSubtreeGen(const BRepGraph_NodeId theNodeId) noexcept { - const BRepGraphInc_ReverseIndex& aRevIdx = myData->myIncStorage.ReverseIndex(); + const BRepGraphInc_Storage& aStorage = myData->myIncStorage; switch (theNodeId.NodeKind) { - case BRepGraph_NodeId::Kind::Vertex: - // Vertex modifications don't propagate. - break; - case BRepGraph_NodeId::Kind::Edge: { - const NCollection_DynamicArray* aWires = - aRevIdx.WiresOfEdge(BRepGraph_EdgeId(theNodeId)); - if (aWires != nullptr) + case BRepGraph_NodeId::Kind::Vertex: { + for (const BRepGraph_EdgeId& anEdgeId : + aStorage.VertexRelations(BRepGraph_VertexId(theNodeId)).EdgeIds) { - for (const BRepGraph_WireId& aWireId : *aWires) + markParentSubtreeGen(anEdgeId); + } + break; + } + case BRepGraph_NodeId::Kind::Edge: { + for (const BRepGraph_CoEdgeId& aCoEdgeId : + aStorage.EdgeRelations(BRepGraph_EdgeId(theNodeId)).CoEdgeIds) + { + if (!aCoEdgeId.IsValid(aStorage.NbCoEdges()) || aStorage.IsRemoved(aCoEdgeId)) + { + continue; + } + const BRepGraph_WireId aWireId = aStorage.CoEdge(aCoEdgeId).ParentWireId; + if (aWireId.IsValid(aStorage.NbWires()) && !aStorage.IsRemoved(aWireId)) + { + markParentSubtreeGen(aWireId); + } + } + break; + } + case BRepGraph_NodeId::Kind::CoEdge: { + const BRepGraph_CoEdgeId aCoEdgeId(theNodeId); + if (aCoEdgeId.IsValid(aStorage.NbCoEdges()) && !aStorage.IsRemoved(aCoEdgeId)) + { + const BRepGraph_WireId aWireId = aStorage.CoEdge(aCoEdgeId).ParentWireId; + if (aWireId.IsValid(aStorage.NbWires())) { markParentSubtreeGen(aWireId); } @@ -703,67 +902,74 @@ void BRepGraph::propagateSubtreeGen(const BRepGraph_NodeId theNodeId) noexcept break; } case BRepGraph_NodeId::Kind::Wire: { - const NCollection_DynamicArray* aFaces = - aRevIdx.FacesOfWire(BRepGraph_WireId(theNodeId)); - if (aFaces != nullptr) + for (const BRepGraph_WireRefId& aRefId : + aStorage.WireRelations(BRepGraph_WireId(theNodeId)).ParentWireRefIds) { - for (const BRepGraph_FaceId& aFaceId : *aFaces) + if (aRefId.IsValid(aStorage.NbWireRefs()) && !aStorage.IsRemoved(aRefId)) { - markParentSubtreeGen(aFaceId); + markParentSubtreeGen(aStorage.WireRef(aRefId).ParentFaceId); } } break; } case BRepGraph_NodeId::Kind::Face: { - const NCollection_DynamicArray* aShells = - aRevIdx.ShellsOfFace(BRepGraph_FaceId(theNodeId)); - if (aShells != nullptr) + for (const BRepGraph_FaceRefId& aRefId : + aStorage.FaceRelations(BRepGraph_FaceId(theNodeId)).ParentFaceRefIds) { - for (const BRepGraph_ShellId& aShellId : *aShells) + if (aRefId.IsValid(aStorage.NbFaceRefs()) && !aStorage.IsRemoved(aRefId)) { - markParentSubtreeGen(aShellId); + markParentSubtreeGen(aStorage.FaceRef(aRefId).ParentShellId); } } break; } case BRepGraph_NodeId::Kind::Shell: { - const NCollection_DynamicArray* aSolids = - aRevIdx.SolidsOfShell(BRepGraph_ShellId(theNodeId)); - if (aSolids != nullptr) + for (const BRepGraph_ShellRefId& aRefId : + aStorage.ShellRelations(BRepGraph_ShellId(theNodeId)).ParentShellRefIds) { - for (const BRepGraph_SolidId& aSolidId : *aSolids) + if (aRefId.IsValid(aStorage.NbShellRefs()) && !aStorage.IsRemoved(aRefId)) { - markParentSubtreeGen(aSolidId); + markParentSubtreeGen(aStorage.ShellRef(aRefId).ParentSolidId); } } break; } case BRepGraph_NodeId::Kind::Occurrence: { - // Occurrence modifications propagate to the parent product. - // Parent product is on the OccurrenceRef that links to this OccurrenceDef. const BRepGraph_OccurrenceId aThisOccId(theNodeId); - for (BRepGraph_OccurrenceRefIterator aRefIt(*this); aRefIt.More(); aRefIt.Next()) + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.OccurrenceRelations(aThisOccId).ParentOccurrenceRefIds) { - const BRepGraphInc::OccurrenceRef& aRef = aRefIt.Current(); - if (aRef.OccurrenceDefId == aThisOccId) + if (!aRefId.IsValid(aStorage.NbOccurrenceRefs()) || aStorage.IsRemoved(aRefId)) { - markParentSubtreeGen(BRepGraph_ProductId::FromNodeId(aRef.ParentId)); - break; + continue; + } + const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); + if (aRef.ParentProductId.IsValid(aStorage.NbProducts()) + && !aStorage.IsRemoved(aRef.ParentProductId)) + { + markParentSubtreeGen(aRef.ParentProductId); } } break; } default: { // Solid/Compound/CompSolid/Product: propagate to parent occurrences - // that reference this node as ChildDefId. + // that reference this node as ChildNodeId. if (BRepGraph_NodeId::IsTopologyKind(theNodeId.NodeKind) || theNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) { - for (BRepGraph_OccurrenceIterator anOccIt(*this); anOccIt.More(); anOccIt.Next()) + for (const BRepGraph_OccurrenceRefId& aRefId : aStorage.OccurrenceRefsOfNode(theNodeId)) { - if (anOccIt.Current().ChildDefId == theNodeId) + if (!aRefId.IsValid(aStorage.NbOccurrenceRefs()) || aStorage.IsRemoved(aRefId)) { - markParentSubtreeGen(anOccIt.CurrentId()); + continue; + } + const BRepGraph_OccurrenceId anOccurrenceId = + aStorage.OccurrenceRef(aRefId).ChildOccurrenceId; + if (anOccurrenceId.IsValid(aStorage.NbOccurrences()) + && !aStorage.IsRemoved(anOccurrenceId)) + { + markParentSubtreeGen(anOccurrenceId); } } } @@ -774,274 +980,37 @@ void BRepGraph::propagateSubtreeGen(const BRepGraph_NodeId theNodeId) noexcept //================================================================================================= -void BRepGraph::markRepModified(const BRepGraph_RepId theRepId) noexcept -{ - if (!theRepId.IsValid()) - { - return; - } - - BRepGraphInc_Storage& aStorage = myData->myIncStorage; - - // Increment OwnGen on the representation. - switch (theRepId.RepKind) - { - case BRepGraph_RepId::Kind::Surface: { - const BRepGraph_SurfaceRepId aRepId(theRepId); - if (aRepId.IsValid(aStorage.NbSurfaces())) - { - ++aStorage.ChangeSurfaceRep(aRepId).OwnGen; - } - break; - } - case BRepGraph_RepId::Kind::Curve3D: { - const BRepGraph_Curve3DRepId aRepId(theRepId); - if (aRepId.IsValid(aStorage.NbCurves3D())) - { - ++aStorage.ChangeCurve3DRep(aRepId).OwnGen; - } - break; - } - case BRepGraph_RepId::Kind::Curve2D: { - const BRepGraph_Curve2DRepId aRepId(theRepId); - if (aRepId.IsValid(aStorage.NbCurves2D())) - { - ++aStorage.ChangeCurve2DRep(aRepId).OwnGen; - } - break; - } - case BRepGraph_RepId::Kind::Triangulation: { - const BRepGraph_TriangulationRepId aRepId(theRepId); - if (aRepId.IsValid(aStorage.NbTriangulations())) - { - ++aStorage.ChangeTriangulationRep(aRepId).OwnGen; - } - break; - } - case BRepGraph_RepId::Kind::Polygon3D: { - const BRepGraph_Polygon3DRepId aRepId(theRepId); - if (aRepId.IsValid(aStorage.NbPolygons3D())) - { - ++aStorage.ChangePolygon3DRep(aRepId).OwnGen; - } - break; - } - case BRepGraph_RepId::Kind::Polygon2D: { - const BRepGraph_Polygon2DRepId aRepId(theRepId); - if (aRepId.IsValid(aStorage.NbPolygons2D())) - { - ++aStorage.ChangePolygon2DRep(aRepId).OwnGen; - } - break; - } - case BRepGraph_RepId::Kind::PolygonOnTri: { - const BRepGraph_PolygonOnTriRepId aRepId(theRepId); - if (aRepId.IsValid(aStorage.NbPolygonsOnTri())) - { - ++aStorage.ChangePolygonOnTriRep(aRepId).OwnGen; - } - break; - } - default: - return; - } - - // Propagate mutation to owning topology nodes. - switch (theRepId.RepKind) - { - case BRepGraph_RepId::Kind::Surface: { - const BRepGraph_SurfaceRepId aSurfaceRepId(theRepId); - for (BRepGraph_FaceIterator aFaceIt(*this); aFaceIt.More(); aFaceIt.Next()) - { - if (aFaceIt.Current().SurfaceRepId == aSurfaceRepId) - { - markModified(aFaceIt.CurrentId()); - } - } - break; - } - case BRepGraph_RepId::Kind::Curve3D: { - const BRepGraph_Curve3DRepId aCurve3DRepId(theRepId); - for (BRepGraph_EdgeIterator anEdgeIt(*this); anEdgeIt.More(); anEdgeIt.Next()) - { - if (anEdgeIt.Current().Curve3DRepId == aCurve3DRepId) - { - markModified(anEdgeIt.CurrentId()); - } - } - break; - } - case BRepGraph_RepId::Kind::Curve2D: { - const BRepGraph_Curve2DRepId aCurve2DRepId(theRepId); - for (BRepGraph_CoEdgeIterator aCoEdgeIt(*this); aCoEdgeIt.More(); aCoEdgeIt.Next()) - { - if (aCoEdgeIt.Current().Curve2DRepId == aCurve2DRepId) - { - markModified(aCoEdgeIt.CurrentId()); - } - } - break; - } - case BRepGraph_RepId::Kind::Triangulation: { - const BRepGraph_TriangulationRepId aTriangulationRepId(theRepId); - for (BRepGraph_FaceIterator aFaceIt(*this); aFaceIt.More(); aFaceIt.Next()) - { - const BRepGraphInc::FaceDef& aFace = aFaceIt.Current(); - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - bool aFound = aFace.TriangulationRepId == aTriangulationRepId; - if (!aFound) - { - const BRepGraph_MeshCache::FaceMeshEntry* aCached = - myData->myMeshCache.FindFaceMesh(aFaceId); - if (aCached != nullptr) - { - for (NCollection_DynamicArray::Iterator aTriIt( - aCached->TriangulationRepIds); - aTriIt.More(); - aTriIt.Next()) - { - if (aTriIt.Value() == aTriangulationRepId) - { - aFound = true; - break; - } - } - } - } - if (aFound) - { - markModified(aFaceId); - } - } - break; - } - case BRepGraph_RepId::Kind::Polygon3D: { - const BRepGraph_Polygon3DRepId aPolygon3DRepId(theRepId); - for (BRepGraph_EdgeIterator anEdgeIt(*this); anEdgeIt.More(); anEdgeIt.Next()) - { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - bool aFound = anEdgeIt.Current().Polygon3DRepId == aPolygon3DRepId; - if (!aFound) - { - const BRepGraph_MeshCache::EdgeMeshEntry* aCached = - myData->myMeshCache.FindEdgeMesh(anEdgeId); - if (aCached != nullptr && aCached->Polygon3DRepId == aPolygon3DRepId) - { - aFound = true; - } - } - if (aFound) - { - markModified(anEdgeId); - } - } - break; - } - case BRepGraph_RepId::Kind::Polygon2D: { - const BRepGraph_Polygon2DRepId aPolygon2DRepId(theRepId); - for (BRepGraph_CoEdgeIterator aCoEdgeIt(*this); aCoEdgeIt.More(); aCoEdgeIt.Next()) - { - const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); - bool aFound = aCoEdgeIt.Current().Polygon2DRepId == aPolygon2DRepId; - if (!aFound) - { - const BRepGraph_MeshCache::CoEdgeMeshEntry* aCached = - myData->myMeshCache.FindCoEdgeMesh(aCoEdgeId); - if (aCached != nullptr && aCached->Polygon2DRepId == aPolygon2DRepId) - { - aFound = true; - } - } - if (aFound) - { - markModified(aCoEdgeId); - } - } - break; - } - case BRepGraph_RepId::Kind::PolygonOnTri: { - const BRepGraph_PolygonOnTriRepId aPolygonOnTriRepId(theRepId); - for (BRepGraph_CoEdgeIterator aCoEdgeIt(*this); aCoEdgeIt.More(); aCoEdgeIt.Next()) - { - const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); - const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Current(); - bool aFound = aCoEdge.PolygonOnTriRepId == aPolygonOnTriRepId; - if (!aFound) - { - const BRepGraph_MeshCache::CoEdgeMeshEntry* aCached = - myData->myMeshCache.FindCoEdgeMesh(aCoEdgeId); - if (aCached != nullptr) - { - for (NCollection_DynamicArray::Iterator aPolyIt( - aCached->PolygonOnTriRepIds); - aPolyIt.More(); - aPolyIt.Next()) - { - if (aPolyIt.Value() == aPolygonOnTriRepId) - { - aFound = true; - break; - } - } - } - } - if (aFound) - { - markModified(aCoEdgeId); - } - } - break; - } - default: - break; - } -} - -//================================================================================================= - -void BRepGraph::SetAllocator(const occ::handle& theAlloc) -{ - Standard_ASSERT_VOID(!myData->myIsDone, - "SetAllocator: must be called before BRepGraph_Builder::Add() - " - "existing graph state will be lost"); - - myData->myAllocator = - !theAlloc.IsNull() ? theAlloc : NCollection_BaseAllocator::CommonBaseAllocator(); - - // Recreate the entire data object with the new allocator. - myData = std::make_unique(myData->myAllocator); - initViews(); -} - const occ::handle& BRepGraph::Allocator() const { - return myData->myAllocator; -} - -BRepGraph_History& BRepGraph::History() -{ - return myData->myHistoryLog; -} - -//================================================================================================= - -const BRepGraph_History& BRepGraph::History() const -{ - return myData->myHistoryLog; + return myData->myIncStorage.Allocator(); } //================================================================================================= BRepGraph_LayerRegistry& BRepGraph::LayerRegistry() { - return myLayerRegistry; + return myData->myLayerRegistry; } //================================================================================================= const BRepGraph_LayerRegistry& BRepGraph::LayerRegistry() const { - return myLayerRegistry; + return myData->myLayerRegistry; +} + +//================================================================================================= + +BRepGraph_CacheRegistry& BRepGraph::CacheRegistry() +{ + return myData->myCacheRegistry; +} + +//================================================================================================= + +const BRepGraph_CacheRegistry& BRepGraph::CacheRegistry() const +{ + return myData->myCacheRegistry; } //================================================================================================= @@ -1076,56 +1045,46 @@ const BRepGraph_Data* BRepGraph::data() const BRepGraph_LayerRegistry& BRepGraph::layerRegistry() { - return myLayerRegistry; + return myData->myLayerRegistry; } //================================================================================================= const BRepGraph_LayerRegistry& BRepGraph::layerRegistry() const { - return myLayerRegistry; + return myData->myLayerRegistry; } //================================================================================================= -BRepGraph_TransientCache& BRepGraph::transientCache() +BRepGraph_CacheRegistry& BRepGraph::cacheRegistry() { - return myTransientCache; + return myData->myCacheRegistry; } //================================================================================================= -const BRepGraph_TransientCache& BRepGraph::transientCache() const +const BRepGraph_CacheRegistry& BRepGraph::cacheRegistry() const { - return myTransientCache; + return myData->myCacheRegistry; } //================================================================================================= -BRepGraph_RefTransientCache& BRepGraph::refTransientCache() +void BRepGraph::initViewsAndRegistries() noexcept { - return myRefTransientCache; -} - -//================================================================================================= - -const BRepGraph_RefTransientCache& BRepGraph::refTransientCache() const -{ - return myRefTransientCache; -} - -//================================================================================================= - -BRepGraph_MeshCacheStorage& BRepGraph::meshCache() -{ - return myData->myMeshCache; -} - -//================================================================================================= - -const BRepGraph_MeshCacheStorage& BRepGraph::meshCache() const -{ - return myData->myMeshCache; + if (myData == nullptr) + { + return; + } + myData->myTopoView = TopoView(this); + myData->myUIDsView = UIDsView(this); + myData->myRefsView = RefsView(this); + myData->myShapesView = ShapesView(this); + myData->myEditorView = EditorView(this); + myData->myMeshView = MeshView(this); + myData->myLayerRegistry.Attach(this); + myData->myCacheRegistry.Attach(this); } //================================================================================================= @@ -1151,10 +1110,6 @@ const BRepGraphInc::BaseRef* BRepGraph::refEntity(const BRepGraph_RefId theId) c const BRepGraph_WireRefId anId(theId); return anId.IsValid(aStorage.NbWireRefs()) ? &aStorage.WireRef(anId) : nullptr; } - case BRepGraph_RefId::Kind::CoEdge: { - const BRepGraph_CoEdgeRefId anId(theId); - return anId.IsValid(aStorage.NbCoEdgeRefs()) ? &aStorage.CoEdgeRef(anId) : nullptr; - } case BRepGraph_RefId::Kind::Vertex: { const BRepGraph_VertexRefId anId(theId); return anId.IsValid(aStorage.NbVertexRefs()) ? &aStorage.VertexRef(anId) : nullptr; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx index 1d728dc027..6df93d74fa 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph.hxx @@ -17,24 +17,19 @@ #include #include #include -#include +#include #include +#include #include #include #include -#include -#include -#include -#include - #include #include #include #include #include - -#include #include +#include #include @@ -43,19 +38,23 @@ class BRepGraph_MutGuard; struct BRepGraph_Data; class BRepGraphInc_Storage; +class BRepGraph_CacheRegistry; class BRepGraph_Layer; -class BRepGraph_MeshCacheStorage; +class BRepGraph_LayerLock; +class BRepGraph_LayerRegistry; +class BRepGraph_CacheMesh; +class BRepGraph_Validate; +class BRepGraph_Deduplicate; +class BRepGraphODE; +class BRepGraphODE_Storage; class NCollection_BaseAllocator; class TCollection_AsciiString; -class BRepGraph_Builder; -class BRepGraph_History; - //! @brief Topology-geometry graph over TopoDS / BRep. //! //! Stores B-Rep topology as flat entity vectors (incidence-table model) with -//! integer cross-references, enabling cache-friendly traversal, O(1) upward -//! navigation via reverse indices, and parallel face-level geometry extraction. +//! integer cross-references, enabling cache-friendly traversal, relation-table +//! parent navigation, and parallel face-level geometry extraction. //! //! Key design concepts: //! - **NodeId** (Kind + Index): lightweight typed address into per-kind vectors. @@ -64,7 +63,7 @@ class BRepGraph_History; //! Curve3D, Curve2D, Triangulation, Polygon) decoupled from topology nodes. //! - **CoEdge**: half-edge entity owning PCurve data for each edge-face binding; //! seam edges use paired CoEdges with opposite Orientation (Parasolid convention). -//! - **Lifecycle**: BRepGraph_Builder::Add() populates from TopoDS_Shape; +//! - **Lifecycle**: Shapes().Add() populates from TopoDS_Shape; //! Editor() is the single mutation entry point for both structural creation/removal //! (Add*, Remove*, Append*) and field-level RAII-scoped mutation (Mut*()) with //! automatic cache invalidation and upward SubtreeGen propagation. @@ -82,7 +81,7 @@ class BRepGraph_History; //! Deferred invalidation (BRepGraph_DeferredScope) batches SubtreeGen propagation; //! concurrent Editor().Mut*() calls during deferred mode still require external //! serialization. -//! BRepGraph_Builder::Add() is internally parallel when requested. +//! Shapes().Add() is internally parallel when requested. //! //! ## UID persistence //! UIDs use monotonic counters (not vector indices), persisting across Compact() @@ -90,8 +89,9 @@ class BRepGraph_History; //! See BRepGraph_UID.hxx for the serialization contract. //! //! ## Extension model -//! Extend via BRepGraph_Layer (per-node attributes) or BRepGraph_TransientCache -//! (algorithm-computed caches). Direct storage extension is not supported. +//! Extend via BRepGraph_Layer (persistent metadata / observers) or +//! BRepGraph_CacheRegistry (typed algorithm-computed transient cache services). +//! Direct storage extension is not supported. //! //! ## ID systems //! Four ID types with different stability guarantees: @@ -101,38 +101,35 @@ class BRepGraph_History; //! Use for cross-session storage, history tracking, and external references. //! - **RefId** (Kind + per-kind Index): same stability as NodeId, but addresses reference entries //! (Shell->Solid binding, Face->Shell binding, CoEdge->Wire binding) rather than defs. -//! - **RepId** (Kind + per-kind Index): addresses geometry/mesh representation objects (Surface, -//! Curve3D, Curve2D, Triangulation, Polygon) independently of topology nodes. +//! - **RepId** (Kind + per-kind Index): addresses owner-scoped geometry/mesh representation slots +//! (Surface, Curve3D, Curve2D, Triangulation, Polygon). //! //! ## Iterator guide //! Choose the iterator that matches your traversal need: //! - **BRepGraph_Iterator\**: flat sequential scan of ALL definitions of one kind //! (e.g. every FaceDef, skipping removed). Use for bulk per-kind algorithms. //! - **BRepGraph_DefsIterator / BRepGraph_RefsIterator**: single-level typed children of one -//! parent (e.g. active shells of one solid, coedge refs of one wire). Zero allocation. +//! parent (e.g. active shells of one solid, coedges of one wire). Zero allocation. //! Use when you have a specific parent and need its direct children. //! - **BRepGraph_ChildExplorer**: depth-first downward walk from a root with accumulated //! location/orientation per step. Use when visiting descendants across multiple levels or //! when the global transform matters. Supports Recursive and DirectChildren modes. -//! - **BRepGraph_ParentExplorer**: upward walk via reverse indices from a starting node. +//! - **BRepGraph_ParentExplorer**: upward walk via relation tables from a starting node. //! Use when tracing which shells/solids/compounds contain a given face or edge. //! - **BRepGraph_RelatedIterator**: single-level semantic neighbors (adjacent faces, boundary //! edges, incident vertices). No structural descent; no location accumulation. -//! - **BRepGraph_WireExplorer**: ordered edge traversal within a single wire, following -//! connectivity order (graph equivalent of BRepTools_WireExplorer). class BRepGraph { public: DEFINE_STANDARD_ALLOC - BRepGraph(const BRepGraph&) = delete; + //! Copying is intentionally disabled: BRepGraph is the unique owner of graph data. + BRepGraph(const BRepGraph&) = delete; + //! Copying is intentionally disabled: BRepGraph is the unique owner of graph data. BRepGraph& operator=(const BRepGraph&) = delete; //! Default constructor. Creates an empty graph with default allocator. Standard_EXPORT BRepGraph(); - //! Construct with a custom allocator for internal collections. - //! @param[in] theAlloc allocator for internal collections (null uses CommonBaseAllocator) - Standard_EXPORT explicit BRepGraph(const occ::handle& theAlloc); //! Destructor. Standard_EXPORT ~BRepGraph(); //! Move constructor. @@ -143,33 +140,31 @@ public: //! Reset the graph to an empty state. Increments generation and regenerates the graph GUID. Standard_EXPORT void Clear(); - //! Return true if the graph was successfully built. - [[nodiscard]] Standard_EXPORT bool IsDone() const; + //! Return true when the graph contains no topology definitions. + [[nodiscard]] Standard_EXPORT bool IsEmpty() const; - //! Verify reverse-index consistency against forward entity / reference-entry tables. + //! Verify relation consistency against entity / reference-entry tables. //! Intended for debug builds and regression tests of incremental mutation paths. - //! @return true when every forward ref has a matching reverse entry. - [[nodiscard]] Standard_EXPORT bool ValidateReverseIndex() const; + //! @return true when every stored relation matches its endpoints. + [[nodiscard]] Standard_EXPORT bool ValidateRelations() const; //! Return root product identifiers (products not referenced by any active occurrence). //! Maintained incrementally by Editor/EditorView mutations. //! Returns empty vector if the graph has not been built. - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& RootProductIds() const; - //! Replace the internal allocator and re-create all storage. - Standard_EXPORT void SetAllocator(const occ::handle& theAlloc); - //! Return the current allocator. [[nodiscard]] Standard_EXPORT const occ::handle& Allocator() const; -public: - //! Shared cache for edge/vertex shapes during multi-face reconstruction. - using ReconstructCache = NCollection_DataMap; + //! Return true when this wrapper references graph data. + [[nodiscard]] Standard_EXPORT bool IsValid() const noexcept; + + //! Return true when this wrapper does not reference graph data. + [[nodiscard]] bool IsNull() const noexcept { return !IsValid(); } class TopoView; class UIDsView; - class CacheView; class RefsView; class ShapesView; class EditorView; @@ -180,15 +175,11 @@ public: [[nodiscard]] Standard_EXPORT const TopoView& Topo() const; //! Access unique identifiers. [[nodiscard]] Standard_EXPORT const UIDsView& UIDs() const; - //! Access transient cache values through the stable grouped-view API. - //! This is the only public cache interface. - [[nodiscard]] Standard_EXPORT CacheView& Cache(); - //! Access transient cache values (const, read-only Get/Has/CacheKinds). - //! This is the only public cache interface. - [[nodiscard]] Standard_EXPORT const CacheView& Cache() const; //! Access reference entries and their UIDs. [[nodiscard]] Standard_EXPORT const RefsView& Refs() const; //! Access cached and fresh shape reconstruction. + [[nodiscard]] Standard_EXPORT ShapesView& Shapes(); + //! Access shape ingestion, cached shape reconstruction and fresh shape reconstruction. [[nodiscard]] Standard_EXPORT const ShapesView& Shapes() const; //! Access programmatic graph construction and mutation. [[nodiscard]] Standard_EXPORT EditorView& Editor(); @@ -196,19 +187,14 @@ public: //! Exposes IsDeferredMode() and ValidateMutationBoundary() on a const graph. //! All structural mutation methods require the non-const Editor() overload. [[nodiscard]] Standard_EXPORT const EditorView& Editor() const; - //! Access mesh data with cache-first, persistent-fallback priority. - //! For mesh cache writes and rep creation, use BRepGraph_Tool::Mesh. + //! Access mesh data with explicit Cache()/Persistent() sub-views and Editor() for cache + //! mutations. Persistent rep creation lives on Editor().Edges(), Editor().CoEdges(), + //! Editor().Faces() (since reps back the topology defs). + //! @return read-only mesh view [[nodiscard]] Standard_EXPORT const MeshView& Mesh() const; - - //! Access history subsystem directly. - //! History is returned directly rather than through a lightweight view - //! because it is already a self-contained query and recording subsystem - //! with no per-view cached state. - //! @return history subsystem for tracking modifications - [[nodiscard]] Standard_EXPORT BRepGraph_History& History(); - //! Access history subsystem directly (const). - //! @return history subsystem for tracking modifications - [[nodiscard]] Standard_EXPORT const BRepGraph_History& History() const; + //! Non-const access to mesh view (required to call Editor() sub-view for cache mutations). + //! @return mutable mesh view + [[nodiscard]] Standard_EXPORT MeshView& Mesh(); //! Access registered graph layers. //! @return layer registry for managing attribute layers @@ -217,16 +203,35 @@ public: //! @return layer registry for managing attribute layers [[nodiscard]] Standard_EXPORT const BRepGraph_LayerRegistry& LayerRegistry() const; + //! Access registered graph cache services. + //! @return cache registry for managing typed transient cache services + [[nodiscard]] Standard_EXPORT BRepGraph_CacheRegistry& CacheRegistry(); + //! Access registered graph cache services (const). + //! @return cache registry for managing typed transient cache services + [[nodiscard]] Standard_EXPORT const BRepGraph_CacheRegistry& CacheRegistry() const; + private: - friend class BRepGraph_Builder; + friend class BRepGraph_Cache; + friend class BRepGraph_CacheRegistry; friend class BRepGraph_Compact; friend class BRepGraph_Copy; + friend class BRepGraph_Deduplicate; + friend class BRepGraph_Layer; + friend class BRepGraph_LayerLock; + friend class BRepGraph_LayerRegistry; friend class BRepGraph_Tool; friend class BRepGraph_Transform; + friend class BRepGraph_Validate; + friend class BRepGraphInc_Populate; + friend class BRepGraphInc_Reconstruct; + friend class BRepGraphODE; + friend class BRepGraphODE_Storage; template friend class BRepGraph_MutGuard; - //! @{ + friend struct BRepGraph_NodeId; + friend struct BRepGraph_RefId; + friend struct BRepGraph_RepId; //! Access the underlying storage. [[nodiscard]] Standard_EXPORT BRepGraphInc_Storage& incStorage(); @@ -236,70 +241,63 @@ private: [[nodiscard]] Standard_EXPORT BRepGraph_Data* data(); [[nodiscard]] Standard_EXPORT const BRepGraph_Data* data() const; + //! Bind graph-owned views and registries to this owner. + Standard_EXPORT void initViewsAndRegistries() noexcept; + //! Access the layer registry. [[nodiscard]] Standard_EXPORT BRepGraph_LayerRegistry& layerRegistry(); [[nodiscard]] Standard_EXPORT const BRepGraph_LayerRegistry& layerRegistry() const; - //! Access the raw transient cache for friend algorithms and builders. - [[nodiscard]] Standard_EXPORT BRepGraph_TransientCache& transientCache(); - [[nodiscard]] Standard_EXPORT const BRepGraph_TransientCache& transientCache() const; - - //! Access the raw reference transient cache. - [[nodiscard]] Standard_EXPORT BRepGraph_RefTransientCache& refTransientCache(); - [[nodiscard]] Standard_EXPORT const BRepGraph_RefTransientCache& refTransientCache() const; - - //! Access the mesh cache storage. - [[nodiscard]] Standard_EXPORT BRepGraph_MeshCacheStorage& meshCache(); - [[nodiscard]] Standard_EXPORT const BRepGraph_MeshCacheStorage& meshCache() const; + //! Access the cache registry. + [[nodiscard]] Standard_EXPORT BRepGraph_CacheRegistry& cacheRegistry(); + [[nodiscard]] Standard_EXPORT const BRepGraph_CacheRegistry& cacheRegistry() const; //! Generic reference lookup by RefId (const). //! Returns nullptr if the RefId is invalid or out of range. Standard_EXPORT const BRepGraphInc::BaseRef* refEntity(const BRepGraph_RefId theId) const; - //! @} + //! Invalidate reconstructed shapes and dependent caches below a node. + //! @param[in] theNode root node of the invalidated subgraph + Standard_EXPORT void invalidateSubgraphImpl(const BRepGraph_NodeId theNode); - Standard_EXPORT void invalidateSubgraphImpl(const BRepGraph_NodeId theNode); - Standard_EXPORT BRepGraph_UID allocateUID(const BRepGraph_NodeId theNodeId); + //! Allocate and attach a persistent definition UID for a freshly appended node. + //! @param[in] theNodeId node slot receiving the UID + //! @return allocated definition UID + Standard_EXPORT BRepGraph_UID allocateUID(const BRepGraph_NodeId theNodeId); + + //! Allocate and attach a persistent reference UID for a freshly appended reference. + //! @param[in] theRefId reference slot receiving the UID + //! @return allocated reference UID Standard_EXPORT BRepGraph_RefUID allocateRefUID(const BRepGraph_RefId theRefId); + //! Mark a topology definition as modified and propagate cache invalidation. + //! @param[in] theNodeId modified topology definition Standard_EXPORT void markModified(const BRepGraph_NodeId theNodeId) noexcept; + + //! Mark a reference entry as modified and propagate cache invalidation. + //! @param[in] theRefId modified reference entry Standard_EXPORT void markRefModified(const BRepGraph_RefId theRefId) noexcept; //! Optimized overload: skips changeTopoEntity() dispatch //! when the caller already holds a mutable reference to the target entity. Standard_EXPORT void markModified(const BRepGraph_NodeId theNodeId, BRepGraphInc::BaseDef& theEntity) noexcept; - Standard_EXPORT void markRefModified(const BRepGraph_RefId theRefId, - BRepGraphInc::BaseRef& theRef) noexcept; - //! Increment SubtreeGen on a parent node (NOT OwnGen - parent's own data didn't change). //! Uses wave guard to prevent exponential blowup on diamond topologies. //! Mutex-free: no shape cache clear, no dispatch. Standard_EXPORT void markParentSubtreeGen(const BRepGraph_NodeId theParentId) noexcept; - //! Propagate SubtreeGen upward through reverse indices via markParentSubtreeGen(). + //! Propagate SubtreeGen upward through relation tables via markParentSubtreeGen(). Standard_EXPORT void propagateSubtreeGen(const BRepGraph_NodeId theNodeId) noexcept; - //! Increment OwnGen on a representation and propagate mutation - //! to the owning topology node(s). - Standard_EXPORT void markRepModified(const BRepGraph_RepId theRepId) noexcept; - //! Generic topology definition lookup by NodeId (const). Standard_EXPORT const BRepGraphInc::BaseDef* topoEntity(const BRepGraph_NodeId theId) const; //! Generic mutable topology definition lookup by NodeId. Standard_EXPORT BRepGraphInc::BaseDef* changeTopoEntity(const BRepGraph_NodeId theId); - //! Initialize cached view objects to point to this graph. - Standard_EXPORT void initViews(); - // Fields at the bottom (OCCT style) std::unique_ptr myData; - - //! Registered layers are stored on BRepGraph, not BRepGraph_Data, to survive Compact swap. - BRepGraph_LayerRegistry myLayerRegistry; - BRepGraph_TransientCache myTransientCache; //!< Transient algorithm caches (BndBox, UVBounds) - BRepGraph_RefTransientCache myRefTransientCache; //!< Transient per-reference caches }; // Included after BRepGraph is complete so the template body sees markModified(). diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx deleted file mode 100644 index 166c3ad754..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.cxx +++ /dev/null @@ -1,684 +0,0 @@ -// 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - -//================================================================================================= - -static void assertMutationBoundary(BRepGraph& theGraph, const char* theContext) -{ - (void)theContext; - const bool isValid = theGraph.Editor().ValidateMutationBoundary(); - Standard_ASSERT_VOID(isValid, theContext); - (void)isValid; -} - -} // namespace - -//================================================================================================= - -uint32_t BRepGraph_Builder::snapshotCountForKind(const BRepGraph& theGraph, - const TopAbs_ShapeEnum theShapeType) -{ - const BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage; - switch (theShapeType) - { - case TopAbs_COMPOUND: - return static_cast(aStorage.NbCompounds()); - case TopAbs_COMPSOLID: - return static_cast(aStorage.NbCompSolids()); - case TopAbs_SOLID: - return static_cast(aStorage.NbSolids()); - case TopAbs_SHELL: - return static_cast(aStorage.NbShells()); - case TopAbs_FACE: - return static_cast(aStorage.NbFaces()); - case TopAbs_WIRE: - return static_cast(aStorage.NbWires()); - case TopAbs_EDGE: - return static_cast(aStorage.NbEdges()); - case TopAbs_VERTEX: - return static_cast(aStorage.NbVertices()); - default: - return 0; - } -} - -//================================================================================================= - -BRepGraph_NodeId BRepGraph_Builder::detectTopologyRoot(const BRepGraph& theGraph, - const TopAbs_ShapeEnum theShapeType, - const uint32_t theOldCountOfShapeKind) -{ - const uint32_t aNewCount = snapshotCountForKind(theGraph, theShapeType); - if (aNewCount <= theOldCountOfShapeKind) - { - return BRepGraph_NodeId(); - } - - // BRepGraphInc_Populate::Append appends entities in declaration order, so the first entity - // appended for a given shape type is always the shape root (index == pre-append count). - // This assumption holds as long as no intermediate entities of the same type are inserted - // before the root node during population. - switch (theShapeType) - { - case TopAbs_COMPOUND: - return BRepGraph_CompoundId(theOldCountOfShapeKind); - case TopAbs_COMPSOLID: - return BRepGraph_CompSolidId(theOldCountOfShapeKind); - case TopAbs_SOLID: - return BRepGraph_SolidId(theOldCountOfShapeKind); - case TopAbs_SHELL: - return BRepGraph_ShellId(theOldCountOfShapeKind); - case TopAbs_FACE: - return BRepGraph_FaceId(theOldCountOfShapeKind); - case TopAbs_WIRE: - return BRepGraph_WireId(theOldCountOfShapeKind); - case TopAbs_EDGE: - return BRepGraph_EdgeId(theOldCountOfShapeKind); - case TopAbs_VERTEX: - return BRepGraph_VertexId(theOldCountOfShapeKind); - default: - return BRepGraph_NodeId(); - } -} - -//================================================================================================= - -void BRepGraph_Builder::populateUIDs(BRepGraph& theGraph) -{ - BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage; - - if (!aStorage.GetIsDone()) - { - return; - } - - for (BRepGraph_FullVertexIterator aVertexIt(theGraph); aVertexIt.More(); aVertexIt.Next()) - { - theGraph.allocateUID(aVertexIt.CurrentId()); - } - for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - theGraph.allocateUID(anEdgeIt.CurrentId()); - } - for (BRepGraph_FullCoEdgeIterator aCoEdgeIt(theGraph); aCoEdgeIt.More(); aCoEdgeIt.Next()) - { - theGraph.allocateUID(aCoEdgeIt.CurrentId()); - } - for (BRepGraph_FullWireIterator aWireIt(theGraph); aWireIt.More(); aWireIt.Next()) - { - theGraph.allocateUID(aWireIt.CurrentId()); - } - for (BRepGraph_FullFaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) - { - theGraph.allocateUID(aFaceIt.CurrentId()); - } - for (BRepGraph_FullShellIterator aShellIt(theGraph); aShellIt.More(); aShellIt.Next()) - { - theGraph.allocateUID(aShellIt.CurrentId()); - } - for (BRepGraph_FullSolidIterator aSolidIt(theGraph); aSolidIt.More(); aSolidIt.Next()) - { - theGraph.allocateUID(aSolidIt.CurrentId()); - } - for (BRepGraph_FullCompoundIterator aCompoundIt(theGraph); aCompoundIt.More(); aCompoundIt.Next()) - { - theGraph.allocateUID(aCompoundIt.CurrentId()); - } - for (BRepGraph_FullCompSolidIterator aCompSolidIt(theGraph); aCompSolidIt.More(); - aCompSolidIt.Next()) - { - theGraph.allocateUID(aCompSolidIt.CurrentId()); - } - for (BRepGraph_FullProductIterator aProductIt(theGraph); aProductIt.More(); aProductIt.Next()) - { - theGraph.allocateUID(aProductIt.CurrentId()); - } - for (BRepGraph_FullOccurrenceIterator anOccurrenceIt(theGraph); anOccurrenceIt.More(); - anOccurrenceIt.Next()) - { - theGraph.allocateUID(anOccurrenceIt.CurrentId()); - } - - for (BRepGraph_FullShellRefIterator aShellRefIt(theGraph); aShellRefIt.More(); aShellRefIt.Next()) - { - theGraph.allocateRefUID(aShellRefIt.CurrentId()); - } - for (BRepGraph_FullFaceRefIterator aFaceRefIt(theGraph); aFaceRefIt.More(); aFaceRefIt.Next()) - { - theGraph.allocateRefUID(aFaceRefIt.CurrentId()); - } - for (BRepGraph_FullWireRefIterator aWireRefIt(theGraph); aWireRefIt.More(); aWireRefIt.Next()) - { - theGraph.allocateRefUID(aWireRefIt.CurrentId()); - } - for (BRepGraph_FullCoEdgeRefIterator aCoEdgeRefIt(theGraph); aCoEdgeRefIt.More(); - aCoEdgeRefIt.Next()) - { - theGraph.allocateRefUID(aCoEdgeRefIt.CurrentId()); - } - for (BRepGraph_FullVertexRefIterator aVertexRefIt(theGraph); aVertexRefIt.More(); - aVertexRefIt.Next()) - { - theGraph.allocateRefUID(aVertexRefIt.CurrentId()); - } - for (BRepGraph_FullSolidRefIterator aSolidRefIt(theGraph); aSolidRefIt.More(); aSolidRefIt.Next()) - { - theGraph.allocateRefUID(aSolidRefIt.CurrentId()); - } - for (BRepGraph_FullChildRefIterator aChildRefIt(theGraph); aChildRefIt.More(); aChildRefIt.Next()) - { - theGraph.allocateRefUID(aChildRefIt.CurrentId()); - } -} - -//================================================================================================= - -void BRepGraph_Builder::appendImpl(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const Options& theOptions, - NCollection_DynamicArray* theOutFlatRoots) -{ - BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage; - const int anOldVtx = aStorage.NbVertices(); - const int anOldEdge = aStorage.NbEdges(); - const int anOldCoEdge = aStorage.NbCoEdges(); - const int anOldWire = aStorage.NbWires(); - const int anOldFace = aStorage.NbFaces(); - const int anOldShell = aStorage.NbShells(); - const int anOldSolid = aStorage.NbSolids(); - const int anOldComp = aStorage.NbCompounds(); - const int anOldCS = aStorage.NbCompSolids(); - const int anOldProduct = aStorage.NbProducts(); - const int anOldOccurrence = aStorage.NbOccurrences(); - const int anOldShellRef = aStorage.NbShellRefs(); - const int anOldFaceRef = aStorage.NbFaceRefs(); - const int anOldWireRef = aStorage.NbWireRefs(); - const int anOldCoEdgeRef = aStorage.NbCoEdgeRefs(); - const int anOldVertexRef = aStorage.NbVertexRefs(); - const int anOldSolidRef = aStorage.NbSolidRefs(); - const int anOldChildRef = aStorage.NbChildRefs(); - - occ::handle aTmpAlloc = new NCollection_IncAllocator; - const occ::handle aParamLayer = - theGraph.LayerRegistry().FindLayer(); - const occ::handle aRegularityLayer = - theGraph.LayerRegistry().FindLayer(); - - if (theOptions.Flatten) - { - NCollection_DynamicArray aAppendedRoots(8, theGraph.Allocator()); - BRepGraphInc_Populate::AppendFlattened(aStorage, - theShape, - theOptions.Parallel, - aAppendedRoots, - theOptions.Populate, - aParamLayer, - aRegularityLayer, - aTmpAlloc); - if (theOutFlatRoots != nullptr) - { - for (const BRepGraph_NodeId& anId : aAppendedRoots) - { - theOutFlatRoots->Append(anId); - } - } - } - else - { - BRepGraphInc_Populate::Append(aStorage, - theShape, - theOptions.Parallel, - theOptions.Populate, - aParamLayer, - aRegularityLayer, - aTmpAlloc); - } - - if (!aStorage.GetIsDone()) - { - return; - } - - theGraph.myData->myCurrentShapes.Clear(); - - populateUIDsIncremental(theGraph, - anOldVtx, - anOldEdge, - anOldCoEdge, - anOldWire, - anOldFace, - anOldShell, - anOldSolid, - anOldComp, - anOldCS, - anOldProduct, - anOldOccurrence, - anOldShellRef, - anOldFaceRef, - anOldWireRef, - anOldCoEdgeRef, - anOldVertexRef, - anOldSolidRef, - anOldChildRef); - - theGraph.myData->myIsDone = true; - - assertMutationBoundary(theGraph, "BRepGraph_Builder::Add: post-append mutation boundary"); -} - -//================================================================================================= - -BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph, const TopoDS_Shape& theShape) -{ - return Add(theGraph, theShape, Options{}); -} - -//================================================================================================= - -BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const Options& theOptions) -{ - Result aResult; - if (theShape.IsNull()) - { - return aResult; - } - - const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType()); - - NCollection_DynamicArray aFlatRoots; - appendImpl(theGraph, theShape, theOptions, theOptions.Flatten ? &aFlatRoots : nullptr); - - if (!theGraph.myData->myIncStorage.GetIsDone()) - { - return aResult; - } - - if (theOptions.Flatten && !aFlatRoots.IsEmpty()) - { - aResult.TopologyRoot = aFlatRoots.First(); - } - else - { - aResult.TopologyRoot = detectTopologyRoot(theGraph, theShape.ShapeType(), anOldCount); - } - - if (theOptions.CreateAutoProduct && aResult.TopologyRoot.IsValid()) - { - aResult.Product = - theGraph.Editor().Products().LinkProductToTopology(aResult.TopologyRoot, theShape.Location()); - if (aResult.Product.IsValid()) - { - const BRepGraphInc::ProductDef& aProductDef = - theGraph.myData->myIncStorage.Product(aResult.Product); - if (!aProductDef.OccurrenceRefIds.IsEmpty()) - { - const BRepGraph_OccurrenceRefId anOccRefId = aProductDef.OccurrenceRefIds.First(); - const BRepGraph_OccurrenceId anOccId = - theGraph.myData->myIncStorage.OccurrenceRef(anOccRefId).OccurrenceDefId; - aResult.Occurrence = anOccId; - } - } - } - - // Pre-allocate transient cache for lock-free parallel access. - { - BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage; - int aCounts[BRepGraph_TransientCache::THE_KIND_COUNT] = {}; - aCounts[static_cast(BRepGraph_NodeId::Kind::Vertex)] = aStorage.NbVertices(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Edge)] = aStorage.NbEdges(); - aCounts[static_cast(BRepGraph_NodeId::Kind::CoEdge)] = aStorage.NbCoEdges(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Wire)] = aStorage.NbWires(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Face)] = aStorage.NbFaces(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Shell)] = aStorage.NbShells(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Solid)] = aStorage.NbSolids(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Compound)] = aStorage.NbCompounds(); - aCounts[static_cast(BRepGraph_NodeId::Kind::CompSolid)] = aStorage.NbCompSolids(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Product)] = aStorage.NbProducts(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Occurrence)] = aStorage.NbOccurrences(); - int aReservedKindCount = BRepGraph_TransientCache::THE_DEFAULT_RESERVED_KIND_COUNT; - const int aRegisteredKindCount = BRepGraph_CacheKindRegistry::NbRegistered(); - if (aRegisteredKindCount > aReservedKindCount) - { - aReservedKindCount = aRegisteredKindCount; - } - theGraph.myTransientCache.Reserve(aReservedKindCount, aCounts); - } - - aResult.Ok = - aResult.TopologyRoot.IsValid() || (theOptions.CreateAutoProduct && aResult.Product.IsValid()); - return aResult; -} - -//================================================================================================= - -BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const BRepGraph_NodeId theParent) -{ - return Add(theGraph, theShape, theParent, Options{}); -} - -//================================================================================================= - -BRepGraph_Builder::Result BRepGraph_Builder::Add(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const BRepGraph_NodeId theParent, - const Options& theOptions) -{ - Result aResult; - if (theShape.IsNull() || !theParent.IsValid()) - { - return aResult; - } - - const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType()); - - Options anInner = theOptions; - anInner.CreateAutoProduct = false; - - NCollection_DynamicArray aFlatRoots; - appendImpl(theGraph, theShape, anInner, anInner.Flatten ? &aFlatRoots : nullptr); - - if (!theGraph.myData->myIncStorage.GetIsDone()) - { - return aResult; - } - - if (anInner.Flatten && !aFlatRoots.IsEmpty()) - { - aResult.TopologyRoot = aFlatRoots.First(); - } - else - { - aResult.TopologyRoot = detectTopologyRoot(theGraph, theShape.ShapeType(), anOldCount); - } - if (!aResult.TopologyRoot.IsValid()) - { - return aResult; - } - - switch (theParent.NodeKind) - { - case BRepGraph_NodeId::Kind::Product: { - const BRepGraph_ProductId aChildProduct = - theGraph.Editor().Products().LinkProductToTopology(aResult.TopologyRoot, TopLoc_Location()); - if (!aChildProduct.IsValid()) - { - return aResult; - } - - BRepGraph_OccurrenceRefId anOccRefId; - const BRepGraph_OccurrenceId anOccId = - theGraph.Editor().Products().LinkProducts(BRepGraph_ProductId(theParent), - aChildProduct, - theShape.Location(), - BRepGraph_OccurrenceId(), - &anOccRefId); - if (!anOccId.IsValid()) - { - return aResult; - } - aResult.Product = aChildProduct; - aResult.Occurrence = anOccId; - aResult.InsertedRef = anOccRefId; - aResult.Ok = true; - return aResult; - } - case BRepGraph_NodeId::Kind::Compound: { - const BRepGraph_ChildRefId aRid = - theGraph.Editor().Compounds().AddChild(BRepGraph_CompoundId(theParent), - aResult.TopologyRoot, - theShape.Orientation()); - if (!aRid.IsValid()) - { - return aResult; - } - aResult.InsertedRef = aRid; - aResult.Ok = true; - return aResult; - } - case BRepGraph_NodeId::Kind::Shell: { - const BRepGraph_ShellId aShell(theParent); - if (aResult.TopologyRoot.NodeKind == BRepGraph_NodeId::Kind::Face) - { - const BRepGraph_FaceRefId aRid = - theGraph.Editor().Shells().AddFace(aShell, - BRepGraph_FaceId(aResult.TopologyRoot), - theShape.Orientation()); - if (!aRid.IsValid()) - { - return aResult; - } - aResult.InsertedRef = aRid; - aResult.Ok = true; - return aResult; - } - const BRepGraph_ChildRefId aRid = - theGraph.Editor().Shells().AddChild(aShell, aResult.TopologyRoot, theShape.Orientation()); - if (!aRid.IsValid()) - { - return aResult; - } - aResult.InsertedRef = aRid; - aResult.Ok = true; - return aResult; - } - case BRepGraph_NodeId::Kind::Solid: { - const BRepGraph_SolidId aSolid(theParent); - if (aResult.TopologyRoot.NodeKind == BRepGraph_NodeId::Kind::Shell) - { - const BRepGraph_ShellRefId aRid = - theGraph.Editor().Solids().AddShell(aSolid, - BRepGraph_ShellId(aResult.TopologyRoot), - theShape.Orientation()); - if (!aRid.IsValid()) - { - return aResult; - } - aResult.InsertedRef = aRid; - aResult.Ok = true; - return aResult; - } - const BRepGraph_ChildRefId aRid = - theGraph.Editor().Solids().AddChild(aSolid, aResult.TopologyRoot, theShape.Orientation()); - if (!aRid.IsValid()) - { - return aResult; - } - aResult.InsertedRef = aRid; - aResult.Ok = true; - return aResult; - } - case BRepGraph_NodeId::Kind::CompSolid: { - if (aResult.TopologyRoot.NodeKind != BRepGraph_NodeId::Kind::Solid) - { - return aResult; - } - const BRepGraph_SolidRefId aRid = - theGraph.Editor().CompSolids().AddSolid(BRepGraph_CompSolidId(theParent), - BRepGraph_SolidId(aResult.TopologyRoot), - theShape.Orientation()); - if (!aRid.IsValid()) - { - return aResult; - } - aResult.InsertedRef = aRid; - aResult.Ok = true; - return aResult; - } - default: - return aResult; - } -} - -//================================================================================================= - -void BRepGraph_Builder::populateUIDsIncremental(BRepGraph& theGraph, - const int theOldVtx, - const int theOldEdge, - const int theOldCoEdge, - const int theOldWire, - const int theOldFace, - const int theOldShell, - const int theOldSolid, - const int theOldComp, - const int theOldCS, - const int theOldProduct, - const int theOldOccurrence, - const int theOldShellRef, - const int theOldFaceRef, - const int theOldWireRef, - const int theOldCoEdgeRef, - const int theOldVertexRef, - const int theOldSolidRef, - const int theOldChildRef) -{ - BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage; - - if (!aStorage.GetIsDone()) - { - return; - } - - for (BRepGraph_FullVertexIterator aVertexIt(theGraph, BRepGraph_VertexId(theOldVtx)); - aVertexIt.More(); - aVertexIt.Next()) - { - theGraph.allocateUID(aVertexIt.CurrentId()); - } - for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph, BRepGraph_EdgeId(theOldEdge)); anEdgeIt.More(); - anEdgeIt.Next()) - { - theGraph.allocateUID(anEdgeIt.CurrentId()); - } - for (BRepGraph_FullCoEdgeIterator aCoEdgeIt(theGraph, BRepGraph_CoEdgeId(theOldCoEdge)); - aCoEdgeIt.More(); - aCoEdgeIt.Next()) - { - theGraph.allocateUID(aCoEdgeIt.CurrentId()); - } - for (BRepGraph_FullWireIterator aWireIt(theGraph, BRepGraph_WireId(theOldWire)); aWireIt.More(); - aWireIt.Next()) - { - theGraph.allocateUID(aWireIt.CurrentId()); - } - for (BRepGraph_FullFaceIterator aFaceIt(theGraph, BRepGraph_FaceId(theOldFace)); aFaceIt.More(); - aFaceIt.Next()) - { - theGraph.allocateUID(aFaceIt.CurrentId()); - } - for (BRepGraph_FullShellIterator aShellIt(theGraph, BRepGraph_ShellId(theOldShell)); - aShellIt.More(); - aShellIt.Next()) - { - theGraph.allocateUID(aShellIt.CurrentId()); - } - for (BRepGraph_FullSolidIterator aSolidIt(theGraph, BRepGraph_SolidId(theOldSolid)); - aSolidIt.More(); - aSolidIt.Next()) - { - theGraph.allocateUID(aSolidIt.CurrentId()); - } - for (BRepGraph_FullCompoundIterator aCompoundIt(theGraph, BRepGraph_CompoundId(theOldComp)); - aCompoundIt.More(); - aCompoundIt.Next()) - { - theGraph.allocateUID(aCompoundIt.CurrentId()); - } - for (BRepGraph_FullCompSolidIterator aCompSolidIt(theGraph, BRepGraph_CompSolidId(theOldCS)); - aCompSolidIt.More(); - aCompSolidIt.Next()) - { - theGraph.allocateUID(aCompSolidIt.CurrentId()); - } - for (BRepGraph_FullProductIterator aProductIt(theGraph, BRepGraph_ProductId(theOldProduct)); - aProductIt.More(); - aProductIt.Next()) - { - theGraph.allocateUID(aProductIt.CurrentId()); - } - for (BRepGraph_FullOccurrenceIterator anOccurrenceIt(theGraph, - BRepGraph_OccurrenceId(theOldOccurrence)); - anOccurrenceIt.More(); - anOccurrenceIt.Next()) - { - theGraph.allocateUID(anOccurrenceIt.CurrentId()); - } - - for (BRepGraph_FullShellRefIterator aShellRefIt(theGraph, BRepGraph_ShellRefId(theOldShellRef)); - aShellRefIt.More(); - aShellRefIt.Next()) - { - theGraph.allocateRefUID(aShellRefIt.CurrentId()); - } - for (BRepGraph_FullFaceRefIterator aFaceRefIt(theGraph, BRepGraph_FaceRefId(theOldFaceRef)); - aFaceRefIt.More(); - aFaceRefIt.Next()) - { - theGraph.allocateRefUID(aFaceRefIt.CurrentId()); - } - for (BRepGraph_FullWireRefIterator aWireRefIt(theGraph, BRepGraph_WireRefId(theOldWireRef)); - aWireRefIt.More(); - aWireRefIt.Next()) - { - theGraph.allocateRefUID(aWireRefIt.CurrentId()); - } - for (BRepGraph_FullCoEdgeRefIterator aCoEdgeRefIt(theGraph, - BRepGraph_CoEdgeRefId(theOldCoEdgeRef)); - aCoEdgeRefIt.More(); - aCoEdgeRefIt.Next()) - { - theGraph.allocateRefUID(aCoEdgeRefIt.CurrentId()); - } - for (BRepGraph_FullVertexRefIterator aVertexRefIt(theGraph, - BRepGraph_VertexRefId(theOldVertexRef)); - aVertexRefIt.More(); - aVertexRefIt.Next()) - { - theGraph.allocateRefUID(aVertexRefIt.CurrentId()); - } - for (BRepGraph_FullSolidRefIterator aSolidRefIt(theGraph, BRepGraph_SolidRefId(theOldSolidRef)); - aSolidRefIt.More(); - aSolidRefIt.Next()) - { - theGraph.allocateRefUID(aSolidRefIt.CurrentId()); - } - for (BRepGraph_FullChildRefIterator aChildRefIt(theGraph, BRepGraph_ChildRefId(theOldChildRef)); - aChildRefIt.More(); - aChildRefIt.Next()) - { - theGraph.allocateRefUID(aChildRefIt.CurrentId()); - } -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx deleted file mode 100644 index 523630332f..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Builder.hxx +++ /dev/null @@ -1,134 +0,0 @@ -// 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. - -#ifndef _BRepGraph_Builder_HeaderFile -#define _BRepGraph_Builder_HeaderFile - -#include -#include -#include -#include -#include -#include -#include - -class BRepGraph; -class TopoDS_Shape; - -//! @brief Static helper that ingests a TopoDS_Shape into a BRepGraph. -class BRepGraph_Builder -{ -public: - DEFINE_STANDARD_ALLOC - - //! Build-time options. - struct Options - { - BRepGraphInc_Populate::Options Populate{}; - bool CreateAutoProduct = true; //!< wrap topology root in a Product (unparented Add only) - bool Flatten = false; //!< drop hierarchy containers, append faces as roots - bool Parallel = false; //!< run face-level construction in parallel - }; - - //! Outcome of a single Add() call. - struct Result - { - BRepGraph_NodeId TopologyRoot; - BRepGraph_ProductId Product; - BRepGraph_OccurrenceId Occurrence; - BRepGraph_RefId InsertedRef; - bool Ok = false; - }; - - //! Ingest a TopoDS_Shape as a new root subgraph, wrapping the topology root in a Product. - //! @param[in,out] theGraph graph to populate - //! @param[in] theShape shape to ingest - //! @return Result with TopologyRoot, Product and Occurrence set on success. - [[nodiscard]] static Standard_EXPORT Result Add(BRepGraph& theGraph, - const TopoDS_Shape& theShape); - - //! Ingest a TopoDS_Shape as a new root subgraph with explicit options. - //! @param[in,out] theGraph graph to populate - //! @param[in] theShape shape to ingest - //! @param[in] theOptions build-time options - //! @return Result with TopologyRoot set on success; Product/Occurrence set - //! when theOptions.CreateAutoProduct is true. - [[nodiscard]] static Standard_EXPORT Result Add(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const Options& theOptions); - - //! Ingest a TopoDS_Shape under an existing parent. - //! - //! Parent kind dispatch: - //! - Product: creates a child part-product, links via Occurrence with shape.Location(). - //! - Compound: appends topology root as a child reference. - //! - Shell: appends a Face as a FaceRef; other shapes via AddChild. - //! - Solid: appends a Shell as a ShellRef; other shapes via AddChild. - //! - CompSolid: appends a Solid as a SolidRef. - //! Other parent kinds (Wire, Edge, Vertex, Occurrence) are not supported and yield - //! an invalid Result (Result::Ok == false) without modification to the graph. - //! @param[in,out] theGraph graph to populate - //! @param[in] theShape shape to ingest - //! @param[in] theParent parent node receiving the topology - //! @return Result with TopologyRoot set, plus (Product, Occurrence, InsertedRef) for Product - //! parents or InsertedRef for topology container parents. - [[nodiscard]] static Standard_EXPORT Result Add(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const BRepGraph_NodeId theParent); - - //! Ingest a shape under an existing parent with explicit options. - //! Options::CreateAutoProduct is ignored. - [[nodiscard]] static Standard_EXPORT Result Add(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const BRepGraph_NodeId theParent, - const Options& theOptions); - -private: - static void appendImpl(BRepGraph& theGraph, - const TopoDS_Shape& theShape, - const Options& theOptions, - NCollection_DynamicArray* theOutFlatRoots = nullptr); - - static BRepGraph_NodeId detectTopologyRoot(const BRepGraph& theGraph, - const TopAbs_ShapeEnum theShapeType, - const uint32_t theOldCountOfShapeKind); - - static uint32_t snapshotCountForKind(const BRepGraph& theGraph, - const TopAbs_ShapeEnum theShapeType); - - static void populateUIDs(BRepGraph& theGraph); - - static void populateUIDsIncremental(BRepGraph& theGraph, - const int theOldVtx, - const int theOldEdge, - const int theOldCoEdge, - const int theOldWire, - const int theOldFace, - const int theOldShell, - const int theOldSolid, - const int theOldComp, - const int theOldCS, - const int theOldProduct, - const int theOldOccurrence, - const int theOldShellRef, - const int theOldFaceRef, - const int theOldWireRef, - const int theOldCoEdgeRef, - const int theOldVertexRef, - const int theOldSolidRef, - const int theOldChildRef); - - BRepGraph_Builder() = delete; -}; - -#endif // _BRepGraph_Builder_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Cache.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Cache.cxx new file mode 100644 index 0000000000..933d294070 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Cache.cxx @@ -0,0 +1,499 @@ +// 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 + +#include +#include +#include +#include +#include +#include + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_Cache, Standard_Transient) + +//================================================================================================= + +BRepGraph_Cache::BRepGraph_Cache() = default; + +//================================================================================================= + +void BRepGraph_Cache::NodeEntry::Reset() noexcept +{ + myNode = BRepGraph_NodeId(); + myGeneration = 0; + myKind = GenKind::None; +} + +//================================================================================================= + +bool BRepGraph_Cache::NodeEntry::BindOwnGen(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode) noexcept +{ + return bind(theCache, theNode, GenKind::Own); +} + +//================================================================================================= + +bool BRepGraph_Cache::NodeEntry::BindSubtreeGen(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode) noexcept +{ + return bind(theCache, theNode, GenKind::Subtree); +} + +//================================================================================================= + +bool BRepGraph_Cache::NodeEntry::IsFreshOwn(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode) const noexcept +{ + return isFresh(theCache, theNode, GenKind::Own); +} + +//================================================================================================= + +bool BRepGraph_Cache::NodeEntry::IsFreshSubtree(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode) const noexcept +{ + return isFresh(theCache, theNode, GenKind::Subtree); +} + +//================================================================================================= + +bool BRepGraph_Cache::NodeEntry::IsFresh(const BRepGraph_Cache& theCache) const noexcept +{ + return isFresh(theCache, myNode, myKind); +} + +//================================================================================================= + +bool BRepGraph_Cache::NodeEntry::bind(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode, + const GenKind theKind) noexcept +{ + uint32_t aGeneration = 0; + const bool isValid = theKind == GenKind::Own ? theCache.NodeOwnGen(theNode, aGeneration) + : theKind == GenKind::Subtree ? theCache.NodeSubtreeGen(theNode, aGeneration) + : false; + if (!isValid) + { + Reset(); + return false; + } + + myNode = theNode; + myGeneration = aGeneration; + myKind = theKind; + return true; +} + +//================================================================================================= + +bool BRepGraph_Cache::NodeEntry::isFresh(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode, + const GenKind theKind) const noexcept +{ + if (myKind != theKind || myNode != theNode) + { + return false; + } + + uint32_t aGeneration = 0; + const bool isValid = theKind == GenKind::Own ? theCache.NodeOwnGen(theNode, aGeneration) + : theKind == GenKind::Subtree ? theCache.NodeSubtreeGen(theNode, aGeneration) + : false; + return isValid && aGeneration == myGeneration; +} + +//================================================================================================= + +void BRepGraph_Cache::RefEntry::Reset() noexcept +{ + myRef = BRepGraph_RefId(); + myGeneration = 0; + myIsBound = false; +} + +//================================================================================================= + +bool BRepGraph_Cache::RefEntry::BindOwnGen(const BRepGraph_Cache& theCache, + const BRepGraph_RefId theRef) noexcept +{ + uint32_t aGeneration = 0; + if (!theCache.RefOwnGen(theRef, aGeneration)) + { + Reset(); + return false; + } + + myRef = theRef; + myGeneration = aGeneration; + myIsBound = true; + return true; +} + +//================================================================================================= + +bool BRepGraph_Cache::RefEntry::IsFreshOwn(const BRepGraph_Cache& theCache, + const BRepGraph_RefId theRef) const noexcept +{ + if (!myIsBound || myRef != theRef) + { + return false; + } + + uint32_t aGeneration = 0; + return theCache.RefOwnGen(theRef, aGeneration) && aGeneration == myGeneration; +} + +//================================================================================================= + +bool BRepGraph_Cache::RefEntry::IsFresh(const BRepGraph_Cache& theCache) const noexcept +{ + return IsFreshOwn(theCache, myRef); +} + +//================================================================================================= + +void BRepGraph_Cache::ItemEntry::Reset() noexcept +{ + myItem = BRepGraph_ItemId(); + myGeneration = 0; + myIsBound = false; +} + +//================================================================================================= + +bool BRepGraph_Cache::ItemEntry::BindOwnGen(const BRepGraph_Cache& theCache, + const BRepGraph_ItemId theItem) noexcept +{ + uint32_t aGeneration = 0; + if (!theCache.ItemOwnGen(theItem, aGeneration)) + { + Reset(); + return false; + } + + myItem = theItem; + myGeneration = aGeneration; + myIsBound = true; + return true; +} + +//================================================================================================= + +bool BRepGraph_Cache::ItemEntry::IsFreshOwn(const BRepGraph_Cache& theCache, + const BRepGraph_ItemId theItem) const noexcept +{ + if (!myIsBound || myItem != theItem) + { + return false; + } + + uint32_t aGeneration = 0; + return theCache.ItemOwnGen(theItem, aGeneration) && aGeneration == myGeneration; +} + +//================================================================================================= + +bool BRepGraph_Cache::ItemEntry::IsFresh(const BRepGraph_Cache& theCache) const noexcept +{ + return IsFreshOwn(theCache, myItem); +} + +//================================================================================================= + +void BRepGraph_Cache::Clear() noexcept {} + +//================================================================================================= + +void BRepGraph_Cache::CopyFreshTo(const BRepGraph_CopyRemap&) const {} + +//================================================================================================= + +const BRepGraph& BRepGraph_Cache::Graph() const +{ + if (myGraph == nullptr) + { + throw Standard_ProgramError("BRepGraph_Cache: cache is detached from graph"); + } + return *myGraph; +} + +//================================================================================================= + +void BRepGraph_Cache::OnAttached() noexcept {} + +//================================================================================================= + +void BRepGraph_Cache::OnDetached() noexcept {} + +//================================================================================================= + +bool BRepGraph_Cache::NodeSubtreeGen(const BRepGraph_NodeId theNode, + uint32_t& theGen) const noexcept +{ + if (myGraph == nullptr) + { + return false; + } + const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); + if (aDef == nullptr || theNode.IsRemoved(*myGraph)) + { + return false; + } + theGen = aDef->SubtreeGen; + return true; +} + +//================================================================================================= + +bool BRepGraph_Cache::NodeOwnGen(const BRepGraph_NodeId theNode, uint32_t& theGen) const noexcept +{ + if (myGraph == nullptr) + { + return false; + } + const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); + if (aDef == nullptr || theNode.IsRemoved(*myGraph)) + { + return false; + } + theGen = aDef->OwnGen; + return true; +} + +//================================================================================================= + +bool BRepGraph_Cache::RefOwnGen(const BRepGraph_RefId theRef, uint32_t& theGen) const noexcept +{ + if (myGraph == nullptr) + { + return false; + } + if (theRef.IsRemoved(*myGraph)) + { + return false; + } + // Ref version is the parent node's OwnGen. + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraph_NodeId aParent; + switch (theRef.RefKind) + { + case BRepGraph_RefId::Kind::Vertex: { + const BRepGraph_VertexRefId aRefId(theRef); + if (!aRefId.IsValid(aStorage.NbVertexRefs())) + { + return false; + } + // VertexRef parent is the edge referencing it - find via relations. + const BRepGraph_VertexId aVtxId = aStorage.VertexRef(aRefId).ChildVertexId; + if (!aVtxId.IsValid(aStorage.NbVertices())) + { + return false; + } + for (const BRepGraph_EdgeId& anEdgeId : aStorage.VertexRelations(aVtxId).EdgeIds) + { + if (anEdgeId.IsValid(aStorage.NbEdges())) + { + const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(anEdgeId); + if (anEdge.StartVertexRefId == aRefId || anEdge.EndVertexRefId == aRefId) + { + aParent = anEdgeId; + break; + } + } + } + break; + } + case BRepGraph_RefId::Kind::Wire: { + const BRepGraph_WireRefId aRefId(theRef); + if (!aRefId.IsValid(aStorage.NbWireRefs())) + { + return false; + } + aParent = aStorage.WireRef(aRefId).ParentFaceId; + break; + } + case BRepGraph_RefId::Kind::Face: { + const BRepGraph_FaceRefId aRefId(theRef); + if (!aRefId.IsValid(aStorage.NbFaceRefs())) + { + return false; + } + aParent = aStorage.FaceRef(aRefId).ParentShellId; + break; + } + case BRepGraph_RefId::Kind::Shell: { + const BRepGraph_ShellRefId aRefId(theRef); + if (!aRefId.IsValid(aStorage.NbShellRefs())) + { + return false; + } + aParent = aStorage.ShellRef(aRefId).ParentSolidId; + break; + } + case BRepGraph_RefId::Kind::Solid: { + const BRepGraph_SolidRefId aRefId(theRef); + if (!aRefId.IsValid(aStorage.NbSolidRefs())) + { + return false; + } + aParent = aStorage.SolidRef(aRefId).ParentCompSolidId; + break; + } + case BRepGraph_RefId::Kind::Child: { + const BRepGraph_ChildRefId aRefId(theRef); + if (!aRefId.IsValid(aStorage.NbChildRefs())) + { + return false; + } + aParent = aStorage.ChildRef(aRefId).ParentCompoundId; + break; + } + case BRepGraph_RefId::Kind::Occurrence: { + const BRepGraph_OccurrenceRefId aRefId(theRef); + if (!aRefId.IsValid(aStorage.NbOccurrenceRefs())) + { + return false; + } + aParent = aStorage.OccurrenceRef(aRefId).ParentProductId; + break; + } + default: + return false; + } + if (!aParent.IsValid()) + { + return false; + } + const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(aParent); + if (aDef == nullptr) + { + return false; + } + theGen = aDef->OwnGen; + return true; +} + +//================================================================================================= + +bool BRepGraph_Cache::ItemOwnGen(const BRepGraph_ItemId theItem, uint32_t& theGen) const noexcept +{ + if (!theItem.IsValid()) + { + return false; + } + + switch (theItem.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: + return NodeOwnGen(theItem.NodeId(), theGen); + case BRepGraph_ItemId::Domain::Reference: + return RefOwnGen(theItem.RefId(), theGen); + case BRepGraph_ItemId::Domain::None: + return false; + } + return false; +} + +//================================================================================================= + +bool BRepGraph_Cache::ResolveActiveRefChild(const BRepGraph_RefId theRef, + BRepGraph_NodeId& theNode) const noexcept +{ + theNode = BRepGraph_NodeId(); + if (myGraph == nullptr) + { + return false; + } + + const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRef); + if (aRef == nullptr || theRef.IsRemoved(*myGraph)) + { + return false; + } + + const BRepGraph_NodeId aNode = myGraph->Refs().Gen().ChildNode(theRef); + const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(aNode); + if (aDef == nullptr || aNode.IsRemoved(*myGraph)) + { + return false; + } + + theNode = aNode; + return true; +} + +//================================================================================================= + +bool BRepGraph_Cache::ResolveActiveFaceRef(const BRepGraph_FaceRefId theRef, + BRepGraph_FaceId& theFace) const noexcept +{ + BRepGraph_NodeId aNode; + if (!ResolveActiveRefChild(BRepGraph_RefId(theRef), aNode) + || aNode.NodeKind != BRepGraph_NodeId::Kind::Face) + { + theFace = BRepGraph_FaceId(); + return false; + } + + theFace = BRepGraph_FaceId::FromNodeId(aNode); + return true; +} + +//================================================================================================= + +void BRepGraph_Cache::attachGraph(BRepGraph* theGraph) noexcept +{ + if (myGraph == theGraph) + { + return; + } + if (myGraph != nullptr) + { + detachGraph(); + } + myGraph = theGraph; + if (myGraph != nullptr) + { + OnAttached(); + } +} + +//================================================================================================= + +void BRepGraph_Cache::rebindGraph(BRepGraph* theGraph) noexcept +{ + if (myGraph == theGraph) + { + return; + } + myGraph = theGraph; + if (myGraph != nullptr) + { + OnAttached(); + } +} + +//================================================================================================= + +void BRepGraph_Cache::detachGraph() noexcept +{ + if (myGraph == nullptr) + { + return; + } + Clear(); + OnDetached(); + myGraph = nullptr; +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Cache.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Cache.hxx new file mode 100644 index 0000000000..04979ea1f2 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Cache.hxx @@ -0,0 +1,230 @@ +// 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. + +#ifndef _BRepGraph_Cache_HeaderFile +#define _BRepGraph_Cache_HeaderFile + +#include +#include +#include +#include +#include +#include +#include + +#include + +class BRepGraph; +class BRepGraph_CacheRegistry; +class BRepGraph_CopyRemap; + +//! @brief Lightweight owner-bound base for transient graph cache services. +//! +//! A cache service stores typed, recomputable, graph-local data such as +//! bounding boxes, UV bounds, or display-resolution results. The registry owns +//! only service identity and lifetime binding; concrete caches own their own +//! typed storage and validate freshness lazily via graph generation counters. +class BRepGraph_Cache : public Standard_Transient +{ +public: + //! Cache service identity, unique within a graph registry. + [[nodiscard]] virtual const Standard_GUID& ID() const = 0; + + //! Cache service display name. + [[nodiscard]] virtual const TCollection_AsciiString& Name() const = 0; + + //! Clear all transient data owned by this cache. + Standard_EXPORT virtual void Clear() noexcept; + + //! Copy fresh, remappable cache data into the target graph described by the remap. + //! Default implementation copies nothing. + Standard_EXPORT virtual void CopyFreshTo(const BRepGraph_CopyRemap& theCopy) const; + + DEFINE_STANDARD_RTTIEXT(BRepGraph_Cache, Standard_Transient) + +protected: + //! @brief Value base for node-derived cache entries. + //! + //! Concrete cache services inherit this from their private entry structs and + //! store entries by value in their own typed storage. The base records the + //! node identity plus the generation counter used to validate freshness. + class NodeEntry + { + public: + //! Reset to an unbound stale state. + Standard_EXPORT void Reset() noexcept; + + //! Bind this entry to the current OwnGen of a node. + //! @return false when the cache is detached or the node is inactive + [[nodiscard]] Standard_EXPORT bool BindOwnGen(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode) noexcept; + + //! Bind this entry to the current SubtreeGen of a node. + //! @return false when the cache is detached or the node is inactive + [[nodiscard]] Standard_EXPORT bool BindSubtreeGen(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode) noexcept; + + //! Validate against the current OwnGen of the same node. + [[nodiscard]] Standard_EXPORT bool IsFreshOwn(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode) const noexcept; + + //! Validate against the current SubtreeGen of the same node. + [[nodiscard]] Standard_EXPORT bool IsFreshSubtree( + const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode) const noexcept; + + //! Validate against the generation kind captured at bind time. + [[nodiscard]] Standard_EXPORT bool IsFresh(const BRepGraph_Cache& theCache) const noexcept; + + //! True if the entry was successfully bound to a node generation. + [[nodiscard]] bool IsBound() const noexcept { return myKind != GenKind::None; } + + //! Node identity captured at bind time. + [[nodiscard]] BRepGraph_NodeId Node() const noexcept { return myNode; } + + private: + enum class GenKind : uint8_t + { + None, + Own, + Subtree + }; + + [[nodiscard]] Standard_EXPORT bool bind(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode, + const GenKind theKind) noexcept; + + [[nodiscard]] Standard_EXPORT bool isFresh(const BRepGraph_Cache& theCache, + const BRepGraph_NodeId theNode, + const GenKind theKind) const noexcept; + + BRepGraph_NodeId myNode; + uint32_t myGeneration = 0; + GenKind myKind = GenKind::None; + }; + + //! @brief Value base for reference-derived cache entries. + class RefEntry + { + public: + //! Reset to an unbound stale state. + Standard_EXPORT void Reset() noexcept; + + //! Bind this entry to the current OwnGen of a reference. + //! @return false when the cache is detached or the reference is inactive + [[nodiscard]] Standard_EXPORT bool BindOwnGen(const BRepGraph_Cache& theCache, + const BRepGraph_RefId theRef) noexcept; + + //! Validate against the current OwnGen of the same reference. + [[nodiscard]] Standard_EXPORT bool IsFreshOwn(const BRepGraph_Cache& theCache, + const BRepGraph_RefId theRef) const noexcept; + + //! Validate against the reference captured at bind time. + [[nodiscard]] Standard_EXPORT bool IsFresh(const BRepGraph_Cache& theCache) const noexcept; + + //! True if the entry was successfully bound to a reference generation. + [[nodiscard]] bool IsBound() const noexcept { return myIsBound; } + + //! Reference identity captured at bind time. + [[nodiscard]] BRepGraph_RefId Ref() const noexcept { return myRef; } + + private: + BRepGraph_RefId myRef; + uint32_t myGeneration = 0; + bool myIsBound = false; + }; + + //! @brief Value base for item-derived cache entries. + class ItemEntry + { + public: + //! Reset to an unbound stale state. + Standard_EXPORT void Reset() noexcept; + + //! Bind this entry to the current OwnGen of a graph item. + //! @return false when the cache is detached or the item is inactive + [[nodiscard]] Standard_EXPORT bool BindOwnGen(const BRepGraph_Cache& theCache, + const BRepGraph_ItemId theItem) noexcept; + + //! Validate against the current OwnGen of the same item. + [[nodiscard]] Standard_EXPORT bool IsFreshOwn(const BRepGraph_Cache& theCache, + const BRepGraph_ItemId theItem) const noexcept; + + //! Validate against the item captured at bind time. + [[nodiscard]] Standard_EXPORT bool IsFresh(const BRepGraph_Cache& theCache) const noexcept; + + //! True if the entry was successfully bound to an item generation. + [[nodiscard]] bool IsBound() const noexcept { return myIsBound; } + + //! Item identity captured at bind time. + [[nodiscard]] BRepGraph_ItemId Item() const noexcept { return myItem; } + + private: + BRepGraph_ItemId myItem; + uint32_t myGeneration = 0; + bool myIsBound = false; + }; + + Standard_EXPORT BRepGraph_Cache(); + + //! True while this cache is registered in a live graph registry. + [[nodiscard]] bool IsAttached() const noexcept { return myGraph != nullptr; } + + //! Attached graph for read-only cache services. Raises Standard_ProgramError if detached. + [[nodiscard]] Standard_EXPORT const BRepGraph& Graph() const; + + //! Attached mutable graph for graph-owned cache services. Returns null if detached. + [[nodiscard]] BRepGraph* AttachedGraph() const noexcept { return myGraph; } + + //! Called after the cache is attached to a graph registry. + Standard_EXPORT virtual void OnAttached() noexcept; + + //! Called before the cache is detached from a graph registry. + Standard_EXPORT virtual void OnDetached() noexcept; + + //! Return current SubtreeGen for an active node. + [[nodiscard]] Standard_EXPORT bool NodeSubtreeGen(const BRepGraph_NodeId theNode, + uint32_t& theGen) const noexcept; + + //! Return current OwnGen for an active node. + [[nodiscard]] Standard_EXPORT bool NodeOwnGen(const BRepGraph_NodeId theNode, + uint32_t& theGen) const noexcept; + + //! Return current OwnGen for an active reference. + [[nodiscard]] Standard_EXPORT bool RefOwnGen(const BRepGraph_RefId theRef, + uint32_t& theGen) const noexcept; + + //! Return current OwnGen for an active graph item. + [[nodiscard]] Standard_EXPORT bool ItemOwnGen(const BRepGraph_ItemId theItem, + uint32_t& theGen) const noexcept; + + //! Resolve an active reference to its active child node. + [[nodiscard]] Standard_EXPORT bool ResolveActiveRefChild( + const BRepGraph_RefId theRef, + BRepGraph_NodeId& theNode) const noexcept; + + //! Resolve an active face reference to its active face node. + [[nodiscard]] Standard_EXPORT bool ResolveActiveFaceRef(const BRepGraph_FaceRefId theRef, + BRepGraph_FaceId& theFace) const noexcept; + +private: + friend class ::BRepGraph_CacheRegistry; + + Standard_EXPORT void attachGraph(BRepGraph* theGraph) noexcept; + Standard_EXPORT void rebindGraph(BRepGraph* theGraph) noexcept; + Standard_EXPORT void detachGraph() noexcept; + + BRepGraph* myGraph = nullptr; +}; + +#endif // _BRepGraph_Cache_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheDerivedState.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheDerivedState.cxx new file mode 100644 index 0000000000..e7eee6bc04 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheDerivedState.cxx @@ -0,0 +1,710 @@ +// 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 "BRepGraph_CacheDerivedState.hxx" + +#include "BRepGraph.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_CacheDerivedState, BRepGraph_Cache) + +namespace +{ + +const Standard_GUID& theGUID() +{ + static const Standard_GUID aGUID("a1c2e3f4-5678-4abc-9def-0123456789ab"); + return aGUID; +} + +template +IdT remappedNode(const BRepGraph_CopyRemap& theCopy, const IdT theId) +{ + if (!theId.IsValid()) + { + return IdT(); + } + const BRepGraph_ItemId* aTarget = theCopy.TargetItem(BRepGraph_ItemId(theId)); + if (aTarget == nullptr || !aTarget->IsNode()) + { + return IdT(); + } + return IdT::FromNodeId(aTarget->NodeId()); +} + +//================================================================================================= + +BRepGraph_VertexId resolveChildVertex(const BRepGraph& theGraph, BRepGraph_VertexRefId theRef) +{ + if (!theRef.IsValid(theGraph.Refs().Vertices().Nb()) || theRef.IsRemoved(theGraph)) + { + return BRepGraph_VertexId::Invalid(); + } + const BRepGraphInc::VertexRef& aVRef = theGraph.Refs().Vertices().Entry(theRef); + return aVRef.ChildVertexId.IsValid(theGraph.Topo().Vertices().Nb()) + && !aVRef.ChildVertexId.IsRemoved(theGraph) + ? aVRef.ChildVertexId + : BRepGraph_VertexId::Invalid(); +} + +//================================================================================================= + +const occ::handle& edgeCurve3D(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge) +{ + return BRepGraph_Tool::Edge::Curve(theGraph, theEdge); +} + +//================================================================================================= + +const occ::handle& coEdgePCurve(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + return BRepGraph_Tool::CoEdge::PCurve(theGraph, theCoEdge); +} + +//================================================================================================= + +occ::handle faceSurface(const BRepGraph& theGraph, const BRepGraph_FaceId theFace) +{ + if (!theFace.IsValid(theGraph.Topo().Faces().Nb()) || theFace.IsRemoved(theGraph)) + { + return occ::handle(); + } + return BRepGraph_Tool::Face::Surface(theGraph, theFace); +} + +//================================================================================================= + +bool coEdgeOrientedVertices(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge, + BRepGraph_VertexId& theStartVertex, + BRepGraph_VertexId& theEndVertex) +{ + if (!theCoEdge.IsValid(theGraph.Topo().CoEdges().Nb()) || theCoEdge.IsRemoved(theGraph)) + { + return false; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); + if (!aCoEdge.ChildEdgeId.IsValid(theGraph.Topo().Edges().Nb()) + || aCoEdge.ChildEdgeId.IsRemoved(theGraph)) + { + return false; + } + + const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(aCoEdge.ChildEdgeId); + const BRepGraph_VertexRefId aStartRef = + aCoEdge.Orientation == TopAbs_REVERSED ? anEdge.EndVertexRefId : anEdge.StartVertexRefId; + const BRepGraph_VertexRefId anEndRef = + aCoEdge.Orientation == TopAbs_REVERSED ? anEdge.StartVertexRefId : anEdge.EndVertexRefId; + + theStartVertex = resolveChildVertex(theGraph, aStartRef); + theEndVertex = resolveChildVertex(theGraph, anEndRef); + return theStartVertex.IsValid() && theEndVertex.IsValid(); +} + +//================================================================================================= + +bool isEdgeSameRange(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const std::pair& theEdgeRange) +{ + const BRepGraphInc::EdgeRelations& anEdgeRel = theGraph.Topo().Edges().Relations(theEdge); + for (const BRepGraph_CoEdgeId& aCoEdgeId : anEdgeRel.CoEdgeIds) + { + if (!aCoEdgeId.IsValid(theGraph.Topo().CoEdges().Nb()) || aCoEdgeId.IsRemoved(theGraph)) + { + continue; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(aCoEdgeId); + if (!aCoEdge.FaceId.IsValid(theGraph.Topo().Faces().Nb()) || aCoEdge.FaceId.IsRemoved(theGraph)) + { + continue; + } + + if (coEdgePCurve(theGraph, aCoEdgeId).IsNull()) + { + continue; + } + + const std::pair aPCurveRange = + BRepGraph_Tool::CoEdge::Range(theGraph, aCoEdgeId); + if (std::abs(aPCurveRange.first - theEdgeRange.first) > Precision::PConfusion() + || std::abs(aPCurveRange.second - theEdgeRange.second) > Precision::PConfusion()) + { + return false; + } + } + return true; +} + +//================================================================================================= + +bool isEdgeSameParameter(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraphInc::EdgeDef& theEdgeDef, + const occ::handle& theCurve3D, + const std::pair& theEdgeRange) +{ + const double aTol = theEdgeDef.Tolerance + Precision::Confusion(); + constexpr int THE_NB_SAMPLES = 5; + + const BRepGraphInc::EdgeRelations& anEdgeRel = theGraph.Topo().Edges().Relations(theEdge); + for (int anIdx = 0; anIdx <= THE_NB_SAMPLES; ++anIdx) + { + const double aParam = + theEdgeRange.first + (theEdgeRange.second - theEdgeRange.first) * anIdx / THE_NB_SAMPLES; + const gp_Pnt aPoint3D = theCurve3D->Value(aParam); + + for (const BRepGraph_CoEdgeId& aCoEdgeId : anEdgeRel.CoEdgeIds) + { + if (!aCoEdgeId.IsValid(theGraph.Topo().CoEdges().Nb()) || aCoEdgeId.IsRemoved(theGraph)) + { + continue; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(aCoEdgeId); + if (!aCoEdge.FaceId.IsValid(theGraph.Topo().Faces().Nb()) + || aCoEdge.FaceId.IsRemoved(theGraph)) + { + continue; + } + + const occ::handle& aPCurve = coEdgePCurve(theGraph, aCoEdgeId); + if (aPCurve.IsNull()) + { + continue; + } + + const occ::handle aSurface = faceSurface(theGraph, aCoEdge.FaceId); + if (aSurface.IsNull()) + { + continue; + } + + const gp_Pnt2d aUV = aPCurve->Value(aParam); + const gp_Pnt aSurfacePoint = aSurface->Value(aUV.X(), aUV.Y()); + if (aPoint3D.Distance(aSurfacePoint) > aTol) + { + return false; + } + } + } + return true; +} + +} // namespace + +//================================================================================================= + +const Standard_GUID& BRepGraph_CacheDerivedState::GetID() +{ + return theGUID(); +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_CacheDerivedState::ID() const +{ + return theGUID(); +} + +//================================================================================================= + +const TCollection_AsciiString& BRepGraph_CacheDerivedState::Name() const +{ + static const TCollection_AsciiString aName("BRepGraph_CacheDerivedState"); + return aName; +} + +//================================================================================================= + +void BRepGraph_CacheDerivedState::Clear() noexcept +{ + std::unique_lock aLock(myMutex); + myEdgeEntries.Clear(); + myWireEntries.Clear(); + myShellEntries.Clear(); +} + +//================================================================================================= + +void BRepGraph_CacheDerivedState::CopyFreshTo(const BRepGraph_CopyRemap& theCopy) const +{ + occ::handle aTargetCache = + theCopy.TargetGraph().CacheRegistry().Ensure(); + + std::shared_lock aSourceLock(myMutex); + std::unique_lock aTargetLock(aTargetCache->myMutex); + + for (NCollection_DataMap::Iterator anIt(myEdgeEntries); anIt.More(); + anIt.Next()) + { + const BRepGraph_EdgeId aSourceEdge = anIt.Key(); + const EdgeEntry& aSourceEntry = anIt.Value(); + if (!aSourceEntry.IsFreshOwn(*this, BRepGraph_NodeId(aSourceEdge))) + { + continue; + } + const BRepGraph_EdgeId aTargetEdge = remappedNode(theCopy, aSourceEdge); + if (!aTargetEdge.IsValidIn(theCopy.TargetGraph().Topo().Edges())) + { + continue; + } + EdgeEntry aTargetEntry = aSourceEntry; + if (aTargetEntry.BindOwnGen(*aTargetCache, BRepGraph_NodeId(aTargetEdge))) + { + aTargetCache->myEdgeEntries.Bind(aTargetEdge, aTargetEntry); + } + } + + for (NCollection_DataMap::Iterator anIt(myWireEntries); anIt.More(); + anIt.Next()) + { + const BRepGraph_WireId aSourceWire = anIt.Key(); + const WireEntry& aSourceEntry = anIt.Value(); + if (!aSourceEntry.IsFreshOwn(*this, BRepGraph_NodeId(aSourceWire))) + { + continue; + } + const BRepGraph_WireId aTargetWire = remappedNode(theCopy, aSourceWire); + if (!aTargetWire.IsValidIn(theCopy.TargetGraph().Topo().Wires())) + { + continue; + } + WireEntry aTargetEntry = aSourceEntry; + if (aTargetEntry.BindOwnGen(*aTargetCache, BRepGraph_NodeId(aTargetWire))) + { + aTargetCache->myWireEntries.Bind(aTargetWire, aTargetEntry); + } + } + + for (NCollection_DataMap::Iterator anIt(myShellEntries); + anIt.More(); + anIt.Next()) + { + const BRepGraph_ShellId aSourceShell = anIt.Key(); + const ShellEntry& aSourceEntry = anIt.Value(); + if (!aSourceEntry.IsFreshOwn(*this, BRepGraph_NodeId(aSourceShell))) + { + continue; + } + const BRepGraph_ShellId aTargetShell = remappedNode(theCopy, aSourceShell); + if (!aTargetShell.IsValidIn(theCopy.TargetGraph().Topo().Shells())) + { + continue; + } + ShellEntry aTargetEntry = aSourceEntry; + if (aTargetEntry.BindOwnGen(*aTargetCache, BRepGraph_NodeId(aTargetShell))) + { + aTargetCache->myShellEntries.Bind(aTargetShell, aTargetEntry); + } + } +} + +//================================================================================================= + +bool BRepGraph_CacheDerivedState::ComputeEdgeStatus(const BRepGraph& theGraph, + BRepGraph_EdgeId theEdge, + EdgeEntry& theEntry) +{ + theEntry.Status = EdgeGeometryStatus::Invalid; + theEntry.IsClosed = false; + theEntry.SameRange = false; + theEntry.SameParameter = false; + + if (!theEdge.IsValid(theGraph.Topo().Edges().Nb()) || theEdge.IsRemoved(theGraph)) + { + return false; + } + + const BRepGraphInc::EdgeDef& aDef = theGraph.Topo().Edges().Definition(theEdge); + + const BRepGraph_VertexId aStartV = resolveChildVertex(theGraph, aDef.StartVertexRefId); + const BRepGraph_VertexId aEndV = resolveChildVertex(theGraph, aDef.EndVertexRefId); + theEntry.IsClosed = aStartV.IsValid() && aStartV == aEndV; + + const occ::handle& aCurve3D = edgeCurve3D(theGraph, theEdge); + const bool hasCurve3D = !aCurve3D.IsNull(); + theEntry.Status = + hasCurve3D ? EdgeGeometryStatus::HasCurve3D : EdgeGeometryStatus::MissingCurve3D; + + if (hasCurve3D) + { + const std::pair anEdgeRange = BRepGraph_Tool::Edge::Range(theGraph, theEdge); + theEntry.SameRange = isEdgeSameRange(theGraph, theEdge, anEdgeRange); + theEntry.SameParameter = + theEntry.SameRange && isEdgeSameParameter(theGraph, theEdge, aDef, aCurve3D, anEdgeRange); + return true; + } + + if (theEntry.IsClosed) + { + theEntry.Status = EdgeGeometryStatus::DegenerateOnSurface; + theEntry.SameRange = true; + theEntry.SameParameter = true; + return true; + } + + if (aStartV.IsValid() && aEndV.IsValid()) + { + const BRepGraphInc::VertexDef& aStartDef = theGraph.Topo().Vertices().Definition(aStartV); + const BRepGraphInc::VertexDef& aEndDef = theGraph.Topo().Vertices().Definition(aEndV); + + const double aDist = aStartDef.Point.Distance(aEndDef.Point); + if (aDist <= aDef.Tolerance) + { + theEntry.Status = EdgeGeometryStatus::DegenerateOnSurface; + theEntry.SameRange = true; + theEntry.SameParameter = true; + return true; + } + } + return true; +} + +//================================================================================================= + +bool BRepGraph_CacheDerivedState::ComputeWireIsClosed(const BRepGraph& theGraph, + BRepGraph_WireId theWire) +{ + if (!theWire.IsValid(theGraph.Topo().Wires().Nb()) || theWire.IsRemoved(theGraph)) + { + return false; + } + + const BRepGraphInc::WireRelations& aWR = theGraph.Topo().Wires().Relations(theWire); + if (aWR.CoEdgeIds.IsEmpty()) + { + return false; + } + + BRepGraph_VertexId aFirstStart; + BRepGraph_VertexId aPreviousEnd; + bool hasFirst = false; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aWR.CoEdgeIds) + { + BRepGraph_VertexId aStart; + BRepGraph_VertexId anEnd; + if (!coEdgeOrientedVertices(theGraph, aCoEdgeId, aStart, anEnd)) + { + return false; + } + if (!hasFirst) + { + aFirstStart = aStart; + hasFirst = true; + } + else if (aStart != aPreviousEnd) + { + return false; + } + aPreviousEnd = anEnd; + } + + return hasFirst && aPreviousEnd == aFirstStart; +} + +//================================================================================================= + +bool BRepGraph_CacheDerivedState::ComputeShellStatus(const BRepGraph& theGraph, + BRepGraph_ShellId theShell, + ShellEntry& theEntry) +{ + theEntry.Status = ShellClosureStatus::Invalid; + + if (!theShell.IsValid(theGraph.Topo().Shells().Nb()) || theShell.IsRemoved(theGraph)) + { + return false; + } + + const BRepGraphInc::ShellRelations& aSR = theGraph.Topo().Shells().Relations(theShell); + if (aSR.FaceRefIds.IsEmpty()) + { + theEntry.Status = ShellClosureStatus::Empty; + return true; + } + + NCollection_FlatMap anActiveFaces; + + for (const BRepGraph_FaceRefId& aFaceRefId : aSR.FaceRefIds) + { + if (!aFaceRefId.IsValid(theGraph.Refs().Faces().Nb()) || aFaceRefId.IsRemoved(theGraph)) + { + continue; + } + + const BRepGraphInc::FaceRef& aFaceRef = theGraph.Refs().Faces().Entry(aFaceRefId); + const BRepGraph_FaceId aFaceId = aFaceRef.ChildFaceId; + if (aFaceId.IsValid(theGraph.Topo().Faces().Nb()) && !aFaceId.IsRemoved(theGraph)) + { + anActiveFaces.Add(aFaceId); + } + } + + if (anActiveFaces.IsEmpty()) + { + theEntry.Status = ShellClosureStatus::Empty; + return true; + } + + NCollection_DataMap anEdgeUsage; + + for (NCollection_FlatMap::Iterator aFaceIter(anActiveFaces); aFaceIter.More(); + aFaceIter.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIter.Value(); + const BRepGraphInc::FaceRelations& aFR = theGraph.Topo().Faces().Relations(aFaceId); + for (const BRepGraph_WireRefId& aWireRefId : aFR.WireRefIds) + { + if (!aWireRefId.IsValid(theGraph.Refs().Wires().Nb()) || aWireRefId.IsRemoved(theGraph)) + { + continue; + } + + const BRepGraphInc::WireRef& aWireRef = theGraph.Refs().Wires().Entry(aWireRefId); + const BRepGraph_WireId aWireId = aWireRef.ChildWireId; + if (!aWireId.IsValid(theGraph.Topo().Wires().Nb()) || aWireId.IsRemoved(theGraph)) + { + continue; + } + + const BRepGraphInc::WireRelations& aWR = theGraph.Topo().Wires().Relations(aWireId); + for (const BRepGraph_CoEdgeId& aCoEdgeId : aWR.CoEdgeIds) + { + if (!aCoEdgeId.IsValid(theGraph.Topo().CoEdges().Nb()) || aCoEdgeId.IsRemoved(theGraph)) + { + continue; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(aCoEdgeId); + if (aCoEdge.FaceId != aFaceId) + { + continue; + } + + const BRepGraph_EdgeId anEdgeId = aCoEdge.ChildEdgeId; + if (!anEdgeId.IsValid(theGraph.Topo().Edges().Nb()) || anEdgeId.IsRemoved(theGraph)) + { + continue; + } + + EdgeEntry anEdgeEntry; + if (!ComputeEdgeStatus(theGraph, anEdgeId, anEdgeEntry) + || anEdgeEntry.Status == EdgeGeometryStatus::DegenerateOnSurface) + { + continue; + } + + uint32_t* aCount = anEdgeUsage.ChangeSeek(anEdgeId); + if (aCount != nullptr) + { + ++(*aCount); + } + else + { + anEdgeUsage.Bind(anEdgeId, 1u); + } + } + } + } + + if (anEdgeUsage.IsEmpty()) + { + theEntry.Status = ShellClosureStatus::Closed; + return true; + } + + bool hasOpen = false; + bool hasNonManifold = false; + + for (NCollection_DataMap::Iterator anIter(anEdgeUsage); anIter.More(); + anIter.Next()) + { + const uint32_t aCount = anIter.Value(); + if (aCount == 1u) + { + hasOpen = true; + } + else if (aCount > 2u) + { + hasNonManifold = true; + } + } + + if (hasNonManifold) + { + theEntry.Status = ShellClosureStatus::NonManifold; + } + else if (hasOpen) + { + theEntry.Status = ShellClosureStatus::Open; + } + else + { + theEntry.Status = ShellClosureStatus::Closed; + } + + return true; +} + +//================================================================================================= + +bool BRepGraph_CacheDerivedState::GetEdgeStatus(BRepGraph_EdgeId theEdge, EdgeEntry& theEntry) +{ + EdgeEntry aComputed; + if (!ComputeEdgeStatus(Graph(), theEdge, aComputed)) + { + return false; + } + + std::unique_lock aLock(myMutex); + EdgeEntry* aStored = myEdgeEntries.ChangeSeek(theEdge); + if (aStored != nullptr) + { + *aStored = aComputed; + } + else + { + myEdgeEntries.Bind(theEdge, aComputed); + aStored = myEdgeEntries.ChangeSeek(theEdge); + } + if (!aStored->BindOwnGen(*this, theEdge)) + { + myEdgeEntries.UnBind(theEdge); + return false; + } + theEntry = *aStored; + return true; +} + +//================================================================================================= + +bool BRepGraph_CacheDerivedState::GetWireIsClosed(BRepGraph_WireId theWire, bool& theClosed) +{ + const bool aClosed = ComputeWireIsClosed(Graph(), theWire); + + std::unique_lock aLock(myMutex); + WireEntry* aStored = myWireEntries.ChangeSeek(theWire); + if (aStored == nullptr) + { + myWireEntries.Bind(theWire, WireEntry()); + aStored = myWireEntries.ChangeSeek(theWire); + } + aStored->IsClosed = aClosed; + if (!aStored->BindOwnGen(*this, theWire)) + { + myWireEntries.UnBind(theWire); + return false; + } + theClosed = aClosed; + return true; +} + +//================================================================================================= + +bool BRepGraph_CacheDerivedState::GetShellStatus(BRepGraph_ShellId theShell, ShellEntry& theEntry) +{ + ShellEntry aComputed; + if (!ComputeShellStatus(Graph(), theShell, aComputed)) + { + return false; + } + + std::unique_lock aLock(myMutex); + ShellEntry* aStored = myShellEntries.ChangeSeek(theShell); + if (aStored != nullptr) + { + *aStored = aComputed; + } + else + { + myShellEntries.Bind(theShell, aComputed); + aStored = myShellEntries.ChangeSeek(theShell); + } + if (!aStored->BindOwnGen(*this, theShell)) + { + myShellEntries.UnBind(theShell); + return false; + } + theEntry = *aStored; + return true; +} + +//================================================================================================= + +void BRepGraph_CacheDerivedState::SetEdgeStatus(BRepGraph_EdgeId theEdge, const EdgeEntry& theEntry) +{ + std::unique_lock aLock(myMutex); + EdgeEntry* aStored = myEdgeEntries.ChangeSeek(theEdge); + if (aStored != nullptr) + { + *aStored = theEntry; + } + else + { + myEdgeEntries.Bind(theEdge, theEntry); + aStored = myEdgeEntries.ChangeSeek(theEdge); + } + [[maybe_unused]] const bool isBound = aStored->BindOwnGen(*this, theEdge); +} + +//================================================================================================= + +void BRepGraph_CacheDerivedState::SetWireIsClosed(BRepGraph_WireId theWire, bool theClosed) +{ + std::unique_lock aLock(myMutex); + WireEntry* aStored = myWireEntries.ChangeSeek(theWire); + if (aStored == nullptr) + { + myWireEntries.Bind(theWire, WireEntry()); + aStored = myWireEntries.ChangeSeek(theWire); + } + aStored->IsClosed = theClosed; + [[maybe_unused]] const bool isBound = aStored->BindOwnGen(*this, theWire); +} + +//================================================================================================= + +void BRepGraph_CacheDerivedState::SetShellStatus(BRepGraph_ShellId theShell, + const ShellEntry& theEntry) +{ + std::unique_lock aLock(myMutex); + ShellEntry* aStored = myShellEntries.ChangeSeek(theShell); + if (aStored != nullptr) + { + *aStored = theEntry; + } + else + { + myShellEntries.Bind(theShell, theEntry); + aStored = myShellEntries.ChangeSeek(theShell); + } + [[maybe_unused]] const bool isBound = aStored->BindOwnGen(*this, theShell); +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheDerivedState.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheDerivedState.hxx new file mode 100644 index 0000000000..98c4938a4a --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheDerivedState.hxx @@ -0,0 +1,171 @@ +// 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. + +#ifndef _BRepGraph_CacheDerivedState_HeaderFile +#define _BRepGraph_CacheDerivedState_HeaderFile + +#include +#include +#include +#include +#include + +#include + +class BRepGraphInc_Storage; + +//! @brief Cache for derived edge, wire, and shell properties. +//! +//! Stores degeneracy, closure, SameRange, and SameParameter results derived from +//! current BRepGraph topology and geometry. Cache getters compute on read, +//! refresh the stored entries, and expose only fresh values to callers. +class BRepGraph_CacheDerivedState : public BRepGraph_Cache +{ +public: + //! @brief Geometry status of an edge. + //! + enum class EdgeGeometryStatus + { + HasCurve3D, //!< Edge has a valid 3D curve + DegenerateOnSurface, //!< Edge collapses to a point on a face surface + MissingCurve3D, //!< Edge has no 3D curve and no valid degenerate evidence + Invalid //!< Edge id is invalid or removed + }; + + //! @brief Shell closure status. + //! + enum class ShellClosureStatus + { + Empty, //!< Shell has no active faces + Open, //!< At least one non-degenerate boundary edge has exactly one use + Closed, //!< All non-degenerate boundary edges have exactly two uses + NonManifold, //!< At least one non-degenerate boundary edge has more than two uses + Invalid //!< Shell id is invalid or removed + }; + + //! @brief Cached derived-state entry for an edge. + //! + //! Inherits generation-based freshness tracking from NodeEntry and adds + //! edge-specific derived properties. + struct EdgeEntry : public NodeEntry + { + EdgeGeometryStatus Status = EdgeGeometryStatus::Invalid; //!< Edge geometry status + bool IsClosed = false; //!< True if start vertex == end vertex + bool SameRange = false; //!< True if all PCurve ranges equal 3D curve range + bool SameParameter = false; //!< True if PCurves evaluate to the 3D curve on surfaces + }; + + //! @brief Cached derived-state entry for a wire. + //! + //! Inherits generation-based freshness tracking from NodeEntry and adds + //! wire-specific derived properties. + struct WireEntry : public NodeEntry + { + bool IsClosed = false; //!< True if the wire forms a topological loop + }; + + //! @brief Cached derived-state entry for a shell. + //! + //! Inherits generation-based freshness tracking from NodeEntry and adds + //! shell-specific derived properties. + struct ShellEntry : public NodeEntry + { + ShellClosureStatus Status = ShellClosureStatus::Invalid; //!< Shell closure status + }; + + //! Returns the unique cache service GUID. + [[nodiscard]] Standard_EXPORT static const Standard_GUID& GetID(); + + //! Returns the unique cache service GUID. + [[nodiscard]] Standard_EXPORT const Standard_GUID& ID() const override; + + //! Returns the cache service display name. + [[nodiscard]] Standard_EXPORT const TCollection_AsciiString& Name() const override; + + //! Clears all cached entries. + Standard_EXPORT void Clear() noexcept override; + + //! Copy fresh, remappable derived-state entries into the target graph. + Standard_EXPORT void CopyFreshTo(const BRepGraph_CopyRemap& theCopy) const override; + + //! @brief Return edge status, computing and storing a fresh entry. + //! @param[in] theEdge edge definition identifier + //! @param[out] theEntry filled with a fresh derived entry + //! @return true if computation succeeded + [[nodiscard]] Standard_EXPORT bool GetEdgeStatus(BRepGraph_EdgeId theEdge, EdgeEntry& theEntry); + + //! @brief Return wire closure, computing and storing a fresh entry. + //! @param[in] theWire wire definition identifier + //! @param[out] theClosed filled with the fresh derived value + //! @return true if computation succeeded + [[nodiscard]] Standard_EXPORT bool GetWireIsClosed(BRepGraph_WireId theWire, bool& theClosed); + + //! @brief Return shell status, computing and storing a fresh entry. + //! @param[in] theShell shell definition identifier + //! @param[out] theEntry filled with a fresh derived entry + //! @return true if computation succeeded + [[nodiscard]] Standard_EXPORT bool GetShellStatus(BRepGraph_ShellId theShell, + ShellEntry& theEntry); + + //! @brief Store a pre-computed edge entry (warm-start from import or after computation). + //! @param[in] theEdge edge definition identifier + //! @param[in] theEntry pre-computed entry to store + Standard_EXPORT void SetEdgeStatus(BRepGraph_EdgeId theEdge, const EdgeEntry& theEntry); + + //! @brief Store a pre-computed wire closure value. + //! @param[in] theWire wire definition identifier + //! @param[in] theClosed pre-computed closure value + Standard_EXPORT void SetWireIsClosed(BRepGraph_WireId theWire, bool theClosed); + + //! @brief Store a pre-computed shell entry (warm-start from import or after computation). + //! @param[in] theShell shell definition identifier + //! @param[in] theEntry pre-computed entry to store + Standard_EXPORT void SetShellStatus(BRepGraph_ShellId theShell, const ShellEntry& theEntry); + + //! Compute edge derived state directly from a BRepGraph without caching. + //! Used by callers like Reconstruct that need one-shot computation. + //! @param[in] theGraph source graph + //! @param[in] theEdge edge definition identifier + //! @param[out] theEntry filled with computed entry + //! @return true if computation succeeded + [[nodiscard]] Standard_EXPORT static bool ComputeEdgeStatus(const BRepGraph& theGraph, + BRepGraph_EdgeId theEdge, + EdgeEntry& theEntry); + + //! Compute wire closure directly from a BRepGraph without caching. + //! @param[in] theGraph source graph + //! @param[in] theWire wire definition identifier + //! @return true if the wire is closed + [[nodiscard]] Standard_EXPORT static bool ComputeWireIsClosed(const BRepGraph& theGraph, + BRepGraph_WireId theWire); + + //! Compute shell closure status directly from a BRepGraph without caching. + //! @param[in] theGraph source graph + //! @param[in] theShell shell definition identifier + //! @param[out] theEntry filled with computed entry + //! @return true if computation succeeded + [[nodiscard]] Standard_EXPORT static bool ComputeShellStatus(const BRepGraph& theGraph, + BRepGraph_ShellId theShell, + ShellEntry& theEntry); + + DEFINE_STANDARD_RTTIEXT(BRepGraph_CacheDerivedState, BRepGraph_Cache) + +private: + mutable std::shared_mutex myMutex; + + NCollection_DataMap myEdgeEntries; + NCollection_DataMap myWireEntries; + NCollection_DataMap myShellEntries; +}; + +#endif // _BRepGraph_CacheDerivedState_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheIterator.hxx new file mode 100644 index 0000000000..818c149322 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheIterator.hxx @@ -0,0 +1,63 @@ +// 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. + +#ifndef _BRepGraph_CacheIterator_HeaderFile +#define _BRepGraph_CacheIterator_HeaderFile + +#include +#include + +//! @brief Iterator over registered cache families in a BRepGraph_CacheRegistry. +//! +//! Supports OCCT More()/Next()/Value() pattern and STL range-for via begin()/end(). +class BRepGraph_CacheIterator +{ +public: + //! Construct an iterator over all cache families in the registry. + explicit BRepGraph_CacheIterator(const BRepGraph_CacheRegistry& theRegistry) + : myRegistry(&theRegistry), + myCount(theRegistry.NbCaches()) + { + } + + //! True if the iterator has a current element. + [[nodiscard]] bool More() const { return myCurrent < myCount; } + + //! Advance to the next cache family. + void Next() { ++myCurrent; } + + //! Return the current cache family descriptor. + [[nodiscard]] occ::handle Value() const { return myRegistry->Cache(myCurrent); } + + //! Return the current slot index in the registry. + [[nodiscard]] uint32_t Slot() const { return myCurrent; } + + //! Number of cache families in the registry. + [[nodiscard]] uint32_t NbCaches() const { return myRegistry->NbCaches(); } + + //! STL range-for support. + NCollection_ForwardRangeIterator begin() + { + return NCollection_ForwardRangeIterator(this); + } + + //! Sentinel marking end of iteration. + NCollection_ForwardRangeSentinel end() const { return NCollection_ForwardRangeSentinel{}; } + +private: + const BRepGraph_CacheRegistry* myRegistry; + uint32_t myCount; + uint32_t myCurrent = 0; +}; + +#endif // _BRepGraph_CacheIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheKindIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheKindIterator.hxx deleted file mode 100644 index 10caa2f317..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheKindIterator.hxx +++ /dev/null @@ -1,76 +0,0 @@ -// 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. - -#ifndef _BRepGraph_CacheKindIterator_HeaderFile -#define _BRepGraph_CacheKindIterator_HeaderFile - -#include -#include - -//! @brief Zero-allocation iterator over populated cache kinds on a node or reference. -//! -//! Template parameter TKeyId is either BRepGraph_NodeId or BRepGraph_RefId. -//! Supports OCCT More()/Next()/Value() pattern and STL range-for via begin()/end(). -//! -//! Constructed by BRepGraph::CacheView::CacheKindIter(). Stores populated -//! kind slot indices in a fixed-size stack buffer (no heap allocation). -//! -//! @code -//! // Range-for: -//! for (const occ::handle& aKind : aGraph.Cache().CacheKindIter(aNode)) -//! doSomething(aKind); -//! -//! // Traditional: -//! for (auto anIt = aGraph.Cache().CacheKindIter(aNode); anIt.More(); anIt.Next()) -//! doSomething(anIt.Value()); -//! @endcode -template -class BRepGraph_CacheKindIterator -{ -public: - //! True if the iterator has a current element. - [[nodiscard]] bool More() const { return myCurrent < myCount; } - - //! Advance to the next populated cache kind. - void Next() { ++myCurrent; } - - //! Return the current cache kind descriptor. - [[nodiscard]] occ::handle Value() const - { - return BRepGraph_CacheKindRegistry::FindKind(mySlots[myCurrent]); - } - - //! Return the current cache-kind slot index (for fast slot-based access). - [[nodiscard]] int KindSlot() const { return mySlots[myCurrent]; } - - //! Number of populated cache kinds found. - [[nodiscard]] int NbKinds() const { return myCount; } - - //! STL range-for support. - NCollection_ForwardRangeIterator begin() - { - return NCollection_ForwardRangeIterator(this); - } - - //! Sentinel marking end of iteration. - NCollection_ForwardRangeSentinel end() const { return NCollection_ForwardRangeSentinel{}; } - -private: - friend class BRepGraph::CacheView; - static constexpr int THE_MAX_SLOTS = BRepGraph_TransientCache::THE_DEFAULT_RESERVED_KIND_COUNT; - int mySlots[THE_MAX_SLOTS]; - int myCount = 0; - int myCurrent = 0; -}; - -#endif // _BRepGraph_CacheKindIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheMesh.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheMesh.cxx new file mode 100644 index 0000000000..d507909912 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheMesh.cxx @@ -0,0 +1,1115 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_CacheMesh::Driver, Standard_Transient) +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_CacheMesh, BRepGraph_Cache) + +namespace +{ +const Standard_GUID& theGUID() +{ + static const Standard_GUID aGUID("d706ee7b-3c3a-4994-8fdc-c6902bd81450"); + return aGUID; +} + +bool isSameDriver(const occ::handle& theLeft, + const occ::handle& theRight) +{ + if (theLeft.IsNull() || theRight.IsNull()) + { + return theLeft == theRight; + } + return theLeft->ID().IsSame(theRight->ID()) && theLeft->RecipeHash() == theRight->RecipeHash(); +} + +template +IdT remappedNode(const BRepGraph_CopyRemap& theCopy, const IdT theId) +{ + if (!theId.IsValid()) + { + return IdT(); + } + const BRepGraph_ItemId* aTarget = theCopy.TargetItem(BRepGraph_ItemId(theId)); + if (aTarget == nullptr || !aTarget->IsNode()) + { + return IdT(); + } + return IdT::FromNodeId(aTarget->NodeId()); +} + +void appendPolygonsOnTri( + NCollection_LinearVector>& theTarget, + const NCollection_LinearVector>& theSource) +{ + for (const occ::handle& aPolygon : theSource) + { + if (!aPolygon.IsNull()) + { + theTarget.Append(aPolygon); + } + } +} +} // namespace + +//================================================================================================= + +struct BRepGraph_CacheMesh::Slot +{ + Slot() + : Allocator(new NCollection_IncAllocator), + FaceMeshes(256, Allocator), + CoEdgeMeshes(256, Allocator), + EdgeMeshes(256, Allocator) + { + Allocator->SetThreadSafe(true); + } + + SlotId Id = DefaultDisplaySlot; + occ::handle MeshDriver; + uint64_t RecipeHash = 0; + uint32_t Generation = 1; + + occ::handle Allocator; + NCollection_DynamicArray FaceMeshes; + NCollection_DynamicArray CoEdgeMeshes; + NCollection_DynamicArray EdgeMeshes; + + void Clear() + { + FaceMeshes.Clear(true); + CoEdgeMeshes.Clear(true); + EdgeMeshes.Clear(true); + Allocator->Reset(false); + ++Generation; + } +}; + +//================================================================================================= + +template +void BRepGraph_CacheMesh::ensureSize(NCollection_DynamicArray& theVec, const size_t theIndex) +{ + while (theVec.Size() <= theIndex) + { + theVec.Appended(); + } +} + +//================================================================================================= + +BRepGraph_CacheMesh::BRepGraph_CacheMesh() +{ + (void)changeSlot(DefaultDisplaySlot); +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_CacheMesh::GetID() +{ + return theGUID(); +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_CacheMesh::ID() const +{ + return GetID(); +} + +//================================================================================================= + +const TCollection_AsciiString& BRepGraph_CacheMesh::Name() const +{ + static const TCollection_AsciiString aName("BRepGraph.CacheMesh"); + return aName; +} + +//================================================================================================= + +void BRepGraph_CacheMesh::Clear() noexcept +{ + for (size_t aSlotIdx = 0; aSlotIdx < mySlots.Size(); ++aSlotIdx) + { + mySlots.ChangeValue(aSlotIdx).Clear(); + } +} + +//================================================================================================= + +void BRepGraph_CacheMesh::CopyFreshTo(const BRepGraph_CopyRemap& theCopy) const +{ + occ::handle aTargetCache = + theCopy.TargetGraph().CacheRegistry().Ensure(); + aTargetCache->SetActiveDisplaySlot(myActiveSlot); + + for (size_t aSlotIdx = 0; aSlotIdx < mySlots.Size(); ++aSlotIdx) + { + const Slot& aSrcSlot = mySlots.Value(aSlotIdx); + Slot& aDstSlot = aTargetCache->changeSlot(aSrcSlot.Id); + + aDstSlot.MeshDriver = aSrcSlot.MeshDriver; + aDstSlot.RecipeHash = aSrcSlot.RecipeHash; + aDstSlot.Clear(); + + for (BRepGraph_FaceId aSrcFace = BRepGraph_FaceId::Start(); + aSrcFace.IsValid(static_cast(aSrcSlot.FaceMeshes.Size())); + ++aSrcFace) + { + const FaceMeshEntry* aSrcEntry = FindFaceMesh(aSrcSlot.Id, aSrcFace); + if (aSrcEntry == nullptr) + { + continue; + } + const BRepGraph_FaceId aDstFace = remappedNode(theCopy, aSrcFace); + if (!aDstFace.IsValidIn(theCopy.TargetGraph().Topo().Faces())) + { + continue; + } + FaceMeshEntry& aDstEntry = aTargetCache->ChangeFaceMesh(aSrcSlot.Id, aDstFace); + aDstEntry.Triangulation = aSrcEntry->Triangulation; + aTargetCache->bindEntry(aDstEntry, aDstFace, aDstSlot); + ++aDstEntry.MeshGeneration; + } + + for (BRepGraph_EdgeId aSrcEdge = BRepGraph_EdgeId::Start(); + aSrcEdge.IsValid(static_cast(aSrcSlot.EdgeMeshes.Size())); + ++aSrcEdge) + { + const EdgeMeshEntry* aSrcEntry = FindEdgeMesh(aSrcSlot.Id, aSrcEdge); + if (aSrcEntry == nullptr) + { + continue; + } + const BRepGraph_EdgeId aDstEdge = remappedNode(theCopy, aSrcEdge); + if (!aDstEdge.IsValidIn(theCopy.TargetGraph().Topo().Edges())) + { + continue; + } + EdgeMeshEntry& aDstEntry = aTargetCache->ChangeEdgeMesh(aSrcSlot.Id, aDstEdge); + aDstEntry.Polygon3D = aSrcEntry->Polygon3D; + aTargetCache->bindEntry(aDstEntry, aDstEdge, aDstSlot); + } + + for (BRepGraph_CoEdgeId aSrcCoEdge = BRepGraph_CoEdgeId::Start(); + aSrcCoEdge.IsValid(static_cast(aSrcSlot.CoEdgeMeshes.Size())); + ++aSrcCoEdge) + { + const CoEdgeMeshEntry* aSrcRaw = findCoEdgeEntryRaw(aSrcSlot.Id, aSrcCoEdge); + if (aSrcRaw == nullptr) + { + continue; + } + + const bool hasFreshPolygon2D = FindCoEdgePolygon2D(aSrcSlot.Id, aSrcCoEdge) != nullptr; + bool hasFreshPolygonsOnTri = FindCoEdgePolygonOnTri(aSrcSlot.Id, aSrcCoEdge) != nullptr; + if (!hasFreshPolygon2D && !hasFreshPolygonsOnTri) + { + continue; + } + + const BRepGraph_CoEdgeId aDstCoEdge = remappedNode(theCopy, aSrcCoEdge); + if (!aDstCoEdge.IsValidIn(theCopy.TargetGraph().Topo().CoEdges())) + { + continue; + } + + if (hasFreshPolygonsOnTri) + { + const BRepGraphInc::CoEdgeDef& aDstDef = + theCopy.TargetGraph().Topo().CoEdges().Definition(aDstCoEdge); + hasFreshPolygonsOnTri = + aDstDef.FaceId.IsValidIn(aDstSlot.FaceMeshes) + && aTargetCache->findFaceEntryRaw(aSrcSlot.Id, aDstDef.FaceId) != nullptr; + } + + CoEdgeMeshEntry& aDstEntry = aTargetCache->ChangeCoEdgeMesh(aSrcSlot.Id, aDstCoEdge); + if (hasFreshPolygon2D) + { + aDstEntry.Polygon2D = aSrcRaw->Polygon2D; + } + if (hasFreshPolygonsOnTri) + { + appendPolygonsOnTri(aDstEntry.PolygonsOnTri, aSrcRaw->PolygonsOnTri); + } + if (aDstEntry.IsPresent()) + { + aTargetCache->bindEntry(aDstEntry, aDstCoEdge, aDstSlot); + } + else + { + aDstEntry.Reset(); + } + } + } +} + +//================================================================================================= + +BRepGraph_CacheMesh::Slot& BRepGraph_CacheMesh::changeSlot(const SlotId theSlot) +{ + while (mySlots.Size() <= theSlot) + { + mySlots.EmplaceAppend(); + } + Slot& aSlot = mySlots.ChangeValue(static_cast(theSlot)); + aSlot.Id = theSlot; + return aSlot; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::Slot* BRepGraph_CacheMesh::findSlot(const SlotId theSlot) const +{ + if (theSlot >= mySlots.Size()) + { + return nullptr; + } + return &mySlots.Value(static_cast(theSlot)); +} + +//================================================================================================= + +void BRepGraph_CacheMesh::RegisterDriver(const SlotId theSlot, const occ::handle& theDriver) +{ + Slot& aSlot = changeSlot(theSlot); + if (isSameDriver(aSlot.MeshDriver, theDriver)) + { + aSlot.MeshDriver = theDriver; + return; + } + + aSlot.MeshDriver = theDriver; + aSlot.RecipeHash = theDriver.IsNull() ? 0 : theDriver->RecipeHash(); + aSlot.Clear(); +} + +//================================================================================================= + +void BRepGraph_CacheMesh::UnregisterDriver(const SlotId theSlot) +{ + Slot* aSlot = const_cast(findSlot(theSlot)); + if (aSlot == nullptr) + { + return; + } + aSlot->MeshDriver.Nullify(); + aSlot->RecipeHash = 0; + aSlot->Clear(); +} + +//================================================================================================= + +const occ::handle& BRepGraph_CacheMesh::DriverOf( + const SlotId theSlot) const +{ + static const occ::handle THE_NULL_DRIVER; + const Slot* aSlot = findSlot(theSlot); + return aSlot == nullptr ? THE_NULL_DRIVER : aSlot->MeshDriver; +} + +//================================================================================================= + +BRepGraph_CacheMesh::SlotState BRepGraph_CacheMesh::State(const SlotId theSlot) const +{ + SlotState aState; + aState.Slot = theSlot; + if (const Slot* aSlot = findSlot(theSlot)) + { + aState.RecipeHash = aSlot->RecipeHash; + aState.Generation = aSlot->Generation; + aState.HasDriver = !aSlot->MeshDriver.IsNull(); + } + return aState; +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::isSlotActual(const Slot& theSlot, + const EntryStamp& theStamp) const noexcept +{ + return theStamp.RecipeHash == theSlot.RecipeHash && theStamp.SlotGeneration == theSlot.Generation; +} + +//================================================================================================= + +void BRepGraph_CacheMesh::bindEntry(FaceMeshEntry& theEntry, + const BRepGraph_FaceId theFace, + const Slot& theSlot) const +{ + (void)theEntry.BindSubtreeGen(*this, BRepGraph_NodeId(theFace)); + theEntry.Stamp.RecipeHash = theSlot.RecipeHash; + theEntry.Stamp.SlotGeneration = theSlot.Generation; +} + +//================================================================================================= + +void BRepGraph_CacheMesh::bindEntry(CoEdgeMeshEntry& theEntry, + const BRepGraph_CoEdgeId theCoEdge, + const Slot& theSlot) const +{ + (void)theEntry.CoEdgeStamp.BindSubtreeGen(*this, BRepGraph_NodeId(theCoEdge)); + + const BRepGraph* aGraph = AttachedGraph(); + if (aGraph != nullptr) + { + const BRepGraphInc::CoEdgeDef& aDef = aGraph->Topo().CoEdges().Definition(theCoEdge); + if (aDef.FaceId.IsValid()) + { + (void)theEntry.FaceTopologyStamp.BindSubtreeGen(*this, BRepGraph_NodeId(aDef.FaceId)); + theEntry.BoundFaceId = aDef.FaceId; + const FaceMeshEntry* aFaceEntry = findFaceEntryRaw(theSlot.Id, aDef.FaceId); + theEntry.FaceMeshGeneration = aFaceEntry != nullptr ? aFaceEntry->MeshGeneration : 0; + } + } + + theEntry.SlotStamp.RecipeHash = theSlot.RecipeHash; + theEntry.SlotStamp.SlotGeneration = theSlot.Generation; +} + +//================================================================================================= + +void BRepGraph_CacheMesh::bindEntry(EdgeMeshEntry& theEntry, + const BRepGraph_EdgeId theEdge, + const Slot& theSlot) const +{ + (void)theEntry.BindSubtreeGen(*this, BRepGraph_NodeId(theEdge)); + theEntry.Stamp.RecipeHash = theSlot.RecipeHash; + theEntry.Stamp.SlotGeneration = theSlot.Generation; +} + +//================================================================================================= + +void BRepGraph_CacheMesh::BindFresh(FaceMeshEntry& theEntry, const BRepGraph_FaceId theFace) const +{ + const Slot* aSlot = findSlot(DefaultDisplaySlot); + if (aSlot != nullptr) + { + bindEntry(theEntry, theFace, *aSlot); + } +} + +//================================================================================================= + +void BRepGraph_CacheMesh::BindFresh(CoEdgeMeshEntry& theEntry, + const BRepGraph_CoEdgeId theCoEdge) const +{ + const Slot* aSlot = findSlot(DefaultDisplaySlot); + if (aSlot != nullptr) + { + bindEntry(theEntry, theCoEdge, *aSlot); + } +} + +//================================================================================================= + +void BRepGraph_CacheMesh::BindFresh(EdgeMeshEntry& theEntry, const BRepGraph_EdgeId theEdge) const +{ + const Slot* aSlot = findSlot(DefaultDisplaySlot); + if (aSlot != nullptr) + { + bindEntry(theEntry, theEdge, *aSlot); + } +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::Ensure(BRepGraph& theGraph, + const SlotId theSlot, + const Message_ProgressRange& theRange) +{ + Slot& aSlot = changeSlot(theSlot); + if (aSlot.MeshDriver.IsNull()) + { + return false; + } + + const uint64_t aRecipeHash = aSlot.MeshDriver->RecipeHash(); + if (aSlot.RecipeHash != aRecipeHash) + { + aSlot.RecipeHash = aRecipeHash; + aSlot.Clear(); + } + + DirtySet aDirtySet = collectDirty(theGraph, aSlot); + if (aDirtySet.IsEmpty()) + { + return true; + } + return aSlot.MeshDriver->Fill(theGraph, theSlot, aDirtySet, theRange); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::Ensure(BRepGraph& theGraph, + const BRepGraph_NodeId theRoot, + const SlotId theSlot, + const Message_ProgressRange& theRange) +{ + Slot& aSlot = changeSlot(theSlot); + if (aSlot.MeshDriver.IsNull()) + { + return false; + } + + const uint64_t aRecipeHash = aSlot.MeshDriver->RecipeHash(); + if (aSlot.RecipeHash != aRecipeHash) + { + aSlot.RecipeHash = aRecipeHash; + aSlot.Clear(); + } + + DirtySet aDirtySet = collectDirty(theGraph, theRoot, aSlot); + if (aDirtySet.IsEmpty()) + { + return true; + } + return aSlot.MeshDriver->Fill(theGraph, theSlot, aDirtySet, theRange); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::Ensure(BRepGraph& theGraph, + const NCollection_Array1& theNodes, + const SlotId theSlot, + const Message_ProgressRange& theRange) +{ + Slot& aSlot = changeSlot(theSlot); + if (aSlot.MeshDriver.IsNull()) + { + return false; + } + + const uint64_t aRecipeHash = aSlot.MeshDriver->RecipeHash(); + if (aSlot.RecipeHash != aRecipeHash) + { + aSlot.RecipeHash = aRecipeHash; + aSlot.Clear(); + } + + DirtySet aDirtySet = collectDirty(theGraph, theNodes, aSlot); + if (aDirtySet.IsEmpty()) + { + return true; + } + return aSlot.MeshDriver->Fill(theGraph, theSlot, aDirtySet, theRange); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::Needs(BRepGraph& theGraph, + const BRepGraph_NodeId theRoot, + const SlotId theSlot) const +{ + const Slot* aSlot = findSlot(theSlot); + if (aSlot == nullptr) + { + return true; + } + if (!aSlot->MeshDriver.IsNull() && aSlot->RecipeHash != aSlot->MeshDriver->RecipeHash()) + { + return true; + } + return !collectDirty(theGraph, theRoot, *aSlot).IsEmpty(); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::HasFaceMesh(const BRepGraph_FaceId theFace) const +{ + return HasFaceMesh(DefaultDisplaySlot, theFace); +} + +//================================================================================================= + +const BRepGraph_CacheMesh::FaceMeshEntry* BRepGraph_CacheMesh::FindFaceMesh( + const BRepGraph_FaceId theFace) const +{ + return FindFaceMesh(DefaultDisplaySlot, theFace); +} + +//================================================================================================= + +BRepGraph_CacheMesh::FaceMeshEntry& BRepGraph_CacheMesh::ChangeFaceMesh( + const BRepGraph_FaceId theFace) +{ + return ChangeFaceMesh(DefaultDisplaySlot, theFace); +} + +//================================================================================================= + +void BRepGraph_CacheMesh::ClearFaceMesh(const BRepGraph_FaceId theFace) +{ + ClearFaceMesh(DefaultDisplaySlot, theFace); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::HasCoEdgeMesh(const BRepGraph_CoEdgeId theCoEdge) const +{ + return findCoEdgeMesh(DefaultDisplaySlot, theCoEdge) != nullptr; +} + +//================================================================================================= + +//================================================================================================= + +BRepGraph_CacheMesh::CoEdgeMeshEntry& BRepGraph_CacheMesh::ChangeCoEdgeMesh( + const BRepGraph_CoEdgeId theCoEdge) +{ + return ChangeCoEdgeMesh(DefaultDisplaySlot, theCoEdge); +} + +//================================================================================================= + +void BRepGraph_CacheMesh::ClearCoEdgeMesh(const BRepGraph_CoEdgeId theCoEdge) +{ + ClearCoEdgeMesh(DefaultDisplaySlot, theCoEdge); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::HasEdgeMesh(const BRepGraph_EdgeId theEdge) const +{ + return HasEdgeMesh(DefaultDisplaySlot, theEdge); +} + +//================================================================================================= + +const BRepGraph_CacheMesh::EdgeMeshEntry* BRepGraph_CacheMesh::FindEdgeMesh( + const BRepGraph_EdgeId theEdge) const +{ + return FindEdgeMesh(DefaultDisplaySlot, theEdge); +} + +//================================================================================================= + +BRepGraph_CacheMesh::EdgeMeshEntry& BRepGraph_CacheMesh::ChangeEdgeMesh( + const BRepGraph_EdgeId theEdge) +{ + return ChangeEdgeMesh(DefaultDisplaySlot, theEdge); +} + +//================================================================================================= + +void BRepGraph_CacheMesh::ClearEdgeMesh(const BRepGraph_EdgeId theEdge) +{ + ClearEdgeMesh(DefaultDisplaySlot, theEdge); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::HasFaceMesh(const SlotId theSlot, const BRepGraph_FaceId theFace) const +{ + return FindFaceMesh(theSlot, theFace) != nullptr; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::FaceMeshEntry* BRepGraph_CacheMesh::FindFaceMesh( + const SlotId theSlot, + const BRepGraph_FaceId theFace) const +{ + const Slot* aSlot = findSlot(theSlot); + if (aSlot == nullptr || !theFace.IsValidIn(aSlot->FaceMeshes)) + { + return nullptr; + } + + const FaceMeshEntry& anEntry = aSlot->FaceMeshes.Value(static_cast(theFace.Index)); + if (!anEntry.IsPresent() || !isSlotActual(*aSlot, anEntry.Stamp) || !anEntry.IsFresh(*this)) + { + return nullptr; + } + return &anEntry; +} + +//================================================================================================= + +BRepGraph_CacheMesh::FaceMeshEntry& BRepGraph_CacheMesh::ChangeFaceMesh( + const SlotId theSlot, + const BRepGraph_FaceId theFace) +{ + Slot& aSlot = changeSlot(theSlot); + ensureSize(aSlot.FaceMeshes, static_cast(theFace.Index)); + return aSlot.FaceMeshes.ChangeValue(static_cast(theFace.Index)); +} + +//================================================================================================= + +void BRepGraph_CacheMesh::ClearFaceMesh(const SlotId theSlot, const BRepGraph_FaceId theFace) +{ + Slot* aSlot = const_cast(findSlot(theSlot)); + if (aSlot != nullptr && theFace.IsValidIn(aSlot->FaceMeshes)) + { + aSlot->FaceMeshes.ChangeValue(static_cast(theFace.Index)).Reset(); + } +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::HasCoEdgeMesh(const SlotId theSlot, + const BRepGraph_CoEdgeId theCoEdge) const +{ + return findCoEdgeMesh(theSlot, theCoEdge) != nullptr; +} + +//================================================================================================= + +//================================================================================================= + +bool BRepGraph_CacheMesh::isCoEdgePolygon2DFresh(const CoEdgeMeshEntry& theEntry, + const Slot& theSlot) const noexcept +{ + return isSlotActual(theSlot, theEntry.SlotStamp) && theEntry.CoEdgeStamp.IsFresh(*this); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::isCoEdgePolygonOnTriFresh(const CoEdgeMeshEntry& theEntry, + const Slot& theSlot) const noexcept +{ + if (!isSlotActual(theSlot, theEntry.SlotStamp) || !theEntry.CoEdgeStamp.IsFresh(*this)) + { + return false; + } + if (!theEntry.FaceTopologyStamp.IsBound()) + { + return true; // free coedge: no face dependency + } + return theEntry.FaceTopologyStamp.IsFresh(*this) && isFaceMeshFresh(theEntry, theSlot); +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::isFaceMeshFresh(const CoEdgeMeshEntry& theEntry, + const Slot& theSlot) const noexcept +{ + if (!theEntry.BoundFaceId.IsValid()) + { + return true; // no face bound + } + if (!theEntry.BoundFaceId.IsValidIn(theSlot.FaceMeshes)) + { + return false; // face entry doesn't exist + } + const FaceMeshEntry& aFaceEntry = + theSlot.FaceMeshes.Value(static_cast(theEntry.BoundFaceId.Index)); + return aFaceEntry.MeshGeneration == theEntry.FaceMeshGeneration; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph_CacheMesh::findCoEdgeEntryRaw( + const SlotId theSlot, + const BRepGraph_CoEdgeId theCoEdge) const +{ + const Slot* aSlot = findSlot(theSlot); + if (aSlot == nullptr || !theCoEdge.IsValidIn(aSlot->CoEdgeMeshes)) + { + return nullptr; + } + const CoEdgeMeshEntry& anEntry = aSlot->CoEdgeMeshes.Value(static_cast(theCoEdge.Index)); + return anEntry.IsPresent() ? &anEntry : nullptr; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph_CacheMesh::FindCoEdgePolygon2D( + const SlotId theSlot, + const BRepGraph_CoEdgeId theCoEdge) const +{ + const Slot* aSlot = findSlot(theSlot); + if (aSlot == nullptr || !theCoEdge.IsValidIn(aSlot->CoEdgeMeshes)) + { + return nullptr; + } + const CoEdgeMeshEntry& anEntry = aSlot->CoEdgeMeshes.Value(static_cast(theCoEdge.Index)); + if (!anEntry.IsPresent() || !isCoEdgePolygon2DFresh(anEntry, *aSlot)) + { + return nullptr; + } + return &anEntry; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph_CacheMesh::FindCoEdgePolygonOnTri( + const SlotId theSlot, + const BRepGraph_CoEdgeId theCoEdge) const +{ + const Slot* aSlot = findSlot(theSlot); + if (aSlot == nullptr || !theCoEdge.IsValidIn(aSlot->CoEdgeMeshes)) + { + return nullptr; + } + const CoEdgeMeshEntry& anEntry = aSlot->CoEdgeMeshes.Value(static_cast(theCoEdge.Index)); + if (!anEntry.IsPresent() || !isCoEdgePolygonOnTriFresh(anEntry, *aSlot)) + { + return nullptr; + } + return &anEntry; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph_CacheMesh::FindCoEdgePolygon2D( + const BRepGraph_CoEdgeId theCoEdge) const +{ + return FindCoEdgePolygon2D(DefaultDisplaySlot, theCoEdge); +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph_CacheMesh::FindCoEdgePolygonOnTri( + const BRepGraph_CoEdgeId theCoEdge) const +{ + return FindCoEdgePolygonOnTri(DefaultDisplaySlot, theCoEdge); +} + +//================================================================================================= + +BRepGraph_CacheMesh::CoEdgeMeshEntry& BRepGraph_CacheMesh::ChangeCoEdgeMesh( + const SlotId theSlot, + const BRepGraph_CoEdgeId theCoEdge) +{ + Slot& aSlot = changeSlot(theSlot); + ensureSize(aSlot.CoEdgeMeshes, static_cast(theCoEdge.Index)); + return aSlot.CoEdgeMeshes.ChangeValue(static_cast(theCoEdge.Index)); +} + +//================================================================================================= + +void BRepGraph_CacheMesh::ClearCoEdgeMesh(const SlotId theSlot, const BRepGraph_CoEdgeId theCoEdge) +{ + Slot* aSlot = const_cast(findSlot(theSlot)); + if (aSlot != nullptr && theCoEdge.IsValidIn(aSlot->CoEdgeMeshes)) + { + aSlot->CoEdgeMeshes.ChangeValue(static_cast(theCoEdge.Index)).Reset(); + } +} + +//================================================================================================= + +bool BRepGraph_CacheMesh::HasEdgeMesh(const SlotId theSlot, const BRepGraph_EdgeId theEdge) const +{ + return FindEdgeMesh(theSlot, theEdge) != nullptr; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::EdgeMeshEntry* BRepGraph_CacheMesh::FindEdgeMesh( + const SlotId theSlot, + const BRepGraph_EdgeId theEdge) const +{ + const Slot* aSlot = findSlot(theSlot); + if (aSlot == nullptr || !theEdge.IsValidIn(aSlot->EdgeMeshes)) + { + return nullptr; + } + + const EdgeMeshEntry& anEntry = aSlot->EdgeMeshes.Value(static_cast(theEdge.Index)); + if (!anEntry.IsPresent() || !isSlotActual(*aSlot, anEntry.Stamp) || !anEntry.IsFresh(*this)) + { + return nullptr; + } + return &anEntry; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph_CacheMesh::findCoEdgeMesh( + const SlotId theSlot, + const BRepGraph_CoEdgeId theCoEdge) const +{ + const Slot* aSlot = findSlot(theSlot); + if (aSlot == nullptr || !theCoEdge.IsValidIn(aSlot->CoEdgeMeshes)) + { + return nullptr; + } + + const CoEdgeMeshEntry& anEntry = aSlot->CoEdgeMeshes.Value(static_cast(theCoEdge.Index)); + if (!anEntry.IsPresent() || !isSlotActual(*aSlot, anEntry.SlotStamp) + || !anEntry.CoEdgeStamp.IsFresh(*this)) + { + return nullptr; + } + return &anEntry; +} + +//================================================================================================= + +BRepGraph_CacheMesh::EdgeMeshEntry& BRepGraph_CacheMesh::ChangeEdgeMesh( + const SlotId theSlot, + const BRepGraph_EdgeId theEdge) +{ + Slot& aSlot = changeSlot(theSlot); + ensureSize(aSlot.EdgeMeshes, static_cast(theEdge.Index)); + return aSlot.EdgeMeshes.ChangeValue(static_cast(theEdge.Index)); +} + +//================================================================================================= + +void BRepGraph_CacheMesh::ClearEdgeMesh(const SlotId theSlot, const BRepGraph_EdgeId theEdge) +{ + Slot* aSlot = const_cast(findSlot(theSlot)); + if (aSlot != nullptr && theEdge.IsValidIn(aSlot->EdgeMeshes)) + { + aSlot->EdgeMeshes.ChangeValue(static_cast(theEdge.Index)).Reset(); + } +} + +//================================================================================================= + +void BRepGraph_CacheMesh::BumpFaceMeshGeneration(const BRepGraph_FaceId theFace, + const SlotId theSlot) +{ + Slot& aSlot = changeSlot(theSlot); + ensureSize(aSlot.FaceMeshes, static_cast(theFace.Index)); + ++aSlot.FaceMeshes.ChangeValue(static_cast(theFace.Index)).MeshGeneration; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::FaceMeshEntry* BRepGraph_CacheMesh::findFaceEntryRaw( + const SlotId theSlot, + const BRepGraph_FaceId theFace) const +{ + const Slot* aSlot = findSlot(theSlot); + if (aSlot == nullptr || !theFace.IsValidIn(aSlot->FaceMeshes)) + { + return nullptr; + } + const FaceMeshEntry& anEntry = aSlot->FaceMeshes.Value(static_cast(theFace.Index)); + return anEntry.IsPresent() ? &anEntry : nullptr; +} + +//================================================================================================= + +BRepGraph_CacheMesh::DirtySet BRepGraph_CacheMesh::collectDirty(BRepGraph& theGraph, + const Slot& theSlot) const +{ + DirtySet aDirtySet; + + for (BRepGraph_FaceId aFaceId = theGraph.Topo().Faces().StartId(); + aFaceId < theGraph.Topo().Faces().EndId(); + ++aFaceId) + { + if (aFaceId.IsRemoved(theGraph)) + { + continue; + } + const FaceMeshEntry* anEntry = aFaceId.IsValidIn(theSlot.FaceMeshes) + ? &theSlot.FaceMeshes.Value(static_cast(aFaceId.Index)) + : nullptr; + if (anEntry == nullptr || !anEntry->IsPresent() || !isSlotActual(theSlot, anEntry->Stamp) + || !anEntry->IsFresh(*this)) + { + aDirtySet.Faces.Append(aFaceId); + } + } + + for (BRepGraph_EdgeId anEdgeId = theGraph.Topo().Edges().StartId(); + anEdgeId < theGraph.Topo().Edges().EndId(); + ++anEdgeId) + { + if (anEdgeId.IsRemoved(theGraph) || theGraph.Topo().Edges().FacesOf(anEdgeId).More()) + { + continue; + } + const EdgeMeshEntry* anEntry = + anEdgeId.IsValidIn(theSlot.EdgeMeshes) + ? &theSlot.EdgeMeshes.Value(static_cast(anEdgeId.Index)) + : nullptr; + if (anEntry == nullptr || !anEntry->IsPresent() || !isSlotActual(theSlot, anEntry->Stamp) + || !anEntry->IsFresh(*this)) + { + aDirtySet.FreeEdges.Append(anEdgeId); + } + } + + // Check coedges for face-mesh-driven staleness on non-dirty faces. + for (BRepGraph_FaceId aFaceId = theGraph.Topo().Faces().StartId(); + aFaceId < theGraph.Topo().Faces().EndId(); + ++aFaceId) + { + if (aFaceId.IsRemoved(theGraph)) + { + continue; + } + bool aFaceAlreadyDirty = false; + for (const auto& aDirtyFace : aDirtySet.Faces) + { + if (aDirtyFace == aFaceId) + { + aFaceAlreadyDirty = true; + break; + } + } + if (aFaceAlreadyDirty) + { + continue; + } + + for (BRepGraph_ChildExplorer aCoEdgeIt(theGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_NodeId::Kind::CoEdge, + BRepGraph_ChildExplorer::TraversalMode::Recursive); + aCoEdgeIt.More(); + aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId(aCoEdgeIt.Current().DefId); + const CoEdgeMeshEntry* aRaw = findCoEdgeEntryRaw(theSlot.Id, aCoEdgeId); + if (aRaw != nullptr && aRaw->IsPresent() && !isCoEdgePolygonOnTriFresh(*aRaw, theSlot)) + { + aDirtySet.Faces.Append(aFaceId); + break; + } + } + } + + return aDirtySet; +} + +//================================================================================================= + +BRepGraph_CacheMesh::DirtySet BRepGraph_CacheMesh::collectDirty(BRepGraph& theGraph, + const BRepGraph_NodeId theRoot, + const Slot& theSlot) const +{ + NCollection_Array1 aNodes(1, 1); + aNodes.SetValue(1, theRoot); + return collectDirty(theGraph, aNodes, theSlot); +} + +//================================================================================================= + +BRepGraph_CacheMesh::DirtySet BRepGraph_CacheMesh::collectDirty( + BRepGraph& theGraph, + const NCollection_Array1& theNodes, + const Slot& theSlot) const +{ + DirtySet aDirtySet; + NCollection_FlatMap aFaces; + NCollection_FlatMap aFreeEdges; + + auto addFace = [&](const BRepGraph_FaceId theFace) { + if (theFace.IsRemoved(theGraph) || !aFaces.Add(theFace)) + { + return; + } + const FaceMeshEntry* anEntry = theFace.IsValidIn(theSlot.FaceMeshes) + ? &theSlot.FaceMeshes.Value(static_cast(theFace.Index)) + : nullptr; + if (anEntry == nullptr || !anEntry->IsPresent() || !isSlotActual(theSlot, anEntry->Stamp) + || !anEntry->IsFresh(*this)) + { + aDirtySet.Faces.Append(theFace); + } + }; + + auto addFreeEdge = [&](const BRepGraph_EdgeId theEdge) { + if (theEdge.IsRemoved(theGraph) || theGraph.Topo().Edges().FacesOf(theEdge).More() + || !aFreeEdges.Add(theEdge)) + { + return; + } + const EdgeMeshEntry* anEntry = theEdge.IsValidIn(theSlot.EdgeMeshes) + ? &theSlot.EdgeMeshes.Value(static_cast(theEdge.Index)) + : nullptr; + if (anEntry == nullptr || !anEntry->IsPresent() || !isSlotActual(theSlot, anEntry->Stamp) + || !anEntry->IsFresh(*this)) + { + aDirtySet.FreeEdges.Append(theEdge); + } + }; + + for (const BRepGraph_NodeId& aNode : theNodes) + { + if (!aNode.IsValid()) + { + continue; + } + + if (aNode.NodeKind == BRepGraph_NodeId::Kind::Face) + { + addFace(BRepGraph_FaceId(aNode)); + } + else if (aNode.NodeKind == BRepGraph_NodeId::Kind::Edge) + { + addFreeEdge(BRepGraph_EdgeId(aNode)); + } + + for (BRepGraph_ChildExplorer aFaceIt(theGraph, aNode, BRepGraph_NodeId::Kind::Face); + aFaceIt.More(); + aFaceIt.Next()) + { + addFace(BRepGraph_FaceId(aFaceIt.Current().DefId)); + } + for (BRepGraph_ChildExplorer anEdgeIt(theGraph, aNode, BRepGraph_NodeId::Kind::Edge); + anEdgeIt.More(); + anEdgeIt.Next()) + { + addFreeEdge(BRepGraph_EdgeId(anEdgeIt.Current().DefId)); + } + } + + // Check coedges of non-dirty faces for face-mesh-driven staleness. + for (NCollection_FlatMap::Iterator aFaceIt(aFaces); aFaceIt.More(); + aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.Value(); + bool aFaceAlreadyDirty = false; + for (const auto& aDirtyFace : aDirtySet.Faces) + { + if (aDirtyFace == aFaceId) + { + aFaceAlreadyDirty = true; + break; + } + } + if (aFaceAlreadyDirty) + { + continue; + } + + for (BRepGraph_ChildExplorer aCoEdgeIt(theGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_NodeId::Kind::CoEdge, + BRepGraph_ChildExplorer::TraversalMode::Recursive); + aCoEdgeIt.More(); + aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId(aCoEdgeIt.Current().DefId); + const CoEdgeMeshEntry* aRaw = findCoEdgeEntryRaw(theSlot.Id, aCoEdgeId); + if (aRaw != nullptr && aRaw->IsPresent() && !isCoEdgePolygonOnTriFresh(*aRaw, theSlot)) + { + aDirtySet.Faces.Append(aFaceId); + break; + } + } + } + + return aDirtySet; +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheMesh.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheMesh.hxx new file mode 100644 index 0000000000..eaa4dac178 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheMesh.hxx @@ -0,0 +1,354 @@ +// 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. + +#ifndef _BRepGraph_CacheMesh_HeaderFile +#define _BRepGraph_CacheMesh_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class BRepGraph; + +//! @brief Registry-owned runtime mesh cache for BRepGraph. +//! +//! CacheMesh stores transient triangulations and polygons produced by meshing drivers. +//! It is not copied, transformed, or serialized. Persistent mesh representations remain +//! in topology definitions and are filled only by import, authored primitive creation, +//! or explicit promotion. +class BRepGraph_CacheMesh : public BRepGraph_Cache +{ +public: + using SlotId = uint32_t; + + static constexpr SlotId DefaultDisplaySlot = 0; + + //! Entry stamp against the slot recipe and cache-local generation. + struct EntryStamp + { + uint64_t RecipeHash = 0; + uint32_t SlotGeneration = 0; + + void Reset() noexcept + { + RecipeHash = 0; + SlotGeneration = 0; + } + }; + + //! Cached mesh entry for a face: single triangulation. + struct FaceMeshEntry : public NodeEntry + { + occ::handle Triangulation; + EntryStamp Stamp; + uint32_t MeshGeneration = 0; + + [[nodiscard]] bool IsPresent() const { return !Triangulation.IsNull(); } + + //! Clear triangulation representation. Does NOT bump MeshGeneration - + //! callers must call BRepGraph_CacheMesh::BumpFaceMeshGeneration() separately. + void ClearRepresentation() noexcept { Triangulation.Nullify(); } + + void Reset() noexcept + { + NodeEntry::Reset(); + Triangulation.Nullify(); + Stamp.Reset(); + MeshGeneration = 0; + } + }; + + //! Cached mesh entry for a coedge: polygon-in-parametric-space and polygon-on-triangulation. + //! + //! CoEdgeMeshEntry composes two NodeEntry fields instead of inheriting from one. + //! This breaks the inheritance pattern used by other entries because the coedge + //! has two independent freshness dimensions: coedge topology and face mesh content. + struct CoEdgeMeshEntry + { + //! Coedge topology freshness (for Polygon2D). + NodeEntry CoEdgeStamp; + + //! Face topology freshness (for PolygonsOnTri). + NodeEntry FaceTopologyStamp; + + //! Face mesh content freshness (for PolygonsOnTri). + uint32_t FaceMeshGeneration = 0; + //! Face whose MeshGeneration we track. + BRepGraph_FaceId BoundFaceId; + + //! Slot recipe freshness. + EntryStamp SlotStamp; + + //! Cached polygon-on-surface. + occ::handle Polygon2D; + //! Cached polygons-on-triangulation. + NCollection_LinearVector> PolygonsOnTri; + + [[nodiscard]] bool IsPresent() const { return !Polygon2D.IsNull() || !PolygonsOnTri.IsEmpty(); } + + void Reset() noexcept + { + CoEdgeStamp.Reset(); + FaceTopologyStamp.Reset(); + FaceMeshGeneration = 0; + BoundFaceId = BRepGraph_FaceId(); + SlotStamp.Reset(); + Polygon2D.Nullify(); + PolygonsOnTri.Clear(); + } + }; + + //! Cached mesh entry for an edge: polygon-3D. + struct EdgeMeshEntry : public NodeEntry + { + occ::handle Polygon3D; + EntryStamp Stamp; + + [[nodiscard]] bool IsPresent() const { return !Polygon3D.IsNull(); } + + void Reset() noexcept + { + NodeEntry::Reset(); + Polygon3D.Nullify(); + Stamp.Reset(); + } + }; + + //! Dirty topology set requested from a cache driver. + struct DirtySet + { + NCollection_LinearVector Faces; + NCollection_LinearVector FreeEdges; + + [[nodiscard]] bool IsEmpty() const { return Faces.IsEmpty() && FreeEdges.IsEmpty(); } + + void Clear() + { + Faces.Clear(); + FreeEdges.Clear(); + } + }; + + //! Runtime state of a cache slot. + struct SlotState + { + SlotId Slot = DefaultDisplaySlot; + uint64_t RecipeHash = 0; + uint32_t Generation = 1; + bool HasDriver = false; + }; + + //! Mesh recomputation driver registered by a meshing toolkit. + class Driver : public Standard_Transient + { + public: + [[nodiscard]] virtual const Standard_GUID& ID() const = 0; + [[nodiscard]] virtual uint64_t RecipeHash() const = 0; + + [[nodiscard]] virtual bool Fill(BRepGraph& theGraph, + SlotId theSlot, + const DirtySet& theDirtySet, + const Message_ProgressRange& theRange) = 0; + + DEFINE_STANDARD_RTTIEXT(Driver, Standard_Transient) + }; + + Standard_EXPORT BRepGraph_CacheMesh(); + + BRepGraph_CacheMesh(const BRepGraph_CacheMesh&) = delete; + BRepGraph_CacheMesh& operator=(const BRepGraph_CacheMesh&) = delete; + + //! Returns the unique cache service GUID. + [[nodiscard]] static Standard_EXPORT const Standard_GUID& GetID(); + + //! Returns the unique cache service GUID. + [[nodiscard]] Standard_EXPORT const Standard_GUID& ID() const override; + + //! Returns the cache service display name. + [[nodiscard]] Standard_EXPORT const TCollection_AsciiString& Name() const override; + + //! Clears all cache slots and registered drivers. + Standard_EXPORT void Clear() noexcept override; + + //! Copy fresh, remappable mesh cache entries into the target graph. + Standard_EXPORT void CopyFreshTo(const BRepGraph_CopyRemap& theCopy) const override; + + //! Get/set the currently active display slot. + //! EffectiveView reads from this slot. + [[nodiscard]] SlotId ActiveDisplaySlot() const { return myActiveSlot; } + + void SetActiveDisplaySlot(SlotId theSlot) { myActiveSlot = theSlot; } + + //! Register or replace a meshing driver for a cache slot. + Standard_EXPORT void RegisterDriver(SlotId theSlot, const occ::handle& theDriver); + + //! Remove a meshing driver from a cache slot and invalidate the slot. + Standard_EXPORT void UnregisterDriver(SlotId theSlot); + + //! Return driver registered for a slot, or null. + [[nodiscard]] Standard_EXPORT const occ::handle& DriverOf(SlotId theSlot) const; + + //! Return current state of a cache slot. + [[nodiscard]] Standard_EXPORT SlotState State(SlotId theSlot = DefaultDisplaySlot) const; + + //! Recompute stale data in a slot using its registered driver. + [[nodiscard]] Standard_EXPORT bool Ensure( + BRepGraph& theGraph, + SlotId theSlot = DefaultDisplaySlot, + const Message_ProgressRange& theRange = Message_ProgressRange()); + + //! Recompute stale data below a topology node using the slot driver. + [[nodiscard]] Standard_EXPORT bool Ensure( + BRepGraph& theGraph, + const BRepGraph_NodeId theRoot, + SlotId theSlot = DefaultDisplaySlot, + const Message_ProgressRange& theRange = Message_ProgressRange()); + + //! Recompute stale data for requested topology nodes using the slot driver. + [[nodiscard]] Standard_EXPORT bool Ensure( + BRepGraph& theGraph, + const NCollection_Array1& theNodes, + SlotId theSlot = DefaultDisplaySlot, + const Message_ProgressRange& theRange = Message_ProgressRange()); + + //! Return true when a cache slot has stale or missing mesh below the topology node. + //! Uses the same actualness rules as Ensure() but does not invoke the driver. + [[nodiscard]] Standard_EXPORT bool Needs(BRepGraph& theGraph, + BRepGraph_NodeId theRoot, + SlotId theSlot = DefaultDisplaySlot) const; + + [[nodiscard]] Standard_EXPORT bool HasFaceMesh(BRepGraph_FaceId theFace) const; + [[nodiscard]] Standard_EXPORT const FaceMeshEntry* FindFaceMesh(BRepGraph_FaceId theFace) const; + [[nodiscard]] Standard_EXPORT FaceMeshEntry& ChangeFaceMesh(BRepGraph_FaceId theFace); + Standard_EXPORT void ClearFaceMesh(BRepGraph_FaceId theFace); + + [[nodiscard]] Standard_EXPORT bool HasCoEdgeMesh(BRepGraph_CoEdgeId theCoEdge) const; + [[nodiscard]] Standard_EXPORT CoEdgeMeshEntry& ChangeCoEdgeMesh(BRepGraph_CoEdgeId theCoEdge); + Standard_EXPORT void ClearCoEdgeMesh(BRepGraph_CoEdgeId theCoEdge); + + [[nodiscard]] Standard_EXPORT bool HasEdgeMesh(BRepGraph_EdgeId theEdge) const; + [[nodiscard]] Standard_EXPORT const EdgeMeshEntry* FindEdgeMesh(BRepGraph_EdgeId theEdge) const; + [[nodiscard]] Standard_EXPORT EdgeMeshEntry& ChangeEdgeMesh(BRepGraph_EdgeId theEdge); + Standard_EXPORT void ClearEdgeMesh(BRepGraph_EdgeId theEdge); + + [[nodiscard]] Standard_EXPORT bool HasFaceMesh(SlotId theSlot, BRepGraph_FaceId theFace) const; + [[nodiscard]] Standard_EXPORT const FaceMeshEntry* FindFaceMesh(SlotId theSlot, + BRepGraph_FaceId theFace) const; + [[nodiscard]] Standard_EXPORT FaceMeshEntry& ChangeFaceMesh(SlotId theSlot, + BRepGraph_FaceId theFace); + Standard_EXPORT void ClearFaceMesh(SlotId theSlot, BRepGraph_FaceId theFace); + + [[nodiscard]] Standard_EXPORT bool HasCoEdgeMesh(SlotId theSlot, + BRepGraph_CoEdgeId theCoEdge) const; + [[nodiscard]] Standard_EXPORT CoEdgeMeshEntry& ChangeCoEdgeMesh(SlotId theSlot, + BRepGraph_CoEdgeId theCoEdge); + Standard_EXPORT void ClearCoEdgeMesh(SlotId theSlot, BRepGraph_CoEdgeId theCoEdge); + + [[nodiscard]] Standard_EXPORT bool HasEdgeMesh(SlotId theSlot, BRepGraph_EdgeId theEdge) const; + [[nodiscard]] Standard_EXPORT const EdgeMeshEntry* FindEdgeMesh(SlotId theSlot, + BRepGraph_EdgeId theEdge) const; + [[nodiscard]] Standard_EXPORT EdgeMeshEntry& ChangeEdgeMesh(SlotId theSlot, + BRepGraph_EdgeId theEdge); + Standard_EXPORT void ClearEdgeMesh(SlotId theSlot, BRepGraph_EdgeId theEdge); + + //! Stamp a freshly written default-slot entry. + Standard_EXPORT void BindFresh(FaceMeshEntry& theEntry, BRepGraph_FaceId theFace) const; + Standard_EXPORT void BindFresh(CoEdgeMeshEntry& theEntry, BRepGraph_CoEdgeId theCoEdge) const; + Standard_EXPORT void BindFresh(EdgeMeshEntry& theEntry, BRepGraph_EdgeId theEdge) const; + + //! Bump face mesh generation after cached content changed. + //! This is the ONLY mutator for MeshGeneration. ClearRepresentation() does not bump. + //! Creates the face entry if it doesn't exist yet (via ensureSize). + Standard_EXPORT void BumpFaceMeshGeneration(BRepGraph_FaceId theFace, + SlotId theSlot = DefaultDisplaySlot); + + //! Raw face entry access (no freshness filtering). For internal use. + [[nodiscard]] Standard_EXPORT const FaceMeshEntry* findFaceEntryRaw( + SlotId theSlot, + BRepGraph_FaceId theFace) const; + + //! Raw coedge entry access (no freshness/generation filtering). + //! Returns nullptr if the slot is absent or the entry has no representation. + //! For internal use. + [[nodiscard]] Standard_EXPORT const CoEdgeMeshEntry* findCoEdgeEntryRaw( + SlotId theSlot, + BRepGraph_CoEdgeId theCoEdge) const; + + //! Return coedge entry if Polygon2D is fresh, nullptr otherwise. + [[nodiscard]] Standard_EXPORT const CoEdgeMeshEntry* FindCoEdgePolygon2D( + SlotId theSlot, + BRepGraph_CoEdgeId theCoEdge) const; + + //! Return coedge entry if PolygonsOnTri is fresh, nullptr otherwise. + [[nodiscard]] Standard_EXPORT const CoEdgeMeshEntry* FindCoEdgePolygonOnTri( + SlotId theSlot, + BRepGraph_CoEdgeId theCoEdge) const; + + //! Return coedge entry if Polygon2D is fresh (default slot), nullptr otherwise. + [[nodiscard]] Standard_EXPORT const CoEdgeMeshEntry* FindCoEdgePolygon2D( + BRepGraph_CoEdgeId theCoEdge) const; + + //! Return coedge entry if PolygonsOnTri is fresh (default slot), nullptr otherwise. + [[nodiscard]] Standard_EXPORT const CoEdgeMeshEntry* FindCoEdgePolygonOnTri( + BRepGraph_CoEdgeId theCoEdge) const; + + DEFINE_STANDARD_RTTIEXT(BRepGraph_CacheMesh, BRepGraph_Cache) + +private: + struct Slot; + + template + static void ensureSize(NCollection_DynamicArray& theVec, size_t theIndex); + + [[nodiscard]] Slot& changeSlot(SlotId theSlot); + [[nodiscard]] const Slot* findSlot(SlotId theSlot) const; + + [[nodiscard]] bool isSlotActual(const Slot& theSlot, const EntryStamp& theStamp) const noexcept; + void bindEntry(FaceMeshEntry& theEntry, BRepGraph_FaceId theFace, const Slot& theSlot) const; + void bindEntry(CoEdgeMeshEntry& theEntry, + BRepGraph_CoEdgeId theCoEdge, + const Slot& theSlot) const; + void bindEntry(EdgeMeshEntry& theEntry, BRepGraph_EdgeId theEdge, const Slot& theSlot) const; + + [[nodiscard]] bool isCoEdgePolygon2DFresh(const CoEdgeMeshEntry& theEntry, + const Slot& theSlot) const noexcept; + [[nodiscard]] bool isCoEdgePolygonOnTriFresh(const CoEdgeMeshEntry& theEntry, + const Slot& theSlot) const noexcept; + [[nodiscard]] bool isFaceMeshFresh(const CoEdgeMeshEntry& theEntry, + const Slot& theSlot) const noexcept; + + [[nodiscard]] const CoEdgeMeshEntry* findCoEdgeMesh(SlotId theSlot, + BRepGraph_CoEdgeId theCoEdge) const; + + [[nodiscard]] DirtySet collectDirty(BRepGraph& theGraph, const Slot& theSlot) const; + [[nodiscard]] DirtySet collectDirty(BRepGraph& theGraph, + const BRepGraph_NodeId theRoot, + const Slot& theSlot) const; + [[nodiscard]] DirtySet collectDirty(BRepGraph& theGraph, + const NCollection_Array1& theNodes, + const Slot& theSlot) const; + + NCollection_LinearVector mySlots; + SlotId myActiveSlot = DefaultDisplaySlot; +}; + +#endif // _BRepGraph_CacheMesh_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheRegistry.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheRegistry.cxx new file mode 100644 index 0000000000..5577b0733a --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheRegistry.cxx @@ -0,0 +1,299 @@ +// 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 + +#include +#include +#include + +#include +#include + +//================================================================================================= + +BRepGraph_CacheRegistry::BRepGraph_CacheRegistry() = default; + +//================================================================================================= + +BRepGraph_CacheRegistry::BRepGraph_CacheRegistry(BRepGraph_CacheRegistry&& theOther) noexcept +{ + std::unique_lock aLock(theOther.myMutex); + myCaches = std::move(theOther.myCaches); + myGuidToSlot = std::move(theOther.myGuidToSlot); + myGraph = theOther.myGraph; + theOther.myGraph = nullptr; +} + +//================================================================================================= + +BRepGraph_CacheRegistry& BRepGraph_CacheRegistry::operator=( + BRepGraph_CacheRegistry&& theOther) noexcept +{ + if (this != &theOther) + { + std::unique_lock aThisLock(myMutex, std::defer_lock); + std::unique_lock anOtherLock(theOther.myMutex, std::defer_lock); + std::lock(aThisLock, anOtherLock); + + detachAllLocked(); + myCaches = std::move(theOther.myCaches); + myGuidToSlot = std::move(theOther.myGuidToSlot); + myGraph = theOther.myGraph; + theOther.myGraph = nullptr; + } + return *this; +} + +//================================================================================================= + +void BRepGraph_CacheRegistry::Attach(BRepGraph* theGraph) noexcept +{ + std::unique_lock aLock(myMutex); + myGraph = theGraph; + for (const occ::handle& aCache : myCaches) + { + if (!aCache.IsNull()) + { + aCache->rebindGraph(myGraph); + } + } +} + +//================================================================================================= + +void BRepGraph_CacheRegistry::Detach() noexcept +{ + std::unique_lock aLock(myMutex); + detachAllLocked(); + myGraph = nullptr; +} + +//================================================================================================= + +uint32_t BRepGraph_CacheRegistry::RegisterCache(const occ::handle& theCache) +{ + std::unique_lock aLock(myMutex); + return registerCacheLocked(theCache); +} + +//================================================================================================= + +void BRepGraph_CacheRegistry::UnregisterCache(const Standard_GUID& theGUID) +{ + std::unique_lock aLock(myMutex); + const uint32_t* aSlotPtr = myGuidToSlot.Seek(theGUID); + if (aSlotPtr == nullptr) + { + return; + } + + const uint32_t aSlot = *aSlotPtr; + const uint32_t aLastSlot = static_cast(myCaches.Size() - 1); + const occ::handle aRemoved = myCaches.Value(static_cast(aSlot)); + if (aSlot != aLastSlot) + { + const occ::handle& aLastCache = myCaches.Value(static_cast(aLastSlot)); + myCaches.ChangeValue(static_cast(aSlot)) = aLastCache; + myGuidToSlot.ChangeFind(aLastCache->ID()) = aSlot; + } + + myCaches.EraseLast(); + myGuidToSlot.UnBind(theGUID); + if (!aRemoved.IsNull()) + { + aRemoved->detachGraph(); + } +} + +//================================================================================================= + +occ::handle BRepGraph_CacheRegistry::FindCache(const Standard_GUID& theGUID) const +{ + std::shared_lock aLock(myMutex); + return findCacheLocked(theGUID); +} + +//================================================================================================= + +bool BRepGraph_CacheRegistry::FindSlot(const Standard_GUID& theGUID, uint32_t& theSlot) const +{ + std::shared_lock aLock(myMutex); + const uint32_t* aSlot = myGuidToSlot.Seek(theGUID); + if (aSlot == nullptr) + { + return false; + } + theSlot = *aSlot; + return true; +} + +//================================================================================================= + +occ::handle BRepGraph_CacheRegistry::Cache(const uint32_t theSlot) const +{ + std::shared_lock aLock(myMutex); + if (static_cast(theSlot) >= myCaches.Size()) + { + return occ::handle(); + } + return myCaches.Value(static_cast(theSlot)); +} + +//================================================================================================= + +void BRepGraph_CacheRegistry::Clear() noexcept +{ + std::unique_lock aLock(myMutex); + detachAllLocked(); + myCaches.Clear(); + myGuidToSlot.Clear(); +} + +//================================================================================================= + +uint32_t BRepGraph_CacheRegistry::Register(const occ::handle& theCache) +{ + return RegisterCache(theCache); +} + +//================================================================================================= + +bool BRepGraph_CacheRegistry::FindSlot(const occ::handle& theCache, + uint32_t& theSlot) const +{ + return !theCache.IsNull() && FindSlot(theCache->ID(), theSlot); +} + +//================================================================================================= + +BRepGraph_CacheIterator BRepGraph_CacheRegistry::CacheIter() const +{ + return BRepGraph_CacheIterator(*this); +} + +//================================================================================================= + +void BRepGraph_CacheRegistry::ClearAll() noexcept +{ + for (uint32_t aSlot = 0;; ++aSlot) + { + occ::handle aCache = cacheAt(aSlot); + if (aCache.IsNull()) + { + return; + } + aCache->Clear(); + } +} + +//================================================================================================= + +void BRepGraph_CacheRegistry::CopyFreshCachesTo( + BRepGraph& theTargetGraph, + const NCollection_FlatDataMap& theItemRemap, + const BRepGraph_CopyRemap::Mode theMode) const +{ + BRepGraph* aSourceGraph = nullptr; + { + std::shared_lock aLock(myMutex); + aSourceGraph = myGraph; + } + if (aSourceGraph == nullptr) + { + return; + } + + const BRepGraph_CopyRemap aCopy(*aSourceGraph, theTargetGraph, theItemRemap, theMode); + for (uint32_t aSlot = 0;; ++aSlot) + { + occ::handle aCache = cacheAt(aSlot); + if (aCache.IsNull()) + { + return; + } + aCache->CopyFreshTo(aCopy); + } +} + +//================================================================================================= + +occ::handle BRepGraph_CacheRegistry::findCacheLocked( + const Standard_GUID& theGUID) const +{ + const uint32_t* aSlot = myGuidToSlot.Seek(theGUID); + if (aSlot == nullptr || static_cast(*aSlot) >= myCaches.Size()) + { + return occ::handle(); + } + return myCaches.Value(static_cast(*aSlot)); +} + +//================================================================================================= + +occ::handle BRepGraph_CacheRegistry::cacheAt(const uint32_t theSlot) const +{ + std::shared_lock aLock(myMutex); + if (static_cast(theSlot) >= myCaches.Size()) + { + return occ::handle(); + } + return myCaches.Value(static_cast(theSlot)); +} + +//================================================================================================= + +uint32_t BRepGraph_CacheRegistry::registerCacheLocked(const occ::handle& theCache) +{ + Standard_ProgramError_Raise_if(theCache.IsNull(), + "BRepGraph_CacheRegistry::RegisterCache() - null cache"); + Standard_ProgramError_Raise_if(theCache->myGraph != nullptr && theCache->myGraph != myGraph, + "BRepGraph_CacheRegistry::RegisterCache() - cache is attached " + "to another graph"); + + if (uint32_t* aSlot = myGuidToSlot.ChangeSeek(theCache->ID())) + { + if (static_cast(*aSlot) < myCaches.Size()) + { + const occ::handle& aPrev = myCaches.Value(static_cast(*aSlot)); + if (!aPrev.IsNull() && aPrev.get() != theCache.get()) + { + aPrev->detachGraph(); + } + theCache->attachGraph(myGraph); + myCaches.ChangeValue(static_cast(*aSlot)) = theCache; + return *aSlot; + } + } + + Standard_OutOfRange_Raise_if(myCaches.Size() > std::numeric_limits::max(), + "BRepGraph_CacheRegistry - too many registered caches"); + const uint32_t aNewSlot = static_cast(myCaches.Size()); + theCache->attachGraph(myGraph); + myCaches.Append(theCache); + myGuidToSlot.Bind(theCache->ID(), aNewSlot); + return aNewSlot; +} + +//================================================================================================= + +void BRepGraph_CacheRegistry::detachAllLocked() noexcept +{ + for (const occ::handle& aCache : myCaches) + { + if (!aCache.IsNull()) + { + aCache->detachGraph(); + } + } +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheRegistry.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheRegistry.hxx new file mode 100644 index 0000000000..8862455928 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheRegistry.hxx @@ -0,0 +1,166 @@ +// 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. + +#ifndef _BRepGraph_CacheRegistry_HeaderFile +#define _BRepGraph_CacheRegistry_HeaderFile + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class BRepGraph; +struct BRepGraph_Data; +class BRepGraph_CacheIterator; + +//! @brief GUID-keyed runtime registry of graph cache services. +//! +//! Stores registered cache services in a stable slot array for O(1) slot access +//! and a GUID-to-slot map for lookup by stable public identity. Cache services +//! own their typed transient data; this registry only manages identity and +//! owner binding. +class BRepGraph_CacheRegistry +{ +public: + DEFINE_STANDARD_ALLOC + + Standard_EXPORT BRepGraph_CacheRegistry(); + + BRepGraph_CacheRegistry(const BRepGraph_CacheRegistry&) = delete; + BRepGraph_CacheRegistry& operator=(const BRepGraph_CacheRegistry&) = delete; + + Standard_EXPORT BRepGraph_CacheRegistry(BRepGraph_CacheRegistry&& theOther) noexcept; + Standard_EXPORT BRepGraph_CacheRegistry& operator=(BRepGraph_CacheRegistry&& theOther) noexcept; + + //! Register a cache service. Replaces an existing cache with the same GUID. + //! @param[in] theCache cache service + //! @return graph-local slot index + Standard_EXPORT uint32_t RegisterCache(const occ::handle& theCache); + + //! Register a cache service. Short form used by graph-local cache operations. + //! @param[in] theCache cache service + //! @return graph-local slot index + Standard_EXPORT uint32_t Register(const occ::handle& theCache); + + //! Remove a cache service by GUID. + //! @param[in] theGUID cache identity + Standard_EXPORT void UnregisterCache(const Standard_GUID& theGUID); + + //! Find a cache service by GUID. + //! @param[in] theGUID cache identity + //! @return cache service, or null handle if not found + [[nodiscard]] Standard_EXPORT occ::handle FindCache( + const Standard_GUID& theGUID) const; + + //! Typed convenience lookup by cache GUID. + template + [[nodiscard]] occ::handle FindCache() const + { + return Find(); + } + + //! Typed lookup by cache GUID. + template + [[nodiscard]] occ::handle Find() const + { + return occ::down_cast(FindCache(T::GetID())); + } + + //! Return an existing cache service or create and register a default one. + template + [[nodiscard]] occ::handle Ensure() + { + std::unique_lock aLock(myMutex); + occ::handle aCache = occ::down_cast(findCacheLocked(T::GetID())); + if (aCache.IsNull()) + { + aCache = new T(); + registerCacheLocked(aCache); + } + return aCache; + } + + //! Return current graph-local slot for a GUID. + //! @param[in] theGUID cache family identity + //! @param[out] theSlot graph-local slot index + //! @return true if the cache service is registered + [[nodiscard]] Standard_EXPORT bool FindSlot(const Standard_GUID& theGUID, + uint32_t& theSlot) const; + + //! Return current graph-local slot for a cache service. + //! @param[in] theCache cache service + //! @param[out] theSlot graph-local slot index + //! @return true if the cache service is registered + [[nodiscard]] Standard_EXPORT bool FindSlot(const occ::handle& theCache, + uint32_t& theSlot) const; + + //! Return cache service by graph-local slot, or null handle if the slot is out of range. + //! @param[in] theSlot graph-local cache slot + [[nodiscard]] Standard_EXPORT occ::handle Cache(uint32_t theSlot) const; + + //! Number of registered cache services. + [[nodiscard]] uint32_t NbCaches() const + { + std::shared_lock aLock(myMutex); + return static_cast(myCaches.Size()); + } + + //! Iterate registered cache services. + [[nodiscard]] Standard_EXPORT BRepGraph_CacheIterator CacheIter() const; + + //! Clear data in all registered cache services. + Standard_EXPORT void ClearAll() noexcept; + + //! Ask registered cache services to copy fresh, remappable data into the target graph. + Standard_EXPORT void CopyFreshCachesTo( + BRepGraph& theTargetGraph, + const NCollection_FlatDataMap& theItemRemap, + const BRepGraph_CopyRemap::Mode theMode) const; + + //! Unregister all cache services. + Standard_EXPORT void Clear() noexcept; + +private: + friend class ::BRepGraph; + friend struct ::BRepGraph_Data; + + //! Attach this registry to graph owner. Propagates context to registered caches. + Standard_EXPORT void Attach(BRepGraph* theGraph) noexcept; + + //! Clear the graph data binding. + Standard_EXPORT void Detach() noexcept; + + [[nodiscard]] Standard_EXPORT occ::handle findCacheLocked( + const Standard_GUID& theGUID) const; + + [[nodiscard]] Standard_EXPORT occ::handle cacheAt(uint32_t theSlot) const; + + Standard_EXPORT uint32_t registerCacheLocked(const occ::handle& theCache); + + Standard_EXPORT void detachAllLocked() noexcept; + + NCollection_LinearVector> myCaches; + NCollection_DataMap myGuidToSlot; + BRepGraph* myGraph = nullptr; + mutable std::shared_mutex myMutex; +}; + +#include + +#endif // _BRepGraph_CacheRegistry_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.cxx deleted file mode 100644 index f9ae8764a7..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.cxx +++ /dev/null @@ -1,291 +0,0 @@ -// 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 -#include -#include - -//================================================================================================= - -void BRepGraph::CacheView::Set(const BRepGraph_NodeId theNode, - const occ::handle& theKind, - const occ::handle& theValue) -{ - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr) - { - return; - } - myGraph->transientCache().Set(theNode, theKind, theValue, aDef->SubtreeGen); -} - -//================================================================================================= - -void BRepGraph::CacheView::Set(const BRepGraph_NodeId theNode, - const int theKindSlot, - const occ::handle& theValue) -{ - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr) - { - return; - } - myGraph->transientCache().Set(theNode, theKindSlot, theValue, aDef->SubtreeGen); -} - -//================================================================================================= - -occ::handle BRepGraph::CacheView::Get( - const BRepGraph_NodeId theNode, - const occ::handle& theKind) const -{ - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr) - { - return occ::handle(); - } - return myGraph->transientCache().Get(theNode, theKind, aDef->SubtreeGen); -} - -//================================================================================================= - -occ::handle BRepGraph::CacheView::Get(const BRepGraph_NodeId theNode, - const int theKindSlot) const -{ - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr) - { - return occ::handle(); - } - return myGraph->transientCache().Get(theNode, theKindSlot, aDef->SubtreeGen); -} - -//================================================================================================= - -bool BRepGraph::CacheView::Has(const BRepGraph_NodeId theNode, - const occ::handle& theKind) const -{ - return !Get(theNode, theKind).IsNull(); -} - -//================================================================================================= - -bool BRepGraph::CacheView::Has(const BRepGraph_NodeId theNode, const int theKindSlot) const -{ - return !Get(theNode, theKindSlot).IsNull(); -} - -//================================================================================================= - -// Remove() intentionally skips IsRemoved checks - stale cache entries -// for removed nodes must still be removable during cleanup. -bool BRepGraph::CacheView::Remove(const BRepGraph_NodeId theNode, - const occ::handle& theKind) -{ - return myGraph->transientCache().Remove(theNode, theKind); -} - -//================================================================================================= - -bool BRepGraph::CacheView::Remove(const BRepGraph_NodeId theNode, const int theKindSlot) -{ - return myGraph->transientCache().Remove(theNode, theKindSlot); -} - -//================================================================================================= - -void BRepGraph::CacheView::Invalidate(const BRepGraph_NodeId theNode, - const occ::handle& theKind) -{ - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr) - { - return; - } - - occ::handle aValue = - myGraph->transientCache().Get(theNode, theKind, aDef->SubtreeGen); - if (!aValue.IsNull()) - { - aValue->Invalidate(); - } -} - -//================================================================================================= - -void BRepGraph::CacheView::Invalidate(const BRepGraph_NodeId theNode, const int theKindSlot) -{ - occ::handle aValue = Get(theNode, theKindSlot); - if (!aValue.IsNull()) - { - aValue->Invalidate(); - } -} - -//================================================================================================= - -BRepGraph_CacheKindIterator BRepGraph::CacheView::CacheKindIter( - const BRepGraph_NodeId theNode) const -{ - BRepGraph_CacheKindIterator anIt; - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr || aDef->IsRemoved) - { - return anIt; - } - - anIt.myCount = - myGraph->transientCache().CollectCacheKindSlots(theNode, aDef->SubtreeGen, anIt.mySlots); - return anIt; -} - -//================================================================================================= - -void BRepGraph::CacheView::Set(const BRepGraph_RefId theRef, - const occ::handle& theKind, - const occ::handle& theValue) -{ - const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRef); - if (aRef == nullptr || aRef->IsRemoved) - { - return; - } - myGraph->refTransientCache().Set(theRef, theKind, theValue, aRef->OwnGen); -} - -//================================================================================================= - -void BRepGraph::CacheView::Set(const BRepGraph_RefId theRef, - const int theKindSlot, - const occ::handle& theValue) -{ - const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRef); - if (aRef == nullptr || aRef->IsRemoved) - { - return; - } - myGraph->refTransientCache().Set(theRef, theKindSlot, theValue, aRef->OwnGen); -} - -//================================================================================================= - -occ::handle BRepGraph::CacheView::Get( - const BRepGraph_RefId theRef, - const occ::handle& theKind) const -{ - const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRef); - if (aRef == nullptr || aRef->IsRemoved) - { - return occ::handle(); - } - return myGraph->refTransientCache().Get(theRef, theKind, aRef->OwnGen); -} - -//================================================================================================= - -occ::handle BRepGraph::CacheView::Get(const BRepGraph_RefId theRef, - const int theKindSlot) const -{ - const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRef); - if (aRef == nullptr || aRef->IsRemoved) - { - return occ::handle(); - } - return myGraph->refTransientCache().Get(theRef, theKindSlot, aRef->OwnGen); -} - -//================================================================================================= - -bool BRepGraph::CacheView::Has(const BRepGraph_RefId theRef, - const occ::handle& theKind) const -{ - return !Get(theRef, theKind).IsNull(); -} - -//================================================================================================= - -bool BRepGraph::CacheView::Has(const BRepGraph_RefId theRef, const int theKindSlot) const -{ - return !Get(theRef, theKindSlot).IsNull(); -} - -//================================================================================================= - -// Remove() intentionally skips IsRemoved checks - stale cache entries -// for removed refs must still be removable during cleanup. -bool BRepGraph::CacheView::Remove(const BRepGraph_RefId theRef, - const occ::handle& theKind) -{ - return myGraph->refTransientCache().Remove(theRef, theKind); -} - -//================================================================================================= - -bool BRepGraph::CacheView::Remove(const BRepGraph_RefId theRef, const int theKindSlot) -{ - return myGraph->refTransientCache().Remove(theRef, theKindSlot); -} - -//================================================================================================= - -void BRepGraph::CacheView::Invalidate(const BRepGraph_RefId theRef, - const occ::handle& theKind) -{ - const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRef); - if (aRef == nullptr || aRef->IsRemoved) - { - return; - } - - occ::handle aValue = - myGraph->refTransientCache().Get(theRef, theKind, aRef->OwnGen); - if (!aValue.IsNull()) - { - aValue->Invalidate(); - } -} - -//================================================================================================= - -void BRepGraph::CacheView::Invalidate(const BRepGraph_RefId theRef, const int theKindSlot) -{ - const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRef); - if (aRef == nullptr || aRef->IsRemoved) - { - return; - } - - occ::handle aValue = - myGraph->refTransientCache().Get(theRef, theKindSlot, aRef->OwnGen); - if (!aValue.IsNull()) - { - aValue->Invalidate(); - } -} - -//================================================================================================= - -BRepGraph_CacheKindIterator BRepGraph::CacheView::CacheKindIter( - const BRepGraph_RefId theRef) const -{ - BRepGraph_CacheKindIterator anIt; - const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRef); - if (aRef == nullptr || aRef->IsRemoved) - { - return anIt; - } - - anIt.myCount = - myGraph->refTransientCache().CollectCacheKindSlots(theRef, aRef->OwnGen, anIt.mySlots); - return anIt; -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.hxx deleted file mode 100644 index 995128212c..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CacheView.hxx +++ /dev/null @@ -1,168 +0,0 @@ -// 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. - -#ifndef _BRepGraph_CacheView_HeaderFile -#define _BRepGraph_CacheView_HeaderFile - -#include -#include - -//! @brief Non-const view for managing transient cache values on nodes. -//! -//! This view is the stable public cache API for BRepGraph callers. -//! External code should use Cache() for all cache access. -//! Low-level storage operations such as reserve or cross-graph cache transfer -//! stay internal to graph-maintenance code through the graph's private cache access. -//! -//! Cached values are keyed by BRepGraph_CacheKind descriptors (Handle-based) -//! and stored as Handle(BRepGraph_CacheValue). Each CacheKind carries a -//! Standard_GUID for stable identity and is registered in -//! BRepGraph_CacheKindRegistry which maps GUIDs to dense runtime slot -//! indices for O(1) internal storage lookup. -//! -//! Supports set, get, remove, invalidate, and kind enumeration per node. -//! Cached data is stored centrally in BRepGraph_TransientCache with -//! generation-based freshness tracking via SubtreeGen. -//! Hot-path callers may pre-resolve a cache-kind slot once through -//! BRepGraph_CacheKindRegistry::Register() and then use slot-based overloads -//! to avoid repeated registry locking. -//! Obtained via BRepGraph::Cache(). -class BRepGraph::CacheView -{ -public: - //! Attach a cached value to a node. - //! @param[in] theNode node to attach the value to - //! @param[in] theKind cache kind descriptor identifying the slot - //! @param[in] theValue cached value to store - Standard_EXPORT void Set(const BRepGraph_NodeId theNode, - const occ::handle& theKind, - const occ::handle& theValue); - - //! Attach a cached value using a pre-resolved cache-kind slot. - Standard_EXPORT void Set(const BRepGraph_NodeId theNode, - const int theKindSlot, - const occ::handle& theValue); - - //! Retrieve a cached value from a node. - //! @param[in] theNode node to query - //! @param[in] theKind cache kind descriptor identifying the slot - //! @return cached value, or null handle if not present or stale - [[nodiscard]] Standard_EXPORT occ::handle Get( - const BRepGraph_NodeId theNode, - const occ::handle& theKind) const; - - //! Retrieve a cached value using a pre-resolved cache-kind slot. - [[nodiscard]] Standard_EXPORT occ::handle Get( - const BRepGraph_NodeId theNode, - const int theKindSlot) const; - - //! Check if a non-stale cached value exists on a node. - //! @param[in] theNode node to query - //! @param[in] theKind cache kind descriptor identifying the slot - //! @return true if a current value exists for this node and kind - [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_NodeId theNode, - const occ::handle& theKind) const; - - //! Check if a non-stale cached value exists using a pre-resolved slot. - [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_NodeId theNode, - const int theKindSlot) const; - - //! Remove a cached value from a node. - //! @param[in] theNode node to remove the value from - //! @param[in] theKind cache kind descriptor identifying the slot - //! @return true if a value was actually removed - Standard_EXPORT bool Remove(const BRepGraph_NodeId theNode, - const occ::handle& theKind); - - //! Remove a cached value using a pre-resolved cache-kind slot. - Standard_EXPORT bool Remove(const BRepGraph_NodeId theNode, const int theKindSlot); - - //! Invalidate (but do not remove) a cached value on a node. - //! @param[in] theNode node whose cache entry to invalidate - //! @param[in] theKind cache kind descriptor identifying the slot - Standard_EXPORT void Invalidate(const BRepGraph_NodeId theNode, - const occ::handle& theKind); - - //! Invalidate a cached value using a pre-resolved cache-kind slot. - Standard_EXPORT void Invalidate(const BRepGraph_NodeId theNode, const int theKindSlot); - - //! Create a zero-allocation iterator over all cache kinds populated on a node. - //! @param[in] theNode node to query - Standard_EXPORT BRepGraph_CacheKindIterator CacheKindIter( - const BRepGraph_NodeId theNode) const; - - //! Create a zero-allocation iterator over all cache kinds populated on a reference. - //! @param[in] theRef reference to query - Standard_EXPORT BRepGraph_CacheKindIterator CacheKindIter( - const BRepGraph_RefId theRef) const; - - // --- Reference-level cache --- - - //! Attach a cached value to a reference. - //! @param[in] theRef reference to attach the value to - //! @param[in] theKind cache kind descriptor identifying the slot - //! @param[in] theValue cached value to store - Standard_EXPORT void Set(const BRepGraph_RefId theRef, - const occ::handle& theKind, - const occ::handle& theValue); - - //! Attach a cached value to a reference using a pre-resolved cache-kind slot. - Standard_EXPORT void Set(const BRepGraph_RefId theRef, - const int theKindSlot, - const occ::handle& theValue); - - //! Retrieve a cached value from a reference. - //! @return cached value, or null handle if not present or stale (OwnGen changed) - [[nodiscard]] Standard_EXPORT occ::handle Get( - const BRepGraph_RefId theRef, - const occ::handle& theKind) const; - - //! Retrieve a cached value from a reference using a pre-resolved cache-kind slot. - [[nodiscard]] Standard_EXPORT occ::handle Get(const BRepGraph_RefId theRef, - const int theKindSlot) const; - - //! Check if a non-stale cached value exists on a reference. - [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_RefId theRef, - const occ::handle& theKind) const; - - //! Check if a non-stale cached value exists on a reference using a pre-resolved slot. - [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_RefId theRef, const int theKindSlot) const; - - //! Remove a cached value from a reference. - //! @return true if a value was actually removed - Standard_EXPORT bool Remove(const BRepGraph_RefId theRef, - const occ::handle& theKind); - - //! Remove a cached value from a reference using a pre-resolved cache-kind slot. - Standard_EXPORT bool Remove(const BRepGraph_RefId theRef, const int theKindSlot); - - //! Invalidate (but do not remove) a cached value on a reference. - Standard_EXPORT void Invalidate(const BRepGraph_RefId theRef, - const occ::handle& theKind); - - //! Invalidate a cached value on a reference using a pre-resolved cache-kind slot. - Standard_EXPORT void Invalidate(const BRepGraph_RefId theRef, const int theKindSlot); - -private: - friend class BRepGraph; - friend struct BRepGraph_Data; - - explicit CacheView(BRepGraph* theGraph) - : myGraph(theGraph) - { - } - - BRepGraph* myGraph; -}; - -#endif // _BRepGraph_CacheView_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.cxx index a01e3fe8e5..9ff180c9c2 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.cxx @@ -12,33 +12,58 @@ // commercial license or contractual agreement. #include + #include #include #include - +#include #include #include namespace { -static int childExplorerKindDepth(const BRepGraph_NodeId::Kind theKind) +static bool childExplorerKindCanContainDescendant(const BRepGraph_NodeId::Kind theParentKind, + const BRepGraph_NodeId::Kind theTargetKind) { - static constexpr int THE_DEPTH[] = { - 2, // Kind::Solid=0 - 3, // Kind::Shell=1 - 4, // Kind::Face=2 - 5, // Kind::Wire=3 - 7, // Kind::Edge=4 - 8, // Kind::Vertex=5 - 0, // Kind::Compound=6 - 1, // Kind::CompSolid=7 - 6, // Kind::CoEdge=8 - 99, // gap=9 - 0, // Kind::Product=10 - 1, // Kind::Occurrence=11 - }; - return THE_DEPTH[static_cast(theKind)]; + using Kind = BRepGraph_NodeId::Kind; + switch (theParentKind) + { + case Kind::Product: + return theTargetKind == Kind::Occurrence || theTargetKind == Kind::Product + || BRepGraph_NodeId::IsTopologyKind(theTargetKind); + case Kind::Occurrence: + return theTargetKind == Kind::Occurrence || theTargetKind == Kind::Product + || BRepGraph_NodeId::IsTopologyKind(theTargetKind); + case Kind::Compound: + return BRepGraph_NodeId::IsTopologyKind(theTargetKind); + case Kind::CompSolid: + return theTargetKind == Kind::Solid || theTargetKind == Kind::Shell + || theTargetKind == Kind::Face || theTargetKind == Kind::Wire + || theTargetKind == Kind::CoEdge || theTargetKind == Kind::Edge + || theTargetKind == Kind::Vertex; + case Kind::Solid: + return theTargetKind == Kind::Shell || theTargetKind == Kind::Face + || theTargetKind == Kind::Wire || theTargetKind == Kind::CoEdge + || theTargetKind == Kind::Edge || theTargetKind == Kind::Vertex; + case Kind::Shell: + return theTargetKind == Kind::Face || theTargetKind == Kind::Wire + || theTargetKind == Kind::CoEdge || theTargetKind == Kind::Edge + || theTargetKind == Kind::Vertex; + case Kind::Face: + return theTargetKind == Kind::Wire || theTargetKind == Kind::CoEdge + || theTargetKind == Kind::Edge || theTargetKind == Kind::Vertex; + case Kind::Wire: + return theTargetKind == Kind::CoEdge || theTargetKind == Kind::Edge + || theTargetKind == Kind::Vertex; + case Kind::CoEdge: + return theTargetKind == Kind::Edge || theTargetKind == Kind::Vertex; + case Kind::Edge: + return theTargetKind == Kind::Vertex; + case Kind::Vertex: + return false; + } + return false; } static BRepGraph_ChildExplorer::Config childExplorerConfig( @@ -75,26 +100,23 @@ static BRepGraph_RefId childRefIdForStep(const BRepGraph& theGraph, const BRepGraph::RefsView& aRefs = theGraph.Refs(); if (theParent.NodeKind != BRepGraph_NodeId::Kind::Product) { - return aRefs.RefAtStep(theParent, theStep); + return aRefs.Gen().RefAtStep(theParent, theStep); } - const BRepGraph::TopoView& aTopo = theGraph.Topo(); - const BRepGraph_ProductId aProductId = BRepGraph_ProductId::FromNodeId(theParent); - const BRepGraphInc::ProductDef& aProduct = aTopo.Products().Definition(aProductId); + const BRepGraph_ProductId aProductId = BRepGraph_ProductId::FromNodeId(theParent); uint32_t anActiveIndex = 0; - for (const BRepGraph_OccurrenceRefId& anOccurrenceRefId : aProduct.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& anOccurrenceRefId : + theGraph.Topo().Products().Relations(aProductId).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& anOccurrenceRef = aRefs.Occurrences().Entry(anOccurrenceRefId); - if (anOccurrenceRef.IsRemoved) + if (anOccurrenceRefId.IsRemoved(theGraph)) { continue; } - const BRepGraphInc::OccurrenceDef& anOccurrence = - aTopo.Occurrences().Definition(anOccurrenceRef.OccurrenceDefId); - if (anOccurrence.IsRemoved) + if (anOccurrenceRef.ChildOccurrenceId.IsRemoved(theGraph)) { continue; } @@ -285,7 +307,7 @@ void BRepGraph_ChildExplorer::startTraversal(const TopLoc_Location& theStartLo { const BRepGraphInc::BaseDef* aRootDef = myGraph->Topo().Gen().TopoEntity(myRoot); if (myConfig.TargetKind.has_value() && myConfig.EmitAvoidKind && aRootDef != nullptr - && !aRootDef->IsRemoved) + && !myRoot.IsRemoved(*myGraph)) { StackFrame aRootFrame; aRootFrame.Node = myRoot; @@ -301,7 +323,7 @@ void BRepGraph_ChildExplorer::startTraversal(const TopLoc_Location& theStartLo // Check if root is valid and not removed. const BRepGraphInc::BaseDef* aBaseDef = myGraph->Topo().Gen().TopoEntity(myRoot); - if (aBaseDef == nullptr || aBaseDef->IsRemoved) + if (aBaseDef == nullptr || myRoot.IsRemoved(*myGraph)) { return; } @@ -369,29 +391,31 @@ void BRepGraph_ChildExplorer::advance() TopLoc_Location aChildLoc = aFrame.AccLocation; TopAbs_Orientation aChildOri = aFrame.AccOrientation; int aStepIdx = static_cast(aIdx); + BRepGraph_RefId aCachedRef; // resolved at iteration time to avoid re-scan in CurrentRef() switch (aFrame.Node.NodeKind) { case Kind::Compound: { - const BRepGraphInc::CompoundDef& aComp = - aDefs.Compounds().Definition(BRepGraph_CompoundId(aFrame.Node)); // Skip removed refs. - const uint32_t aNbChildren = static_cast(aComp.ChildRefIds.Size()); + const BRepGraphInc::CompoundRelations& aRel = + aDefs.Compounds().Relations(BRepGraph_CompoundId(aFrame.Node)); + const uint32_t aNbChildren = static_cast(aRel.ChildRefIds.Size()); uint32_t i = aIdx; for (; i < aNbChildren; ++i) { - const BRepGraph_ChildRefId aRefId = aComp.ChildRefIds.Value(static_cast(i)); - if (!aRefs.IsRemoved(aRefId)) + const BRepGraph_ChildRefId aRefId = aRel.ChildRefIds.Value(static_cast(i)); + if (!aRefs.Gen().IsRemoved(aRefId)) { - aChildNode = aRefs.ChildNode(aRefId); + aChildNode = aRefs.Gen().ChildNode(aRefId); aStepIdx = static_cast(i); + aCachedRef = aRefId; if (myConfig.AccumulateLocation) { - aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + aChildLoc = aFrame.AccLocation * aRefs.Gen().LocalLocation(aRefId); } if (myConfig.AccumulateOrientation) { - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Gen().Orientation(aRefId)); } break; } @@ -401,24 +425,25 @@ void BRepGraph_ChildExplorer::advance() } case Kind::CompSolid: { - const BRepGraphInc::CompSolidDef& aCS = - aDefs.CompSolids().Definition(BRepGraph_CompSolidId(aFrame.Node)); - const uint32_t aNbSolids = static_cast(aCS.SolidRefIds.Size()); + const BRepGraphInc::CompSolidRelations& aRel = + aDefs.CompSolids().Relations(BRepGraph_CompSolidId(aFrame.Node)); + const uint32_t aNbSolids = static_cast(aRel.SolidRefIds.Size()); uint32_t i = aIdx; for (; i < aNbSolids; ++i) { - const BRepGraph_SolidRefId aRefId = aCS.SolidRefIds.Value(static_cast(i)); - if (!aRefs.IsRemoved(aRefId)) + const BRepGraph_SolidRefId aRefId = aRel.SolidRefIds.Value(static_cast(i)); + if (!aRefs.Gen().IsRemoved(aRefId)) { - aChildNode = aRefs.ChildNode(aRefId); + aChildNode = aRefs.Gen().ChildNode(aRefId); aStepIdx = static_cast(i); + aCachedRef = aRefId; if (myConfig.AccumulateLocation) { - aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + aChildLoc = aFrame.AccLocation * aRefs.Gen().LocalLocation(aRefId); } if (myConfig.AccumulateOrientation) { - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Gen().Orientation(aRefId)); } break; } @@ -428,35 +453,25 @@ void BRepGraph_ChildExplorer::advance() } case Kind::Solid: { - const BRepGraphInc::SolidDef& aSolid = - aDefs.Solids().Definition(BRepGraph_SolidId(aFrame.Node)); - const uint32_t aNbShells = static_cast(aSolid.ShellRefIds.Size()); - const uint32_t aNbFree = static_cast(aSolid.AuxChildRefIds.Size()); + const BRepGraphInc::SolidRelations& aRel = + aDefs.Solids().Relations(BRepGraph_SolidId(aFrame.Node)); + const uint32_t aNbShells = static_cast(aRel.ShellRefIds.Size()); uint32_t i = aIdx; - for (; i < aNbShells + aNbFree; ++i) + for (; i < aNbShells; ++i) { - BRepGraph_RefId aRefId; - if (i < aNbShells) + const BRepGraph_RefId aRefId = aRel.ShellRefIds.Value(static_cast(i)); + if (!aRefs.Gen().IsRemoved(aRefId)) { - aRefId = aSolid.ShellRefIds.Value(static_cast(i)); - } - else - { - const uint32_t aFreeIdx = i - aNbShells; - aRefId = aSolid.AuxChildRefIds.Value(static_cast(aFreeIdx)); - } - - if (!aRefs.IsRemoved(aRefId)) - { - aChildNode = aRefs.ChildNode(aRefId); + aChildNode = aRefs.Gen().ChildNode(aRefId); aStepIdx = static_cast(i); + aCachedRef = aRefId; if (myConfig.AccumulateLocation) { - aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + aChildLoc = aFrame.AccLocation * aRefs.Gen().LocalLocation(aRefId); } if (myConfig.AccumulateOrientation) { - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Gen().Orientation(aRefId)); } break; } @@ -466,35 +481,25 @@ void BRepGraph_ChildExplorer::advance() } case Kind::Shell: { - const BRepGraphInc::ShellDef& aShell = - aDefs.Shells().Definition(BRepGraph_ShellId(aFrame.Node)); - const uint32_t aNbFaces = static_cast(aShell.FaceRefIds.Size()); - const uint32_t aNbFree = static_cast(aShell.AuxChildRefIds.Size()); + const BRepGraphInc::ShellRelations& aRel = + aDefs.Shells().Relations(BRepGraph_ShellId(aFrame.Node)); + const uint32_t aNbFaces = static_cast(aRel.FaceRefIds.Size()); uint32_t i = aIdx; - for (; i < aNbFaces + aNbFree; ++i) + for (; i < aNbFaces; ++i) { - BRepGraph_RefId aRefId; - if (i < aNbFaces) + const BRepGraph_RefId aRefId = aRel.FaceRefIds.Value(static_cast(i)); + if (!aRefs.Gen().IsRemoved(aRefId)) { - aRefId = aShell.FaceRefIds.Value(static_cast(i)); - } - else - { - const uint32_t aFreeIdx = i - aNbFaces; - aRefId = aShell.AuxChildRefIds.Value(static_cast(aFreeIdx)); - } - - if (!aRefs.IsRemoved(aRefId)) - { - aChildNode = aRefs.ChildNode(aRefId); + aChildNode = aRefs.Gen().ChildNode(aRefId); aStepIdx = static_cast(i); + aCachedRef = aRefId; if (myConfig.AccumulateLocation) { - aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + aChildLoc = aFrame.AccLocation * aRefs.Gen().LocalLocation(aRefId); } if (myConfig.AccumulateOrientation) { - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Gen().Orientation(aRefId)); } break; } @@ -504,35 +509,25 @@ void BRepGraph_ChildExplorer::advance() } case Kind::Face: { - const BRepGraphInc::FaceDef& aFace = - aDefs.Faces().Definition(BRepGraph_FaceId(aFrame.Node)); - const uint32_t aNbWires = static_cast(aFace.WireRefIds.Size()); - const uint32_t aNbVerts = static_cast(aFace.VertexRefIds.Size()); + const BRepGraphInc::FaceRelations& aRel = + aDefs.Faces().Relations(BRepGraph_FaceId(aFrame.Node)); + const uint32_t aNbWires = static_cast(aRel.WireRefIds.Size()); uint32_t i = aIdx; - for (; i < aNbWires + aNbVerts; ++i) + for (; i < aNbWires; ++i) { - BRepGraph_RefId aRefId; - if (i < aNbWires) + const BRepGraph_RefId aRefId = aRel.WireRefIds.Value(static_cast(i)); + if (!aRefs.Gen().IsRemoved(aRefId)) { - aRefId = aFace.WireRefIds.Value(static_cast(i)); - } - else - { - const uint32_t aVIdx = i - aNbWires; - aRefId = aFace.VertexRefIds.Value(static_cast(aVIdx)); - } - - if (!aRefs.IsRemoved(aRefId)) - { - aChildNode = aRefs.ChildNode(aRefId); + aChildNode = aRefs.Gen().ChildNode(aRefId); aStepIdx = static_cast(i); + aCachedRef = aRefId; if (myConfig.AccumulateLocation) { - aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); + aChildLoc = aFrame.AccLocation * aRefs.Gen().LocalLocation(aRefId); } if (myConfig.AccumulateOrientation) { - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Gen().Orientation(aRefId)); } break; } @@ -542,27 +537,26 @@ void BRepGraph_ChildExplorer::advance() } case Kind::Wire: { - const BRepGraphInc::WireDef& aWire = - aDefs.Wires().Definition(BRepGraph_WireId(aFrame.Node)); - const uint32_t aNbCoEdges = static_cast(aWire.CoEdgeRefIds.Size()); + const BRepGraphInc::WireRelations& aRel = + aDefs.Wires().Relations(BRepGraph_WireId(aFrame.Node)); + const uint32_t aNbCoEdges = static_cast(aRel.CoEdgeIds.Size()); uint32_t i = aIdx; for (; i < aNbCoEdges; ++i) { - const BRepGraph_CoEdgeRefId aRefId = aWire.CoEdgeRefIds.Value(static_cast(i)); - if (!aRefs.IsRemoved(aRefId)) + const BRepGraph_CoEdgeId aCoEdgeId = aRel.CoEdgeIds.Value(static_cast(i)); + if (!aCoEdgeId.IsValid(aDefs.CoEdges().Nb())) { - aChildNode = aRefs.ChildNode(aRefId); - aStepIdx = static_cast(i); - if (myConfig.AccumulateLocation) - { - aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aRefId); - } - if (myConfig.AccumulateOrientation) - { - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aRefId)); - } - break; + continue; } + + if (BRepGraph_NodeId(aCoEdgeId).IsRemoved(*myGraph)) + { + continue; + } + + aChildNode = aCoEdgeId; + aStepIdx = -1; + break; } aFrame.NextChildIdx = i + 1; break; @@ -571,10 +565,8 @@ void BRepGraph_ChildExplorer::advance() case Kind::Edge: { const BRepGraphInc::EdgeDef& anEdge = aDefs.Edges().Definition(BRepGraph_EdgeId(aFrame.Node)); - // Virtual concatenation: 0=Start, 1=End, 2+=Internal. - const uint32_t aNbIntern = static_cast(anEdge.InternalVertexRefIds.Size()); - const uint32_t aNbTotal = 2 + aNbIntern; - uint32_t i = aIdx; + const uint32_t aNbTotal = 2; + uint32_t i = aIdx; for (; i < aNbTotal; ++i) { BRepGraph_VertexRefId aVRefId; @@ -586,26 +578,23 @@ void BRepGraph_ChildExplorer::advance() { aVRefId = anEdge.EndVertexRefId; } - else - { - aVRefId = anEdge.InternalVertexRefIds.Value(static_cast(i - 2)); - } if (!aVRefId.IsValid()) { continue; } - if (!aRefs.IsRemoved(aVRefId)) + if (!aRefs.Gen().IsRemoved(aVRefId)) { - aChildNode = aRefs.ChildNode(aVRefId); + aChildNode = aRefs.Gen().ChildNode(aVRefId); aStepIdx = static_cast(i); + aCachedRef = aVRefId; if (myConfig.AccumulateLocation) { - aChildLoc = aFrame.AccLocation * aRefs.LocalLocation(aVRefId); + aChildLoc = aFrame.AccLocation * aRefs.Gen().LocalLocation(aVRefId); } if (myConfig.AccumulateOrientation) { - aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Orientation(aVRefId)); + aChildOri = TopAbs::Compose(aFrame.AccOrientation, aRefs.Gen().Orientation(aVRefId)); } break; } @@ -620,9 +609,10 @@ void BRepGraph_ChildExplorer::advance() { const BRepGraphInc::CoEdgeDef& aCoEdge = aDefs.CoEdges().Definition(BRepGraph_CoEdgeId(aFrame.Node)); - if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId.IsValid()) + if (!BRepGraph_NodeId(BRepGraph_CoEdgeId(aFrame.Node)).IsRemoved(*myGraph) + && aCoEdge.ChildEdgeId.IsValid()) { - aChildNode = aCoEdge.EdgeDefId; + aChildNode = aCoEdge.ChildEdgeId; aStepIdx = -1; if (myConfig.AccumulateOrientation) { @@ -636,23 +626,30 @@ void BRepGraph_ChildExplorer::advance() case Kind::Occurrence: { // Occurrence references exactly one child node (topology root or product). - // Location is on the OccurrenceRef, not on the OccurrenceDef. + // Location is on the OccurrenceRef, not on the OccurrenceDef. Product + // parents compose that location when pushing the Occurrence frame; root + // Occurrence traversals have no parent ref context and use the first + // live ref. if (aIdx == 0) { const BRepGraphInc::OccurrenceDef& anOcc = aDefs.Occurrences().Definition(BRepGraph_OccurrenceId(aFrame.Node)); - if (!anOcc.IsRemoved && anOcc.ChildDefId.IsValid()) + if (!aFrame.Node.IsRemoved(*myGraph) && anOcc.ChildNodeId.IsValid()) { - aChildNode = anOcc.ChildDefId; + aChildNode = anOcc.ChildNodeId; aStepIdx = -1; - // Location is on the OccurrenceRef - find it by scanning. - if (myConfig.AccumulateLocation) + // Location is on the OccurrenceRef. If the current frame already + // came through a Product->Occurrence ref, AccLocation is already + // instance-specific; do not scan by OccurrenceDef and accidentally + // reuse another duplicate's transform. + if (myConfig.AccumulateLocation && !aFrame.Ref.IsValid()) { for (BRepGraph_FullOccurrenceRefIterator aRefIt(*myGraph); aRefIt.More(); aRefIt.Next()) { const BRepGraphInc::OccurrenceRef& aRef = aRefIt.Current(); - if (!aRef.IsRemoved && aRef.OccurrenceDefId == BRepGraph_OccurrenceId(aFrame.Node)) + if (!aRefIt.CurrentId().IsRemoved(*myGraph) + && aRef.ChildOccurrenceId == BRepGraph_OccurrenceId(aFrame.Node)) { aChildLoc = aFrame.AccLocation * aRef.LocalLocation; break; @@ -666,16 +663,34 @@ void BRepGraph_ChildExplorer::advance() } case Kind::Product: { - // All product children (shape roots and sub-products) go through occurrences. + // Scan occurrence refs directly to get both OccurrenceId and RefId in one pass, + // avoiding the double O(N) scan that Component() + childRefIdForStep() would do. const BRepGraph_ProductId aProdId(aFrame.Node); - const uint32_t aNbComps = - static_cast(myGraph->Topo().Products().NbComponents(aProdId)); - if (aIdx < aNbComps) + uint32_t anActiveIdx = 0; + for (const BRepGraph_OccurrenceRefId& anOccRefId : + aDefs.Products().Relations(aProdId).OccurrenceRefIds) { - const BRepGraph_OccurrenceId anOccId = - myGraph->Topo().Products().Component(aProdId, static_cast(aIdx)); - aChildNode = anOccId; - aStepIdx = static_cast(aIdx); + const BRepGraphInc::OccurrenceRef& anOccRef = aRefs.Occurrences().Entry(anOccRefId); + if (anOccRefId.IsRemoved(*myGraph)) + { + continue; + } + if (anOccRef.ChildOccurrenceId.IsRemoved(*myGraph)) + { + continue; + } + if (anActiveIdx == aIdx) + { + aChildNode = anOccRef.ChildOccurrenceId; + aStepIdx = static_cast(aIdx); + aCachedRef = anOccRefId; + if (myConfig.AccumulateLocation) + { + aChildLoc = aFrame.AccLocation * anOccRef.LocalLocation; + } + break; + } + ++anActiveIdx; } aFrame.NextChildIdx = aIdx + 1; break; @@ -697,12 +712,13 @@ void BRepGraph_ChildExplorer::advance() if (matchesAvoid(aChildNode)) { const BRepGraphInc::BaseDef* anAvoidDef = aDefs.Gen().TopoEntity(aChildNode); - if (myConfig.EmitAvoidKind && anAvoidDef != nullptr && !anAvoidDef->IsRemoved) + if (myConfig.EmitAvoidKind && anAvoidDef != nullptr && !aChildNode.IsRemoved(*myGraph)) { StackFrame aChildFrame; aChildFrame.Node = aChildNode; aChildFrame.NextChildIdx = 0; aChildFrame.StepFromParent = aStepIdx; + aChildFrame.Ref = aCachedRef; aChildFrame.AccLocation = aChildLoc; aChildFrame.AccOrientation = aChildOri; pushFrame(aChildFrame); @@ -716,7 +732,7 @@ void BRepGraph_ChildExplorer::advance() if (shouldEmit(aChildNode)) { const BRepGraphInc::BaseDef* aPostDef = aDefs.Gen().TopoEntity(aChildNode); - if (aPostDef == nullptr || aPostDef->IsRemoved) + if (aPostDef == nullptr || aChildNode.IsRemoved(*myGraph)) { continue; } @@ -725,6 +741,7 @@ void BRepGraph_ChildExplorer::advance() aChildFrame.Node = aChildNode; aChildFrame.NextChildIdx = 0; aChildFrame.StepFromParent = aStepIdx; + aChildFrame.Ref = aCachedRef; aChildFrame.AccLocation = aChildLoc; aChildFrame.AccOrientation = aChildOri; pushFrame(aChildFrame); @@ -734,7 +751,7 @@ void BRepGraph_ChildExplorer::advance() // Check if resolved child is valid and not removed before descending. const BRepGraphInc::BaseDef* aBaseDef = aDefs.Gen().TopoEntity(aChildNode); - if (aBaseDef == nullptr || aBaseDef->IsRemoved) + if (aBaseDef == nullptr || aChildNode.IsRemoved(*myGraph)) { continue; } @@ -749,6 +766,7 @@ void BRepGraph_ChildExplorer::advance() aChildFrame.Node = aChildNode; aChildFrame.NextChildIdx = 0; aChildFrame.StepFromParent = aStepIdx; + aChildFrame.Ref = aCachedRef; aChildFrame.AccLocation = aChildLoc; aChildFrame.AccOrientation = aChildOri; pushFrame(aChildFrame); @@ -801,6 +819,14 @@ BRepGraph_RefId BRepGraph_ChildExplorer::CurrentRef() const return BRepGraph_RefId(); } + // Ref is pre-resolved when the frame was pushed (O(1)). + const BRepGraph_RefId& aCached = myStack[myCurrentFrame].Ref; + if (aCached.IsValid()) + { + return aCached; + } + + // Fallback for non-Product parents (aCachedRef is left default-invalid there). const StackFrame& aCurrentFrame = myStack[myCurrentFrame]; return childRefIdForStep(*myGraph, myStack[myCurrentFrame - 1].Node, @@ -809,6 +835,23 @@ BRepGraph_RefId BRepGraph_ChildExplorer::CurrentRef() const //================================================================================================= +BRepGraph_UsagePath BRepGraph_ChildExplorer::CurrentUsagePath() const +{ + if (!myHasMore || myCurrentFrame < 0) + { + return BRepGraph_UsagePath(); + } + BRepGraph_UsagePath aPath(static_cast(myCurrentFrame + 1)); + for (int aFrameIdx = 0; aFrameIdx <= myCurrentFrame; ++aFrameIdx) + { + const StackFrame& aFrame = myStack[aFrameIdx]; + aPath.Append(BRepGraph_UsagePath::Step{aFrame.Node, aFrame.Ref, aFrame.StepFromParent}); + } + return aPath; +} + +//================================================================================================= + bool BRepGraph_ChildExplorer::shouldDescendFromCurrent() const { if (myConfig.Mode != TraversalMode::Recursive || myCurrentFrame < 0) @@ -817,8 +860,17 @@ bool BRepGraph_ChildExplorer::shouldDescendFromCurrent() const } const BRepGraph_NodeId aNode = myStack[myCurrentFrame].Node; - return !myConfig.TargetKind.has_value() && !matchesAvoid(aNode) - && canHaveChildren(aNode.NodeKind); + if (matchesAvoid(aNode)) + { + return false; + } + + if (myConfig.TargetKind.has_value()) + { + return canContainTarget(aNode.NodeKind, *myConfig.TargetKind); + } + + return canHaveChildren(aNode.NodeKind); } //================================================================================================= @@ -832,6 +884,11 @@ std::optional BRepGraph_ChildExplorer::normalizeAvoidKin return theAvoidKind; } + if (*theAvoidKind == *theTargetKind) + { + return std::nullopt; + } + return canContainTarget(*theAvoidKind, *theTargetKind) ? theAvoidKind : std::nullopt; } @@ -840,9 +897,7 @@ std::optional BRepGraph_ChildExplorer::normalizeAvoidKin bool BRepGraph_ChildExplorer::canContainTarget(const BRepGraph_NodeId::Kind theParentKind, const BRepGraph_NodeId::Kind theTargetKind) { - const int aParentDepth = childExplorerKindDepth(theParentKind); - const int aTargetDepth = childExplorerKindDepth(theTargetKind); - return aParentDepth < aTargetDepth; + return childExplorerKindCanContainDescendant(theParentKind, theTargetKind); } //================================================================================================= @@ -858,7 +913,7 @@ void BRepGraph_ChildExplorer::pushFrame(const StackFrame& theFrame) { // Guard against pathological cycles (e.g., self-referencing compounds). // A valid DFS path cannot exceed the total node count in the graph. - if (myStackTop >= myGraph->Topo().Gen().NbNodes()) + if (myStackTop >= 0 && static_cast(myStackTop) >= myGraph->Topo().Gen().NbNodes()) { return; } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.hxx index 134c18781c..64de1a2deb 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ChildExplorer.hxx @@ -18,10 +18,9 @@ #include #include #include - +#include #include #include - #include #include @@ -41,8 +40,7 @@ //! Compound -> children, CompSolid -> Solids, Solid -> Shells, //! Shell -> Faces, Face -> Wires (+direct Vertices), Wire -> CoEdges, //! CoEdge -> Edge, Edge -> Vertices, -//! Product(assembly) -> Occurrences, Product(part) -> ShapeRoot, -//! Occurrence -> Product. +//! Product -> Occurrences, Occurrence -> Product/topology-root. //! //! Unlike flat definition traversal by typed ids, BRepGraph_ChildExplorer visits //! each occurrence. If Edge[5] is reachable through Face[0] and Face[1], @@ -78,10 +76,8 @@ public: //! Consolidated configuration for the explorer. //! - //! Prefer this struct over the historical 11-overload constructor family. The - //! overloads remain supported for existing callers but the `Config`-based - //! constructor is the stable long-term idiom: new options can be added as - //! fields without another constructor explosion. + //! The `Config`-based constructor is the preferred idiom: new options can be + //! added as fields without additional constructor overloads. //! //! @code //! BRepGraph_ChildExplorer::Config aConfig; @@ -113,28 +109,57 @@ public: const BRepGraph_NodeId theRoot, const Config& theConfig); + //! Explore all descendants of the root node using recursive traversal. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot root node where the walk begins Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theRoot); + //! Explore descendants of the root node using the given traversal mode. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot root node where the walk begins + //! @param[in] theMode traversal strategy (recursive or direct children) Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theRoot, TraversalMode theMode); + //! Explore descendants while pruning branches at the avoid kind. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot root node where the walk begins + //! @param[in] theAvoidKind node kind to avoid descending into + //! @param[in] theEmitAvoidKind if true, emit matching avoid-kind nodes once before skipping + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theRoot, const std::optional& theAvoidKind, bool theEmitAvoidKind, TraversalMode theMode = TraversalMode::Recursive); + //! Explore only descendants of the given target kind. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot root node where the walk begins + //! @param[in] theTargetKind kind of nodes to emit Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theRoot, BRepGraph_NodeId::Kind theTargetKind); + //! Explore only descendants of the given target kind using the given traversal mode. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot root node where the walk begins + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theRoot, BRepGraph_NodeId::Kind theTargetKind, TraversalMode theMode); + //! Explore descendants of the given target kind while pruning branches at the avoid kind. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot root node where the walk begins + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theAvoidKind node kind to avoid descending into + //! @param[in] theEmitAvoidKind if true, emit matching avoid-kind nodes once before skipping + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theRoot, BRepGraph_NodeId::Kind theTargetKind, @@ -142,12 +167,19 @@ public: bool theEmitAvoidKind, TraversalMode theMode = TraversalMode::Recursive); + //! Explore only descendants of the given target kind starting from a product. + //! @param[in] theGraph graph to walk + //! @param[in] theProduct product whose occurrences and topology are explored + //! @param[in] theTargetKind kind of nodes to emit Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_ProductId theProduct, BRepGraph_NodeId::Kind theTargetKind); //! Disambiguates non-product typed ids from the ProductId-specific overload //! family above and keeps them on the generic NodeId traversal path. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot typed root node where the walk begins + //! @param[in] theTargetKind kind of nodes to emit template = 0> BRepGraph_ChildExplorer(const BRepGraph& theGraph, @@ -157,6 +189,12 @@ public: { } + //! Explore only descendants of the given target kind starting from a product, + //! using the given traversal mode. + //! @param[in] theGraph graph to walk + //! @param[in] theProduct product whose occurrences and topology are explored + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_ProductId theProduct, BRepGraph_NodeId::Kind theTargetKind, @@ -164,6 +202,10 @@ public: //! Disambiguates non-product typed ids from the ProductId-specific overload //! family above and keeps them on the generic NodeId traversal path. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot typed root node where the walk begins + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theMode traversal strategy template = 0> BRepGraph_ChildExplorer(const BRepGraph& theGraph, @@ -174,6 +216,13 @@ public: { } + //! Explore only descendants of the given target kind with explicit location/orientation control. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot root node where the walk begins + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theCumLoc if true, accumulate location down the walk + //! @param[in] theCumOri if true, accumulate orientation down the walk + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theRoot, BRepGraph_NodeId::Kind theTargetKind, @@ -181,6 +230,14 @@ public: bool theCumOri, TraversalMode theMode = TraversalMode::Recursive); + //! Explore only descendants of the given target kind starting from a product, + //! with explicit location/orientation control. + //! @param[in] theGraph graph to walk + //! @param[in] theProduct product whose occurrences and topology are explored + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theCumLoc if true, accumulate location down the walk + //! @param[in] theCumOri if true, accumulate orientation down the walk + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_ProductId theProduct, BRepGraph_NodeId::Kind theTargetKind, @@ -190,6 +247,12 @@ public: //! Disambiguates non-product typed ids from the ProductId-specific overload //! family above and keeps them on the generic NodeId traversal path. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot typed root node where the walk begins + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theCumLoc if true, accumulate location down the walk + //! @param[in] theCumOri if true, accumulate orientation down the walk + //! @param[in] theMode traversal strategy template = 0> BRepGraph_ChildExplorer(const BRepGraph& theGraph, @@ -207,6 +270,13 @@ public: { } + //! Explore only descendants of the given target kind with an explicit initial transform. + //! @param[in] theGraph graph to walk + //! @param[in] theRoot root node where the walk begins + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theStartLoc initial accumulated location + //! @param[in] theStartOri initial accumulated orientation + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ChildExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theRoot, BRepGraph_NodeId::Kind theTargetKind, @@ -218,10 +288,13 @@ public: //! Read-only - configuration is fixed for the lifetime of the explorer. [[nodiscard]] const Config& GetConfig() const { return myConfig; } + //! True if another matching descendant is available. [[nodiscard]] bool More() const { return myHasMore; } + //! Advance to the next matching descendant. Standard_EXPORT void Next(); + //! Current matching descendant node with accumulated location and orientation. [[nodiscard]] BRepGraphInc::NodeInstance Current() const { return {myCurrent, myLocation, myOrientation}; @@ -237,18 +310,38 @@ public: //! Returns the exact parent-owned RefId for Current(), when the current step //! is represented by a reference entry. Returns invalid RefId for structural //! links without a dedicated ref entry such as CoEdge->Edge, - //! Product(part)->ShapeRoot and Occurrence->Product. + //! Occurrence->Product/topology-root. [[nodiscard]] Standard_EXPORT BRepGraph_RefId CurrentRef() const; + //! Returns the explicit concrete traversal path from the explorer root to Current(). + [[nodiscard]] Standard_EXPORT BRepGraph_UsagePath CurrentUsagePath() const; + + //! Returns the accumulated location at the most recent ancestor of the given kind. + //! @param[in] theKind node kind to search for in the ancestor chain + //! @return accumulated location at the matching ancestor [[nodiscard]] Standard_EXPORT TopLoc_Location LocationOf(const BRepGraph_NodeId::Kind theKind) const; + //! Returns the node id of the most recent ancestor of the given kind. + //! @param[in] theKind node kind to search for in the ancestor chain + //! @return node id of the matching ancestor [[nodiscard]] Standard_EXPORT BRepGraph_NodeId NodeOf(const BRepGraph_NodeId::Kind theKind) const; + //! Returns the accumulated location at the given stack level. + //! @param[in] theLevel zero-based stack depth (0 = root) + //! @return accumulated location at the specified level [[nodiscard]] Standard_EXPORT TopLoc_Location LocationAt(const int theLevel) const; + //! Returns the node id at the given stack level. + //! @param[in] theLevel zero-based stack depth (0 = root) + //! @return node id at the specified level [[nodiscard]] Standard_EXPORT BRepGraph_NodeId NodeAt(const int theLevel) const; + //! Number of valid ancestor frames currently on the stack (excluding the + //! sentinel below the root). O(1); avoids the O(depth^2) NodeAt(i) walk used + //! to compute container priority in selection-mode building. + [[nodiscard]] int Depth() const noexcept { return myStackTop < 0 ? 0 : myStackTop + 1; } + //! Returns an STL-compatible iterator for range-based for loops. NCollection_ForwardRangeIterator begin() { @@ -264,11 +357,12 @@ private: BRepGraph_NodeId Node; uint32_t NextChildIdx = 0; int StepFromParent = -1; + BRepGraph_RefId Ref; //!< RefId resolved at push time (O(1) in CurrentRef) TopLoc_Location AccLocation; TopAbs_Orientation AccOrientation = TopAbs_FORWARD; }; - void advance(); + Standard_EXPORT void advance(); void startTraversal(const TopLoc_Location& theStartLoc, TopAbs_Orientation theStartOri); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx index 0a36b507c9..d04d1e973f 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.cxx @@ -12,26 +12,31 @@ // 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 @@ -74,7 +79,8 @@ BRepGraph_NodeId remapNodeId( const NCollection_DataMap& theShellMap, const NCollection_DataMap& theSolidMap, const NCollection_DataMap& theCompoundMap, - const NCollection_DataMap& theCompSolidMap) + const NCollection_DataMap& theCompSolidMap, + const NCollection_DataMap& theCoEdgeMap) { if (!theId.IsValid()) { @@ -115,6 +121,10 @@ BRepGraph_NodeId remapNodeId( const BRepGraph_CompSolidId* aNewId = theCompSolidMap.Seek(BRepGraph_CompSolidId(theId)); return aNewId != nullptr ? BRepGraph_NodeId(*aNewId) : BRepGraph_NodeId(); } + case BRepGraph_NodeId::Kind::CoEdge: { + const BRepGraph_CoEdgeId* aNewId = theCoEdgeMap.Seek(BRepGraph_CoEdgeId(theId)); + return aNewId != nullptr ? BRepGraph_NodeId(*aNewId) : BRepGraph_NodeId(); + } default: // Product/Occurrence kinds are not compacted - they reference topology // nodes which are remapped independently. @@ -124,9 +134,9 @@ BRepGraph_NodeId remapNodeId( } template -int countActiveDefs(const BRepGraph& theGraph) +uint32_t countActiveDefs(const BRepGraph& theGraph) { - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) { ++aCount; @@ -154,113 +164,6 @@ void bindActiveIndexMap(const BRepGraph& theGraph, } } -//! Compute shell closedness from rebuilt incidence. -//! A shell is considered closed if each non-degenerate edge used by shell faces -//! is referenced by exactly two faces of that shell. -bool isShellClosedByIncidence(const BRepGraph& theGraph, const BRepGraph_ShellId theShell) -{ - NCollection_Map aShellFaces; - for (BRepGraph_RefsFaceOfShell aFaceIt(theGraph, theShell); aFaceIt.More(); aFaceIt.Next()) - { - const BRepGraphInc::FaceRef& aFR = theGraph.Refs().Faces().Entry(aFaceIt.CurrentId()); - aShellFaces.Add(aFR.FaceDefId); - } - - if (aShellFaces.IsEmpty()) - { - return false; - } - - for (BRepGraph_Iterator anEdgeIt(theGraph); anEdgeIt.More(); - anEdgeIt.Next()) - { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - if (BRepGraph_Tool::Edge::Degenerated(theGraph, anEdgeId)) - { - continue; - } - - int aFaceCountInShell = 0; - const NCollection_DynamicArray& aEdgeFaces = - theGraph.Topo().Edges().Faces(anEdgeId); - for (NCollection_DynamicArray::Iterator aFaceIt(aEdgeFaces); aFaceIt.More(); - aFaceIt.Next()) - { - if (aShellFaces.Contains(aFaceIt.Value())) - { - ++aFaceCountInShell; - } - } - - if (aFaceCountInShell == 1 || aFaceCountInShell > 2) - { - return false; - } - } - - return true; -} - -//! Compute wire closedness from rebuilt coedge/vertex incidence. -//! A wire is considered closed if every participating vertex has even degree. -bool isWireClosedByIncidence(const BRepGraph& theGraph, const BRepGraph_WireId theWire) -{ - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const BRepGraph::TopoView& aTopo = theGraph.Topo(); - - NCollection_DataMap aVertexDegree; - int aNbCoEdges = 0; - - for (BRepGraph_RefsCoEdgeOfWire aCEIt(theGraph, theWire); aCEIt.More(); aCEIt.Next()) - { - const BRepGraphInc::CoEdgeRef& aCRef = aRefs.CoEdges().Entry(aCEIt.CurrentId()); - const BRepGraphInc::CoEdgeDef& aCoEdge = aTopo.CoEdges().Definition(aCRef.CoEdgeDefId); - const BRepGraphInc::EdgeDef& anEdge = aTopo.Edges().Definition(aCoEdge.EdgeDefId); - - const BRepGraph_VertexRefId aStartRefId = - (aCoEdge.Orientation == TopAbs_FORWARD) ? anEdge.StartVertexRefId : anEdge.EndVertexRefId; - const BRepGraph_VertexRefId anEndRefId = - (aCoEdge.Orientation == TopAbs_FORWARD) ? anEdge.EndVertexRefId : anEdge.StartVertexRefId; - - if (!aStartRefId.IsValid() || !anEndRefId.IsValid()) - { - return false; - } - - const BRepGraph_VertexId aStartVtxId = aRefs.Vertices().Entry(aStartRefId).VertexDefId; - const BRepGraph_VertexId anEndVtxId = aRefs.Vertices().Entry(anEndRefId).VertexDefId; - - if (!aVertexDegree.IsBound(aStartVtxId)) - { - aVertexDegree.Bind(aStartVtxId, 0); - } - if (!aVertexDegree.IsBound(anEndVtxId)) - { - aVertexDegree.Bind(anEndVtxId, 0); - } - - aVertexDegree.ChangeFind(aStartVtxId)++; - aVertexDegree.ChangeFind(anEndVtxId)++; - ++aNbCoEdges; - } - - if (aNbCoEdges == 0) - { - return false; - } - - for (NCollection_DataMap::Iterator aDegIt(aVertexDegree); aDegIt.More(); - aDegIt.Next()) - { - if ((aDegIt.Value() % 2) != 0) - { - return false; - } - } - - return true; -} - } // namespace //================================================================================================= @@ -275,7 +178,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph) BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const Options& theOptions) { Result aResult; - if (!theGraph.IsDone()) + if (theGraph.IsEmpty()) { return aResult; } @@ -298,12 +201,12 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aResult.NbRemovedCompSolids = theGraph.Topo().CompSolids().Nb() - countActiveDefs(theGraph); - const int aTotalRemoved = aResult.NbRemovedVertices + aResult.NbRemovedEdges - + aResult.NbRemovedWires + aResult.NbRemovedFaces - + aResult.NbRemovedShells + aResult.NbRemovedSolids - + aResult.NbRemovedCompounds + aResult.NbRemovedCompSolids; + const uint32_t aTotalRemoved = aResult.NbRemovedVertices + aResult.NbRemovedEdges + + aResult.NbRemovedWires + aResult.NbRemovedFaces + + aResult.NbRemovedShells + aResult.NbRemovedSolids + + aResult.NbRemovedCompounds + aResult.NbRemovedCompSolids; - aResult.NbNodesBefore = static_cast(theGraph.Topo().Gen().NbNodes()); + aResult.NbNodesBefore = theGraph.Topo().Gen().NbNodes(); // Short-circuit: nothing to compact. if (aTotalRemoved == 0) @@ -312,32 +215,88 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const return aResult; } + const occ::handle aPermAlloc = new NCollection_IncAllocator(); + // Build old->new index maps for each node kind. - NCollection_DataMap aVertexMap; - NCollection_DataMap anEdgeMap; - NCollection_DataMap aCoEdgeMap; - NCollection_DataMap aWireMap; - NCollection_DataMap aFaceMap; - NCollection_DataMap aShellMap; - NCollection_DataMap aSolidMap; - NCollection_DataMap aCompoundMap; - NCollection_DataMap aCompSolidMap; - NCollection_DataMap aProductMap; - NCollection_DataMap anOccurrenceMap; + const uint32_t aNbVertices = theGraph.Topo().Vertices().Nb(); + const uint32_t aNbEdges = theGraph.Topo().Edges().Nb(); + const uint32_t aNbCoEdges = theGraph.Topo().CoEdges().Nb(); + const uint32_t aNbWires = theGraph.Topo().Wires().Nb(); + const uint32_t aNbFaces = theGraph.Topo().Faces().Nb(); + const uint32_t aNbShells = theGraph.Topo().Shells().Nb(); + const uint32_t aNbSolids = theGraph.Topo().Solids().Nb(); + const uint32_t aNbCompounds = theGraph.Topo().Compounds().Nb(); + const uint32_t aNbCompSolids = theGraph.Topo().CompSolids().Nb(); + const uint32_t aNbProducts = theGraph.Topo().Products().Nb(); + const uint32_t aNbOccurrences = theGraph.Topo().Occurrences().Nb(); + + NCollection_DataMap aVertexMap( + static_cast(aNbVertices), + aPermAlloc); + NCollection_DataMap anEdgeMap(static_cast(aNbEdges), + aPermAlloc); + NCollection_DataMap aCoEdgeMap( + static_cast(aNbCoEdges), + aPermAlloc); + NCollection_DataMap aWireMap(static_cast(aNbWires), + aPermAlloc); + NCollection_DataMap aFaceMap(static_cast(aNbFaces), + aPermAlloc); + NCollection_DataMap aShellMap( + static_cast(aNbShells), + aPermAlloc); + NCollection_DataMap aSolidMap( + static_cast(aNbSolids), + aPermAlloc); + NCollection_DataMap aCompoundMap( + static_cast(aNbCompounds), + aPermAlloc); + NCollection_DataMap aCompSolidMap( + static_cast(aNbCompSolids), + aPermAlloc); + NCollection_DataMap aProductMap( + static_cast(aNbProducts), + aPermAlloc); + NCollection_DataMap anOccurrenceMap( + static_cast(aNbOccurrences), + aPermAlloc); // Per-kind ref id remap: old RefId -> new RefId. // Built during topology rebuild loops; used for RefUID transfer after swap. - NCollection_DataMap aVertexRefMap; - NCollection_DataMap aCoEdgeRefMap; - NCollection_DataMap aWireRefMap; - NCollection_DataMap aFaceRefMap; - NCollection_DataMap aShellRefMap; - NCollection_DataMap aChildRefMap; - NCollection_DataMap aSolidRefMap; - NCollection_DataMap anOccurrenceRefMap; + const uint32_t aNbVertexRefs = theGraph.Refs().Vertices().Nb(); + const uint32_t aNbWireRefs = theGraph.Refs().Wires().Nb(); + const uint32_t aNbFaceRefs = theGraph.Refs().Faces().Nb(); + const uint32_t aNbShellRefs = theGraph.Refs().Shells().Nb(); + const uint32_t aNbChildRefs = theGraph.Refs().Children().Nb(); + const uint32_t aNbSolidRefs = theGraph.Refs().Solids().Nb(); + const uint32_t aNbOccurrenceRefs = theGraph.Refs().Occurrences().Nb(); - NCollection_Map aReachableNodes; - const bool hasRootProducts = !theGraph.RootProductIds().IsEmpty(); + NCollection_DataMap aVertexRefMap( + static_cast(aNbVertexRefs), + aPermAlloc); + NCollection_DataMap aWireRefMap( + static_cast(aNbWireRefs), + aPermAlloc); + NCollection_DataMap aFaceRefMap( + static_cast(aNbFaceRefs), + aPermAlloc); + NCollection_DataMap aShellRefMap( + static_cast(aNbShellRefs), + aPermAlloc); + NCollection_DataMap aChildRefMap( + static_cast(aNbChildRefs), + aPermAlloc); + NCollection_DataMap aSolidRefMap( + static_cast(aNbSolidRefs), + aPermAlloc); + NCollection_DataMap anOccurrenceRefMap( + static_cast(aNbOccurrenceRefs), + aPermAlloc); + + NCollection_Map aReachableNodes( + static_cast(theGraph.Topo().Gen().NbNodes()), + aPermAlloc); + const bool hasRootProducts = !theGraph.RootProductIds().IsEmpty(); if (hasRootProducts) { collectReachableNodesFromRootProducts(theGraph, aReachableNodes); @@ -355,16 +314,34 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const bindActiveIndexMap(theGraph, aCompoundMap, aReachableFilter); bindActiveIndexMap(theGraph, aCompSolidMap, aReachableFilter); - const bool wasHistoryEnabled = theGraph.History().IsEnabled(); - theGraph.History().SetEnabled(theOptions.HistoryMode); + if (hasRootProducts) + { + const uint32_t aMappedNodes = static_cast( + aVertexMap.Size() + anEdgeMap.Size() + aWireMap.Size() + aFaceMap.Size() + aShellMap.Size() + + aSolidMap.Size() + aCompoundMap.Size() + aCompSolidMap.Size()); + aResult.NbUnmappedActiveDefs = countActiveDefs(theGraph) + + countActiveDefs(theGraph) + + countActiveDefs(theGraph) + + countActiveDefs(theGraph) + + countActiveDefs(theGraph) + + countActiveDefs(theGraph) + + countActiveDefs(theGraph) + + countActiveDefs(theGraph) + - aMappedNodes; + } + + BRepGraph_LayerHistory& aHistory = *theGraph.LayerRegistry().Ensure(); + const bool wasHistoryEnabled = aHistory.IsEnabled(); + aHistory.SetEnabled(theOptions.HistoryMode); const BRepGraph_Data* aGraphData = theGraph.data(); - const uint32_t aGeneration = aGraphData->myGeneration.load(std::memory_order_relaxed); + const uint32_t aGeneration = aGraphData->myIncStorage.Generation(); // Construct a fresh graph and rebuild bottom-up. // Geometry nodes (Surface, Curve) are automatically created through Add/Add. BRepGraph aNewGraph; BRepGraph_Data* aNewGraphData = aNewGraph.data(); - aNewGraphData->myGeneration.store(aGeneration, std::memory_order_relaxed); + aNewGraphData->myIncStorage.SetGeneration(aGeneration); + aNewGraphData->myIncStorage.SetGraphGUID(aGraphData->myIncStorage.GraphGUID()); // Helper lambda for remapping NodeIds. auto remapId = [&](const BRepGraph_NodeId& theId) -> BRepGraph_NodeId { @@ -376,7 +353,8 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aShellMap, aSolidMap, aCompoundMap, - aCompSolidMap); + aCompSolidMap, + aCoEdgeMap); }; // Add topology defs bottom-up (Vertex -> Edge -> Wire -> Face -> Shell -> Solid). @@ -389,7 +367,26 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const } const BRepGraphInc::VertexDef& anOldVtx = anIt.Current(); - (void)aNewGraph.Editor().Vertices().Add(anOldVtx.Point, anOldVtx.Tolerance); + const BRepGraph_VertexId aNewVertexId = + aNewGraph.Editor().Vertices().Add(anOldVtx.Point, anOldVtx.Tolerance); + const BRepGraph_VertexId* anExpectedId = aVertexMap.Seek(anIt.CurrentId()); + Standard_ASSERT_RAISE(anExpectedId != nullptr && aNewVertexId == *anExpectedId, + "BRepGraph_Compact: unexpected vertex id"); + } + + // Copy OwnGen/SubtreeGen for vertices. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_VertexId anOldId = anIt.CurrentId(); + const BRepGraph_VertexId* aNewId = aVertexMap.Seek(anOldId); + if (aNewId == nullptr) + { + continue; + } + BRepGraphInc::VertexDef& aNewDef = aNewGraphData->myIncStorage.ChangeVertex(*aNewId); + aNewDef.OwnGen = anIt.Current().OwnGen; + aNewDef.SubtreeGen = anIt.Current().SubtreeGen; + aNewDef.UID = anIt.Current().UID; } // Edges. @@ -402,35 +399,37 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const } const BRepGraphInc::EdgeDef& anOldEdge = anIt.Current(); - const BRepGraph_VertexId aOldStartId = + const BRepGraph_VertexRefId aOldStartRefId = BRepGraph_Tool::Edge::StartVertexId(theGraph, anOldEdgeId); - const BRepGraph_VertexId aNewStart = aOldStartId.IsValid() - ? BRepGraph_VertexId::FromNodeId(remapId(aOldStartId)) - : BRepGraph_VertexId(); - const BRepGraph_VertexId aOldEndId = BRepGraph_Tool::Edge::EndVertexId(theGraph, anOldEdgeId); - const BRepGraph_VertexId aNewEnd = aOldEndId.IsValid() - ? BRepGraph_VertexId::FromNodeId(remapId(aOldEndId)) - : BRepGraph_VertexId(); + const BRepGraph_VertexId aOldStartId = + aOldStartRefId.IsValid() ? theGraph.Refs().Vertices().Entry(aOldStartRefId).ChildVertexId + : BRepGraph_VertexId(); + const BRepGraph_VertexId aNewStart = aOldStartId.IsValid() + ? BRepGraph_VertexId::FromNodeId(remapId(aOldStartId)) + : BRepGraph_VertexId(); + const BRepGraph_VertexRefId aOldEndRefId = + BRepGraph_Tool::Edge::EndVertexId(theGraph, anOldEdgeId); + const BRepGraph_VertexId aOldEndId = + aOldEndRefId.IsValid() ? theGraph.Refs().Vertices().Entry(aOldEndRefId).ChildVertexId + : BRepGraph_VertexId(); + const BRepGraph_VertexId aNewEnd = aOldEndId.IsValid() + ? BRepGraph_VertexId::FromNodeId(remapId(aOldEndId)) + : BRepGraph_VertexId(); // Get the curve handle if available. const occ::handle& aCurve = BRepGraph_Tool::Edge::Curve(theGraph, anOldEdgeId); + const auto [aParamFirst, aParamLast] = BRepGraph_Tool::Edge::Range(theGraph, anOldEdgeId); + const BRepGraph_EdgeId aNewEdgeId = aNewGraph.Editor().Edges().Add(aNewStart, aNewEnd, aCurve, - anOldEdge.ParamFirst, - anOldEdge.ParamLast, + aParamFirst, + aParamLast, anOldEdge.Tolerance); // Copy edge properties. BRepGraph_MutGuard aNewEdge = aNewGraph.Editor().Edges().Mut(aNewEdgeId); - aNewGraph.Editor().Edges().SetDegenerate(aNewEdge, anOldEdge.IsDegenerate); - - aNewGraph.Editor().Edges().SetIsClosed(aNewEdge, anOldEdge.IsClosed); - - aNewGraph.Editor().Edges().SetSameParameter(aNewEdge, anOldEdge.SameParameter); - - aNewGraph.Editor().Edges().SetSameRange(aNewEdge, anOldEdge.SameRange); if (anOldEdge.StartVertexRefId.IsValid() && aNewEdge->StartVertexRefId.IsValid()) { const BRepGraphInc::VertexRef& anOldStartRef = @@ -439,7 +438,6 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aNewGraph.Editor().Vertices().MutRef(aNewEdge->StartVertexRefId); aNewGraph.Editor().Vertices().SetRefOrientation(aNewStartRef, anOldStartRef.Orientation); - aNewGraph.Editor().Vertices().SetRefLocalLocation(aNewStartRef, anOldStartRef.LocalLocation); aVertexRefMap.Bind(anOldEdge.StartVertexRefId, aNewEdge->StartVertexRefId); } @@ -451,96 +449,94 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aNewGraph.Editor().Vertices().MutRef(aNewEdge->EndVertexRefId); aNewGraph.Editor().Vertices().SetRefOrientation(aNewEndRef, anOldEndRef.Orientation); - aNewGraph.Editor().Vertices().SetRefLocalLocation(aNewEndRef, anOldEndRef.LocalLocation); aVertexRefMap.Bind(anOldEdge.EndVertexRefId, aNewEdge->EndVertexRefId); } + } - for (const BRepGraph_VertexRefId& anOldInternalRefId : anOldEdge.InternalVertexRefIds) + // Copy OwnGen/SubtreeGen for edges. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_EdgeId anOldId = anIt.CurrentId(); + const BRepGraph_EdgeId* aNewId = anEdgeMap.Seek(anOldId); + if (aNewId == nullptr) { - const BRepGraphInc::VertexRef& anOldInternalRef = - theGraph.Refs().Vertices().Entry(anOldInternalRefId); - const BRepGraph_VertexId aNewVertexId = - BRepGraph_VertexId::FromNodeId(remapId(anOldInternalRef.VertexDefId)); - if (!aNewVertexId.IsValid()) - { - continue; - } - - const BRepGraph_VertexRefId aNewInternalRefId = - aNewGraph.Editor().Edges().AddInternalVertex(aNewEdgeId, - aNewVertexId, - anOldInternalRef.Orientation); - if (!aNewInternalRefId.IsValid()) - { - continue; - } - - BRepGraph_MutGuard aNewInternalRef = - aNewGraph.Editor().Vertices().MutRef(aNewInternalRefId); - aNewGraph.Editor().Vertices().SetRefLocalLocation(aNewInternalRef, - anOldInternalRef.LocalLocation); - aVertexRefMap.Bind(anOldInternalRefId, aNewInternalRefId); + continue; } + BRepGraphInc::EdgeDef& aNewDef = aNewGraphData->myIncStorage.ChangeEdge(*aNewId); + aNewDef.OwnGen = anIt.Current().OwnGen; + aNewDef.SubtreeGen = anIt.Current().SubtreeGen; + aNewDef.UID = anIt.Current().UID; } // PCurves (added after edges and faces are known). // We need faces first, so do PCurves after face creation. // Wires. + NCollection_LinearVector aWireOldCoEdges(64); + NCollection_LinearVector aNewCoEdgeIds(64); for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) { + aWireOldCoEdges.Clear(false); + aNewCoEdgeIds.Clear(false); + const BRepGraph_WireId anOldWireId = anIt.CurrentId(); if (aWireMap.Seek(anOldWireId) == nullptr) { continue; } + const BRepGraph_WireId aNewWireId = aNewGraph.Editor().Wires().Add(aNewCoEdgeIds.ToArray1()); + Standard_ASSERT_RAISE(aNewWireId == *aWireMap.Seek(anOldWireId), + "BRepGraph_Compact: unexpected wire id"); - NCollection_DynamicArray> aNewEntries; - NCollection_DynamicArray anOldCoEdges; - NCollection_DynamicArray anOldCoEdgeRefs; - for (BRepGraph_RefsCoEdgeOfWire aRefIt(theGraph, anOldWireId); aRefIt.More(); aRefIt.Next()) + for (BRepGraph_CoEdgesOfWire aRefIt(theGraph, anOldWireId); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::CoEdgeRef& aCR = theGraph.Refs().CoEdges().Entry(aRefIt.CurrentId()); const BRepGraphInc::CoEdgeDef& aCoEdge = - theGraph.Topo().CoEdges().Definition(aCR.CoEdgeDefId); - const BRepGraph_EdgeId aNewEdgeDefId = - BRepGraph_EdgeId::FromNodeId(remapId(aCoEdge.EdgeDefId)); - if (aNewEdgeDefId.IsValid()) + theGraph.Topo().CoEdges().Definition(aRefIt.CurrentId()); + const BRepGraph_EdgeId aNewChildEdgeId = + BRepGraph_EdgeId::FromNodeId(remapId(aCoEdge.ChildEdgeId)); + if (aNewChildEdgeId.IsValid()) { - aNewEntries.Append(std::make_pair(aNewEdgeDefId, aCoEdge.Orientation)); - anOldCoEdges.Append(aCR.CoEdgeDefId); - anOldCoEdgeRefs.Append(aRefIt.CurrentId()); + const BRepGraph_CoEdgeId aNewCoEdgeId = + aNewGraphData->myIncStorage.CreateCoEdgeUse(aNewWireId, + aNewChildEdgeId, + BRepGraph_FaceId(), + aCoEdge.Orientation); + aNewGraph.allocateUID(aNewCoEdgeId); + aNewCoEdgeIds.Append(aNewCoEdgeId); + aWireOldCoEdges.Append(aRefIt.CurrentId()); } } - const BRepGraph_WireId aNewWireId = aNewGraph.Editor().Wires().Add(aNewEntries); - const NCollection_DynamicArray& aNewCoEdgeRefs = - aNewGraph.Topo().Wires().Definition(aNewWireId).CoEdgeRefIds; - for (size_t aRefIdx = 0; aRefIdx < anOldCoEdgeRefs.Size() && aRefIdx < aNewCoEdgeRefs.Size(); + for (size_t aRefIdx = 0; aRefIdx < aWireOldCoEdges.Size() && aRefIdx < aNewCoEdgeIds.Size(); ++aRefIdx) { - const BRepGraphInc::CoEdgeRef& anOldRef = - theGraph.Refs().CoEdges().Entry(anOldCoEdgeRefs.Value(aRefIdx)); - BRepGraph_MutGuard aNewRef = - aNewGraph.Editor().CoEdges().MutRef(aNewCoEdgeRefs.Value(aRefIdx)); - aNewGraph.Editor().CoEdges().SetRefLocalLocation(aNewRef, anOldRef.LocalLocation); - // Build aCoEdgeMap from actual CoEdgeDef IDs returned by the builder, - // not from a predicted Nb()-based offset which can be wrong. - const BRepGraphInc::CoEdgeRef& aNewCoEdgeRef = - aNewGraph.Refs().CoEdges().Entry(aNewCoEdgeRefs.Value(aRefIdx)); - aCoEdgeMap.Bind(anOldCoEdges.Value(aRefIdx), aNewCoEdgeRef.CoEdgeDefId); - // Track CoEdgeRef index remap for RefUID transfer. - aCoEdgeRefMap.Bind(anOldCoEdgeRefs.Value(aRefIdx), aNewCoEdgeRefs.Value(aRefIdx)); + aCoEdgeMap.Bind(aWireOldCoEdges.Value(aRefIdx), aNewCoEdgeIds.Value(aRefIdx)); } + } - BRepGraph_MutGuard aNewWire = aNewGraph.Editor().Wires().Mut(aNewWireId); - aNewGraph.Editor().Wires().SetIsClosed(aNewWire, - isWireClosedByIncidence(aNewGraph, aNewWireId)); + // Copy OwnGen/SubtreeGen for wires. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_WireId anOldId = anIt.CurrentId(); + const BRepGraph_WireId* aNewId = aWireMap.Seek(anOldId); + if (aNewId == nullptr) + { + continue; + } + BRepGraphInc::WireDef& aNewDef = aNewGraphData->myIncStorage.ChangeWire(*aNewId); + aNewDef.OwnGen = anIt.Current().OwnGen; + aNewDef.SubtreeGen = anIt.Current().SubtreeGen; + aNewDef.UID = anIt.Current().UID; } // Faces. + NCollection_LinearVector aFaceNextWires(64); + NCollection_LinearVector aFaceOldWireRefs(64); for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) { + aFaceNextWires.Clear(false); + aFaceOldWireRefs.Clear(false); + const BRepGraph_FaceId anOldFaceId = anIt.CurrentId(); if (aFaceMap.Seek(anOldFaceId) == nullptr) { @@ -550,90 +546,106 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const const occ::handle& aSurf = BRepGraph_Tool::Face::Surface(theGraph, anOldFaceId); - // Find outer wire from incidence ref entries. - BRepGraph_WireId aNewOuterWire; - NCollection_DynamicArray aNewInnerWires; - BRepGraph_WireRefId anOldOuterWireRef; - NCollection_DynamicArray anOldInnerWireRefs; + BRepGraph_WireId aFirstWire; for (BRepGraph_RefsWireOfFace aRefIt(theGraph, anOldFaceId); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::WireRef& aWR = theGraph.Refs().Wires().Entry(aRefIt.CurrentId()); - const BRepGraph_WireId aRemapped = BRepGraph_WireId::FromNodeId(remapId(aWR.WireDefId)); + const BRepGraphInc::WireRef& aWR = theGraph.Refs().Wires().Entry(aRefIt.CurrentId()); + const BRepGraph_WireId aRemapped = BRepGraph_WireId::FromNodeId(remapId(aWR.ChildWireId)); if (!aRemapped.IsValid()) { continue; } - if (aWR.IsOuter) + if (!aFirstWire.IsValid()) { - aNewOuterWire = aRemapped; - anOldOuterWireRef = aRefIt.CurrentId(); + aFirstWire = aRemapped; } else { - aNewInnerWires.Append(aRemapped); - anOldInnerWireRefs.Append(aRefIt.CurrentId()); + aFaceNextWires.Append(aRemapped); } + aFaceOldWireRefs.Append(aRefIt.CurrentId()); } - const BRepGraph_FaceId aNewFaceId = - aNewGraph.Editor().Faces().Add(aSurf, aNewOuterWire, aNewInnerWires, anOldFace.Tolerance); + const BRepGraph_FaceId aNewFaceId = aNewGraph.Editor().Faces().Add(aSurf, + aFirstWire, + aFaceNextWires.ToArray1(), + anOldFace.Tolerance); BRepGraph_MutGuard aNewFace = aNewGraph.Editor().Faces().Mut(aNewFaceId); - aNewGraph.Editor().Faces().SetNaturalRestriction(aNewFace, anOldFace.NaturalRestriction); - const NCollection_DynamicArray& aNewWireRefs = aNewFace->WireRefIds; - if (anOldOuterWireRef.IsValid() && !aNewWireRefs.IsEmpty()) + const NCollection_LinearVector& aNewWireRefs = + aNewGraph.Topo().Faces().Relations(aNewFaceId).WireRefIds; + + size_t aWireRefIdx = 0; + for (const BRepGraph_WireRefId& aNewRefId : aNewWireRefs) { - const BRepGraphInc::WireRef& anOldOuterRef = theGraph.Refs().Wires().Entry(anOldOuterWireRef); - BRepGraph_MutGuard aNewOuterRef = - aNewGraph.Editor().Wires().MutRef(aNewWireRefs.First()); - aNewGraph.Editor().Wires().SetRefIsOuter(aNewOuterRef, anOldOuterRef.IsOuter); - - aNewGraph.Editor().Wires().SetRefOrientation(aNewOuterRef, anOldOuterRef.Orientation); - - aNewGraph.Editor().Wires().SetRefLocalLocation(aNewOuterRef, anOldOuterRef.LocalLocation); - aWireRefMap.Bind(anOldOuterWireRef, aNewWireRefs.First()); - } - - for (size_t anInnerIdx = 0; - anInnerIdx < anOldInnerWireRefs.Size() && anInnerIdx + 1 < aNewWireRefs.Size(); - ++anInnerIdx) - { - const BRepGraphInc::WireRef& anOldInnerRef = - theGraph.Refs().Wires().Entry(anOldInnerWireRefs.Value(anInnerIdx)); - BRepGraph_MutGuard aNewInnerRef = - aNewGraph.Editor().Wires().MutRef(aNewWireRefs.Value(anInnerIdx + 1)); - aNewGraph.Editor().Wires().SetRefIsOuter(aNewInnerRef, anOldInnerRef.IsOuter); - - aNewGraph.Editor().Wires().SetRefOrientation(aNewInnerRef, anOldInnerRef.Orientation); - - aNewGraph.Editor().Wires().SetRefLocalLocation(aNewInnerRef, anOldInnerRef.LocalLocation); - aWireRefMap.Bind(anOldInnerWireRefs.Value(anInnerIdx), aNewWireRefs.Value(anInnerIdx + 1)); - } - - for (const BRepGraph_VertexRefId& anOldVertexRefId : anOldFace.VertexRefIds) - { - const BRepGraphInc::VertexRef& anOldVertexRef = - theGraph.Refs().Vertices().Entry(anOldVertexRefId); - const BRepGraph_VertexId aNewVertexId = - BRepGraph_VertexId::FromNodeId(remapId(anOldVertexRef.VertexDefId)); - if (!aNewVertexId.IsValid()) + if (aWireRefIdx >= aFaceOldWireRefs.Size()) { - continue; + break; } + const BRepGraph_WireRefId anOldRefId = aFaceOldWireRefs.Value(aWireRefIdx++); + const BRepGraphInc::WireRef& anOldRef = theGraph.Refs().Wires().Entry(anOldRefId); + BRepGraph_MutGuard aNewRef = + aNewGraph.Editor().Wires().MutRef(aNewRefId); + aNewGraph.Editor().Wires().SetRefOrientation(aNewRef, anOldRef.Orientation); + aWireRefMap.Bind(anOldRefId, aNewRefId); + } + } - const BRepGraph_VertexRefId aNewVertexRefId = - aNewGraph.Editor().Faces().AddVertex(aNewFaceId, aNewVertexId, anOldVertexRef.Orientation); - if (!aNewVertexRefId.IsValid()) + // Copy OwnGen/SubtreeGen for faces. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_FaceId anOldId = anIt.CurrentId(); + const BRepGraph_FaceId* aNewId = aFaceMap.Seek(anOldId); + if (aNewId == nullptr) + { + continue; + } + BRepGraphInc::FaceDef& aNewDef = aNewGraphData->myIncStorage.ChangeFace(*aNewId); + aNewDef.OwnGen = anIt.Current().OwnGen; + aNewDef.SubtreeGen = anIt.Current().SubtreeGen; + // Preserve UID from old entity. + aNewDef.UID = anIt.Current().UID; + } + + // Populate aCoEdgeMap for free coedges (coedges created by AddPCurve that are + // not bound to any wire). Wire coedges were already mapped during wire rebuild. + // Free coedges without wire membership can be remapped by matching + // (ChildEdgeId, FaceId) pairs against the new graph's coedge set. + // Note: the new graph's derived relation tables have not yet been rebuilt at this + // point - see RebuildDerivedRelations() further below - so a direct linear scan + // is used. Free coedges are rare in practice (only PCurves added outside + // wires create them) so the O(old_free * new_total) cost is bounded. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_CoEdgeId anOldCoEdgeId = anIt.CurrentId(); + if (aCoEdgeMap.IsBound(anOldCoEdgeId)) + { + continue; + } + const BRepGraphInc::CoEdgeDef& anOldCE = anIt.Current(); + if (theGraph.incStorage().IsRemoved(anOldCoEdgeId)) + { + continue; + } + const BRepGraph_EdgeId aRemappedEdge = + BRepGraph_EdgeId::FromNodeId(remapId(anOldCE.ChildEdgeId)); + const BRepGraph_FaceId aRemappedFace = BRepGraph_FaceId::FromNodeId(remapId(anOldCE.FaceId)); + if (!aRemappedEdge.IsValid() || !aRemappedFace.IsValid()) + { + continue; + } + for (BRepGraph_Iterator aNewIt(aNewGraph); aNewIt.More(); + aNewIt.Next()) + { + const BRepGraphInc::CoEdgeDef& aNewCE = aNewIt.Current(); + if (!aNewGraph.incStorage().IsRemoved(aNewIt.CurrentId()) + && aNewCE.ChildEdgeId == aRemappedEdge && aNewCE.FaceId == aRemappedFace + && !aCoEdgeMap.IsBound(aNewIt.CurrentId())) { - continue; + aCoEdgeMap.Bind(anOldCoEdgeId, aNewIt.CurrentId()); + break; } - - BRepGraph_MutGuard aNewVertexRef = - aNewGraph.Editor().Vertices().MutRef(aNewVertexRefId); - aNewGraph.Editor().Vertices().SetRefLocalLocation(aNewVertexRef, - anOldVertexRef.LocalLocation); - aVertexRefMap.Bind(anOldVertexRefId, aNewVertexRefId); } } @@ -651,26 +663,17 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const BRepGraph_MutGuard aNewCoEdge = aNewGraph.Editor().CoEdges().Mut(*aNewCoEdgeId); - aNewGraph.Editor().CoEdges().SetFaceDefId( + aNewGraph.Editor().CoEdges().SetFaceId( aNewCoEdge, - BRepGraph_FaceId::FromNodeId(remapId(anOldCoEdge.FaceDefId))); + BRepGraph_FaceId::FromNodeId(remapId(anOldCoEdge.FaceId))); aNewGraph.Editor().CoEdges().SetOrientation(aNewCoEdge, anOldCoEdge.Orientation); - aNewGraph.Editor().CoEdges().SetParamRange(aNewCoEdge, - anOldCoEdge.ParamFirst, - anOldCoEdge.ParamLast); - - aNewGraph.Editor().CoEdges().SetUVBox(aNewCoEdge, anOldCoEdge.UV1, anOldCoEdge.UV2); - - // Continuity (inter-face and seam) lives in BRepGraph_LayerRegularity and - // is migrated by the layer's own remapping hooks; nothing to copy here. - // If the owning face was removed, keep the coedge as free-wire usage only. - // Drop face-bound parametric payload (PCurve/UV/continuity) to avoid stale + // Drop face-bound parametric representation (PCurve/UV/continuity) to avoid stale // face references causing reconstruction/meshing corruption. - if (!aNewCoEdge->FaceDefId.IsValid()) + if (!aNewCoEdge->FaceId.IsValid()) { - aNewGraph.Editor().CoEdges().ClearPCurveBinding(aNewCoEdge); + aNewGraph.Editor().CoEdges().ResetPCurveBinding(aNewCoEdge); continue; } @@ -680,16 +683,22 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const BRepGraph_Tool::CoEdge::PCurve(theGraph, anOldCoEdgeId); if (!anOldPCurve.IsNull()) { - // Use the builder API to create the Curve2DRep - this ensures internal - // registration tables are updated correctly (as documented in EditorView.hxx). - const BRepGraph_Curve2DRepId aNewRepId = - aNewGraph.Editor().CoEdges().CreateCurve2DRep(anOldPCurve); - if (aNewRepId.IsValid()) - { - aNewGraph.Editor().CoEdges().SetCurve2DRepId(aNewCoEdge, aNewRepId); - } + const auto [aCoEdgeParamFirst, aCoEdgeParamLast] = + BRepGraph_Tool::CoEdge::Range(theGraph, anOldCoEdgeId); + aNewGraph.Editor().CoEdges().SetPCurve(*aNewCoEdgeId, + anOldPCurve, + aCoEdgeParamFirst, + aCoEdgeParamLast); } } + + // SetParamRange for coedges without PCurve (face-bound range only). + if (!anOldCoEdge.Curve2DRepId.IsValid()) + { + const auto [aCoEdgeParamFirst, aCoEdgeParamLast] = + BRepGraph_Tool::CoEdge::Range(theGraph, anOldCoEdgeId); + aNewGraph.Editor().CoEdges().SetParamRange(aNewCoEdge, aCoEdgeParamFirst, aCoEdgeParamLast); + } } // Shells. @@ -706,46 +715,33 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const // Add faces to shell via incidence ref entries. for (BRepGraph_RefsFaceOfShell aRefIt(theGraph, anOldShellId); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::FaceRef& aFR = theGraph.Refs().Faces().Entry(aRefIt.CurrentId()); - const BRepGraph_FaceId aNewFace = BRepGraph_FaceId::FromNodeId(remapId(aFR.FaceDefId)); + const BRepGraphInc::FaceRef& aFR = theGraph.Refs().Faces().Entry(aRefIt.CurrentId()); + const BRepGraph_FaceId aNewFace = BRepGraph_FaceId::FromNodeId(remapId(aFR.ChildFaceId)); if (aNewFace.IsValid()) { const BRepGraph_FaceRefId aNewFaceRefId = - aNewGraph.Editor().Shells().AddFace(aNewShellId, aNewFace, aFR.Orientation); + aNewGraph.Editor().Shells().Append(aNewShellId, aNewFace, aFR.Orientation); if (aNewFaceRefId.IsValid()) { - BRepGraph_MutGuard aNewFaceRef = - aNewGraph.Editor().Faces().MutRef(aNewFaceRefId); - aNewGraph.Editor().Faces().SetRefLocalLocation(aNewFaceRef, aFR.LocalLocation); aFaceRefMap.Bind(aRefIt.CurrentId(), aNewFaceRefId); } } } + } - for (BRepGraph_RefsChildOfShell aRefIt(theGraph, anOldShellId); aRefIt.More(); aRefIt.Next()) + // Copy OwnGen/SubtreeGen for shells. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_ShellId anOldId = anIt.CurrentId(); + const BRepGraph_ShellId* aNewId = aShellMap.Seek(anOldId); + if (aNewId == nullptr) { - const BRepGraphInc::ChildRef& aCR = theGraph.Refs().Children().Entry(aRefIt.CurrentId()); - const BRepGraph_NodeId aNewChild = remapId(aCR.ChildDefId); - if (!aNewChild.IsValid()) - { - continue; - } - const BRepGraph_ChildRefId aNewChildRefId = - aNewGraph.Editor().Shells().AddChild(aNewShellId, aNewChild, aCR.Orientation); - if (!aNewChildRefId.IsValid()) - { - continue; - } - BRepGraph_MutGuard aNewChildRef = - aNewGraph.Editor().Gen().MutChildRef(aNewChildRefId); - aNewGraph.Editor().Gen().SetChildRefLocalLocation(aNewChildRef, aCR.LocalLocation); - aChildRefMap.Bind(aRefIt.CurrentId(), aNewChildRefId); + continue; } - - BRepGraph_MutGuard aNewShell = - aNewGraph.Editor().Shells().Mut(aNewShellId); - aNewGraph.Editor().Shells().SetIsClosed(aNewShell, - isShellClosedByIncidence(aNewGraph, aNewShellId)); + BRepGraphInc::ShellDef& aNewDef = aNewGraphData->myIncStorage.ChangeShell(*aNewId); + aNewDef.OwnGen = anIt.Current().OwnGen; + aNewDef.SubtreeGen = anIt.Current().SubtreeGen; + aNewDef.UID = anIt.Current().UID; } // Solids. @@ -762,133 +758,162 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const for (BRepGraph_RefsShellOfSolid aRefIt(theGraph, anOldSolidId); aRefIt.More(); aRefIt.Next()) { const BRepGraphInc::ShellRef& aSR = theGraph.Refs().Shells().Entry(aRefIt.CurrentId()); - const BRepGraph_ShellId aNewShell = BRepGraph_ShellId::FromNodeId(remapId(aSR.ShellDefId)); + const BRepGraph_ShellId aNewShell = BRepGraph_ShellId::FromNodeId(remapId(aSR.ChildShellId)); if (aNewShell.IsValid()) { const BRepGraph_ShellRefId aNewShellRefId = - aNewGraph.Editor().Solids().AddShell(aNewSolidId, aNewShell, aSR.Orientation); + aNewGraph.Editor().Solids().Append(aNewSolidId, aNewShell, aSR.Orientation); if (aNewShellRefId.IsValid()) { - BRepGraph_MutGuard aNewShellRef = - aNewGraph.Editor().Shells().MutRef(aNewShellRefId); - aNewGraph.Editor().Shells().SetRefLocalLocation(aNewShellRef, aSR.LocalLocation); aShellRefMap.Bind(aRefIt.CurrentId(), aNewShellRefId); } } } + } - for (BRepGraph_RefsChildOfSolid aRefIt(theGraph, anOldSolidId); aRefIt.More(); aRefIt.Next()) + // Copy OwnGen/SubtreeGen for solids. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_SolidId anOldId = anIt.CurrentId(); + const BRepGraph_SolidId* aNewId = aSolidMap.Seek(anOldId); + if (aNewId == nullptr) { - const BRepGraphInc::ChildRef& aCR = theGraph.Refs().Children().Entry(aRefIt.CurrentId()); - const BRepGraph_NodeId aNewChild = remapId(aCR.ChildDefId); - if (!aNewChild.IsValid()) - { - continue; - } - const BRepGraph_ChildRefId aNewChildRefId = - aNewGraph.Editor().Solids().AddChild(aNewSolidId, aNewChild, aCR.Orientation); - if (!aNewChildRefId.IsValid()) - { - continue; - } - BRepGraph_MutGuard aNewChildRef = - aNewGraph.Editor().Gen().MutChildRef(aNewChildRefId); - aNewGraph.Editor().Gen().SetChildRefLocalLocation(aNewChildRef, aCR.LocalLocation); - aChildRefMap.Bind(aRefIt.CurrentId(), aNewChildRefId); + continue; } + BRepGraphInc::SolidDef& aNewDef = aNewGraphData->myIncStorage.ChangeSolid(*aNewId); + aNewDef.OwnGen = anIt.Current().OwnGen; + aNewDef.SubtreeGen = anIt.Current().SubtreeGen; + aNewDef.UID = anIt.Current().UID; } // Compounds. + NCollection_LinearVector aCompChildren(64); + NCollection_LinearVector aCompOldChildRefs(64); for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) { + aCompChildren.Clear(false); + aCompOldChildRefs.Clear(false); + const BRepGraph_CompoundId anOldCompoundId = anIt.CurrentId(); if (aCompoundMap.Seek(anOldCompoundId) == nullptr) { continue; } - NCollection_DynamicArray aNewChildren; - NCollection_DynamicArray anOldChildRefs; for (BRepGraph_RefsChildOfCompound aRefIt(theGraph, anOldCompoundId); aRefIt.More(); aRefIt.Next()) { const BRepGraphInc::ChildRef& aCR = theGraph.Refs().Children().Entry(aRefIt.CurrentId()); - const BRepGraph_NodeId aNewChild = remapId(aCR.ChildDefId); + const BRepGraph_NodeId aNewChild = remapId(aCR.ChildNodeId); if (aNewChild.IsValid()) { - aNewChildren.Append(aNewChild); - anOldChildRefs.Append(aRefIt.CurrentId()); + aCompChildren.Append(aNewChild); + aCompOldChildRefs.Append(aRefIt.CurrentId()); } } - const BRepGraph_CompoundId aNewCompoundId = aNewGraph.Editor().Compounds().Add(aNewChildren); + const BRepGraph_CompoundId aNewCompoundId = + aNewGraph.Editor().Compounds().Add(aCompChildren.ToArray1()); if (aNewCompoundId.IsValid()) { - const NCollection_DynamicArray& aNewChildRefs = - aNewGraph.Topo().Compounds().Definition(aNewCompoundId).ChildRefIds; - for (size_t aRefIdx = 0; aRefIdx < anOldChildRefs.Size() && aRefIdx < aNewChildRefs.Size(); + const NCollection_LinearVector& aNewChildRefs = + aNewGraph.Topo().Compounds().Relations(aNewCompoundId).ChildRefIds; + for (size_t aRefIdx = 0; aRefIdx < aCompOldChildRefs.Size() && aRefIdx < aNewChildRefs.Size(); ++aRefIdx) { const BRepGraphInc::ChildRef& anOldRef = - theGraph.Refs().Children().Entry(anOldChildRefs.Value(aRefIdx)); + theGraph.Refs().Children().Entry(aCompOldChildRefs.Value(aRefIdx)); BRepGraph_MutGuard aNewRef = aNewGraph.Editor().Gen().MutChildRef(aNewChildRefs.Value(aRefIdx)); aNewGraph.Editor().Gen().SetChildRefOrientation(aNewRef, anOldRef.Orientation); aNewGraph.Editor().Gen().SetChildRefLocalLocation(aNewRef, anOldRef.LocalLocation); - aChildRefMap.Bind(anOldChildRefs.Value(aRefIdx), aNewChildRefs.Value(aRefIdx)); + aChildRefMap.Bind(aCompOldChildRefs.Value(aRefIdx), aNewChildRefs.Value(aRefIdx)); } } } + // Copy OwnGen/SubtreeGen for compounds. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_CompoundId anOldId = anIt.CurrentId(); + const BRepGraph_CompoundId* aNewId = aCompoundMap.Seek(anOldId); + if (aNewId == nullptr) + { + continue; + } + BRepGraphInc::CompoundDef& aNewDef = aNewGraphData->myIncStorage.ChangeCompound(*aNewId); + aNewDef.OwnGen = anIt.Current().OwnGen; + aNewDef.SubtreeGen = anIt.Current().SubtreeGen; + aNewDef.UID = anIt.Current().UID; + } + // CompSolids. + NCollection_LinearVector aCSSolids(64); + NCollection_LinearVector aCSOldSolidRefs(64); for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) { + aCSSolids.Clear(false); + aCSOldSolidRefs.Clear(false); + const BRepGraph_CompSolidId anOldCompSolidId = anIt.CurrentId(); if (aCompSolidMap.Seek(anOldCompSolidId) == nullptr) { continue; } - NCollection_DynamicArray aNewSolids; - NCollection_DynamicArray anOldSolidRefs; for (BRepGraph_RefsSolidOfCompSolid aRefIt(theGraph, anOldCompSolidId); aRefIt.More(); aRefIt.Next()) { const BRepGraphInc::SolidRef& aSR = theGraph.Refs().Solids().Entry(aRefIt.CurrentId()); - const BRepGraph_SolidId aNewSolid = BRepGraph_SolidId::FromNodeId(remapId(aSR.SolidDefId)); + const BRepGraph_SolidId aNewSolid = BRepGraph_SolidId::FromNodeId(remapId(aSR.ChildSolidId)); if (aNewSolid.IsValid()) { - aNewSolids.Append(aNewSolid); - anOldSolidRefs.Append(aRefIt.CurrentId()); + aCSSolids.Append(aNewSolid); + aCSOldSolidRefs.Append(aRefIt.CurrentId()); } } - const BRepGraph_CompSolidId aNewCompSolidId = aNewGraph.Editor().CompSolids().Add(aNewSolids); + const BRepGraph_CompSolidId aNewCompSolidId = + aNewGraph.Editor().CompSolids().Add(aCSSolids.ToArray1()); if (aNewCompSolidId.IsValid()) { - const NCollection_DynamicArray& aNewSolidRefs = - aNewGraph.Topo().CompSolids().Definition(aNewCompSolidId).SolidRefIds; - for (size_t aRefIdx = 0; aRefIdx < anOldSolidRefs.Size() && aRefIdx < aNewSolidRefs.Size(); + const NCollection_LinearVector& aNewSolidRefs = + aNewGraph.Topo().CompSolids().Relations(aNewCompSolidId).SolidRefIds; + for (size_t aRefIdx = 0; aRefIdx < aCSOldSolidRefs.Size() && aRefIdx < aNewSolidRefs.Size(); ++aRefIdx) { const BRepGraphInc::SolidRef& anOldRef = - theGraph.Refs().Solids().Entry(anOldSolidRefs.Value(aRefIdx)); + theGraph.Refs().Solids().Entry(aCSOldSolidRefs.Value(aRefIdx)); BRepGraph_MutGuard aNewRef = aNewGraph.Editor().Solids().MutRef(aNewSolidRefs.Value(aRefIdx)); aNewGraph.Editor().Solids().SetRefOrientation(aNewRef, anOldRef.Orientation); - aNewGraph.Editor().Solids().SetRefLocalLocation(aNewRef, anOldRef.LocalLocation); - aSolidRefMap.Bind(anOldSolidRefs.Value(aRefIdx), aNewSolidRefs.Value(aRefIdx)); + aSolidRefMap.Bind(aCSOldSolidRefs.Value(aRefIdx), aNewSolidRefs.Value(aRefIdx)); } } } - // Rebuild reverse index from final forward incidence to guarantee compact output consistency. - aNewGraph.incStorage().BuildReverseIndex(); + // Copy OwnGen/SubtreeGen for compsolids. + for (BRepGraph_Iterator anIt(theGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_CompSolidId anOldId = anIt.CurrentId(); + const BRepGraph_CompSolidId* aNewId = aCompSolidMap.Seek(anOldId); + if (aNewId == nullptr) + { + continue; + } + BRepGraphInc::CompSolidDef& aNewDef = aNewGraphData->myIncStorage.ChangeCompSolid(*aNewId); + aNewDef.OwnGen = anIt.Current().OwnGen; + aNewDef.SubtreeGen = anIt.Current().SubtreeGen; + aNewDef.UID = anIt.Current().UID; + } + + // Rebuild relation tables from final incidence to guarantee compact output consistency. + aNewGraph.incStorage().RebuildDerivedRelations(); // Validate rebuilt graph before swapping it into the source graph. // If rebuilt topology is inconsistent, keep source graph unchanged. if (!aNewGraph.Editor().ValidateMutationBoundary()) { aResult.NbNodesAfter = aResult.NbNodesBefore; - theGraph.History().SetEnabled(wasHistoryEnabled); + aHistory.SetEnabled(wasHistoryEnabled); return aResult; } @@ -896,7 +921,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const if (!aCompactValidation.IsValid()) { aResult.NbNodesAfter = aResult.NbNodesBefore; - theGraph.History().SetEnabled(wasHistoryEnabled); + aHistory.SetEnabled(wasHistoryEnabled); return aResult; } @@ -909,9 +934,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const { continue; } - const BRepGraphInc::ProductDef& anOldProduct = - theGraph.Topo().Products().Definition(anOldProductId); - if (anOldProduct.IsRemoved) + if (theGraph.incStorage().IsRemoved(anOldProductId)) { continue; } @@ -926,31 +949,34 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const { // Find the old part-root occurrence ref to preserve its placement. TopLoc_Location anOldRootLoc; - for (const BRepGraph_OccurrenceRefId& anOldOccRefId : anOldProduct.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& anOldOccRefId : + theGraph.Topo().Products().Relations(anOldProductId).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& anOldOccRef = theGraph.Refs().Occurrences().Entry(anOldOccRefId); - if (anOldOccRef.IsRemoved) + if (theGraph.incStorage().IsRemoved(anOldOccRefId)) { continue; } const BRepGraphInc::OccurrenceDef& anOldOccDef = - theGraph.Topo().Occurrences().Definition(anOldOccRef.OccurrenceDefId); - if (!anOldOccDef.IsRemoved && anOldOccDef.ChildDefId.IsValid() - && BRepGraph_NodeId::IsTopologyKind(anOldOccDef.ChildDefId.NodeKind)) + theGraph.Topo().Occurrences().Definition(anOldOccRef.ChildOccurrenceId); + if (!theGraph.incStorage().IsRemoved(anOldOccRef.ChildOccurrenceId) + && anOldOccDef.ChildNodeId.IsValid() + && BRepGraph_NodeId::IsTopologyKind(anOldOccDef.ChildNodeId.NodeKind)) { anOldRootLoc = anOldOccRef.LocalLocation; break; } } - aNewProductId = - aNewGraph.Editor().Products().LinkProductToTopology(aNewShapeRoot, anOldRootLoc); + aNewProductId = aNewGraph.Editor().Products().Add(aNewShapeRoot, anOldRootLoc); + aNewGraph.Editor().Products().AppendDocumentRoot(aNewProductId); } } if (!aNewProductId.IsValid()) { - aNewProductId = aNewGraph.Editor().Products().CreateEmptyProduct(); + aNewProductId = aNewGraph.Editor().Products().Add(); + aNewGraph.Editor().Products().AppendDocumentRoot(aNewProductId); } if (!aNewProductId.IsValid()) @@ -963,26 +989,27 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const // Map part root occurrence (topology child occurrence inside product). BRepGraph_OccurrenceId anOldPartOccurrenceId; BRepGraph_OccurrenceRefId anOldPartOccurrenceRefId; - for (const BRepGraph_OccurrenceRefId& anOldOccRefId : anOldProduct.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& anOldOccRefId : + theGraph.Topo().Products().Relations(anOldProductId).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& anOldOccRef = theGraph.Refs().Occurrences().Entry(anOldOccRefId); - if (anOldOccRef.IsRemoved) + if (theGraph.incStorage().IsRemoved(anOldOccRefId)) { continue; } const BRepGraphInc::OccurrenceDef& anOldOccDef = - theGraph.Topo().Occurrences().Definition(anOldOccRef.OccurrenceDefId); - if (anOldOccDef.IsRemoved) + theGraph.Topo().Occurrences().Definition(anOldOccRef.ChildOccurrenceId); + if (theGraph.incStorage().IsRemoved(anOldOccRef.ChildOccurrenceId)) { continue; } - if (anOldOccDef.ChildDefId.IsValid() - && BRepGraph_NodeId::IsTopologyKind(anOldOccDef.ChildDefId.NodeKind)) + if (anOldOccDef.ChildNodeId.IsValid() + && BRepGraph_NodeId::IsTopologyKind(anOldOccDef.ChildNodeId.NodeKind)) { - anOldPartOccurrenceId = anOldOccRef.OccurrenceDefId; + anOldPartOccurrenceId = anOldOccRef.ChildOccurrenceId; anOldPartOccurrenceRefId = anOldOccRefId; break; } @@ -993,23 +1020,22 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const continue; } - const BRepGraphInc::ProductDef& aNewProduct = - aNewGraph.Topo().Products().Definition(aNewProductId); - for (const BRepGraph_OccurrenceRefId& aNewOccRefId : aNewProduct.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& aNewOccRefId : + aNewGraph.Topo().Products().Relations(aNewProductId).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& aNewOccRef = aNewGraph.Refs().Occurrences().Entry(aNewOccRefId); const BRepGraphInc::OccurrenceDef& aNewOccDef = - aNewGraph.Topo().Occurrences().Definition(aNewOccRef.OccurrenceDefId); - if (aNewOccDef.IsRemoved) + aNewGraph.Topo().Occurrences().Definition(aNewOccRef.ChildOccurrenceId); + if (aNewGraph.incStorage().IsRemoved(aNewOccRef.ChildOccurrenceId)) { continue; } - if (aNewOccDef.ChildDefId.IsValid() - && BRepGraph_NodeId::IsTopologyKind(aNewOccDef.ChildDefId.NodeKind)) + if (aNewOccDef.ChildNodeId.IsValid() + && BRepGraph_NodeId::IsTopologyKind(aNewOccDef.ChildNodeId.NodeKind)) { - anOccurrenceMap.Bind(anOldPartOccurrenceId, aNewOccRef.OccurrenceDefId); + anOccurrenceMap.Bind(anOldPartOccurrenceId, aNewOccRef.ChildOccurrenceId); if (anOldPartOccurrenceRefId.IsValid()) { anOccurrenceRefMap.Bind(anOldPartOccurrenceRefId, aNewOccRefId); @@ -1028,9 +1054,7 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const { continue; } - const BRepGraphInc::ProductDef& anOldParentProduct = - theGraph.Topo().Products().Definition(anOldParentProductId); - if (anOldParentProduct.IsRemoved) + if (theGraph.incStorage().IsRemoved(anOldParentProductId)) { continue; } @@ -1041,24 +1065,26 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const continue; } - for (const BRepGraph_OccurrenceRefId& anOldOccRefId : anOldParentProduct.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& anOldOccRefId : + theGraph.Topo().Products().Relations(anOldParentProductId).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& anOldOccRef = theGraph.Refs().Occurrences().Entry(anOldOccRefId); - if (anOldOccRef.IsRemoved) + if (theGraph.incStorage().IsRemoved(anOldOccRefId)) { continue; } const BRepGraphInc::OccurrenceDef& anOldOccDef = - theGraph.Topo().Occurrences().Definition(anOldOccRef.OccurrenceDefId); - if (anOldOccDef.IsRemoved || !anOldOccDef.ChildDefId.IsValid() - || anOldOccDef.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Product) + theGraph.Topo().Occurrences().Definition(anOldOccRef.ChildOccurrenceId); + if (theGraph.incStorage().IsRemoved(anOldOccRef.ChildOccurrenceId) + || !anOldOccDef.ChildNodeId.IsValid() + || anOldOccDef.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Product) { continue; } - const BRepGraph_ProductId anOldChildProductId(anOldOccDef.ChildDefId); + const BRepGraph_ProductId anOldChildProductId(anOldOccDef.ChildNodeId); const BRepGraph_ProductId* aNewChildId = aProductMap.Seek(anOldChildProductId); if (aNewChildId == nullptr) { @@ -1066,19 +1092,23 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const } const BRepGraph_OccurrenceId aNewOccId = - aNewGraph.Editor().Products().LinkProducts(*aNewParentId, - *aNewChildId, - anOldOccRef.LocalLocation); + aNewGraph.Editor().Products().Append(*aNewParentId, + *aNewChildId, + anOldOccRef.LocalLocation); if (aNewOccId.IsValid()) { - anOccurrenceMap.Bind(anOldOccRef.OccurrenceDefId, aNewOccId); - // Find the new OccurrenceRef in the parent product for RefUID transfer. - const BRepGraphInc::ProductDef& aNewParentDef = - aNewGraph.Topo().Products().Definition(*aNewParentId); - for (const BRepGraph_OccurrenceRefId& aNewOccRefId : aNewParentDef.OccurrenceRefIds) + if (!anOccurrenceMap.IsBound(anOldOccRef.ChildOccurrenceId)) { - if (aNewGraph.Refs().Occurrences().Entry(aNewOccRefId).OccurrenceDefId == aNewOccId) + anOccurrenceMap.Bind(anOldOccRef.ChildOccurrenceId, aNewOccId); + } + // Bind the copied OccurrenceRef entry for RefUID transfer. + // Occurrence refs are usage records, so the ref identity must move with + // the exact parent-owned slot instead of only with the occurrence def. + for (const BRepGraph_OccurrenceRefId& aNewOccRefId : + aNewGraph.Topo().Products().Relations(*aNewParentId).OccurrenceRefIds) + { + if (aNewGraph.Refs().Occurrences().Entry(aNewOccRefId).ChildOccurrenceId == aNewOccId) { anOccurrenceRefMap.Bind(anOldOccRefId, aNewOccRefId); break; @@ -1088,157 +1118,149 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const } } - // Force full mesh/presentation recomputation after compact rebuild. - // Compaction rewrites ids and references; preserving old mesh reps can leave - // stale polygon/triangulation bindings and corrupted appearance/bounds. - aNewGraph.meshCache().Clear(); - aNewGraph.refTransientCache().Clear(); + aResult.NbNodesAfter = aNewGraph.Topo().Gen().NbNodes(); - for (BRepGraph_Iterator anIt(aNewGraph); anIt.More(); anIt.Next()) - { - BRepGraph_MutGuard aFace = - aNewGraph.Editor().Faces().Mut(anIt.CurrentId()); - aNewGraph.Editor().Faces().SetTriangulationRep(aFace, BRepGraph_TriangulationRepId()); - } - - for (BRepGraph_Iterator anIt(aNewGraph); anIt.More(); anIt.Next()) - { - BRepGraph_MutGuard anEdge = - aNewGraph.Editor().Edges().Mut(anIt.CurrentId()); - aNewGraph.Editor().Edges().SetPolygon3DRepId(anEdge, BRepGraph_Polygon3DRepId()); - } - - for (BRepGraph_Iterator anIt(aNewGraph); anIt.More(); anIt.Next()) - { - BRepGraph_MutGuard aCoEdge = - aNewGraph.Editor().CoEdges().Mut(anIt.CurrentId()); - aNewGraph.Editor().CoEdges().SetPolygon2DRepId(aCoEdge, BRepGraph_Polygon2DRepId()); - aNewGraph.Editor().CoEdges().SetPolygonOnTriRepId(aCoEdge, BRepGraph_PolygonOnTriRepId()); - } - - aResult.NbNodesAfter = static_cast(aNewGraph.Topo().Gen().NbNodes()); - - // Transfer per-kind UID vectors from old graph to new graph using index remap maps. - // Each new graph's UID vector[newIdx] = old graph's UID vector[oldIdx]. - auto transferUIDs = [&](const auto& theMap, - const NCollection_DynamicArray& theOldVec, - NCollection_DynamicArray& theNewVec) { - // New vector was already populated by EditorView during reconstruction. - // Overwrite entries that have a mapping from the old graph. - for (const auto& [anOldId, aNewId] : theMap.Items()) - { - if (anOldId.IsValidIn(theOldVec) - && theOldVec.Value(static_cast(anOldId.Index)).IsValid()) + // UIDs are stored inline in entity/ref structs. During compaction, entity + // structs are rebuilt from scratch via Add() calls which allocate fresh UIDs. + // Recompute per-kind node counters as max(UID)+1 per surviving kind. + BRepGraphInc_Storage& aNewStorage = aNewGraphData->myIncStorage; + auto recomputeNodeCounter = + [&](const BRepGraph_NodeId::Kind theKind, const uint32_t theNbEntities, auto&& theAccessor) { + uint32_t aMaxCounter = 0; + for (uint32_t anIdx = 0; anIdx < theNbEntities; ++anIdx) { - theNewVec.ChangeValue(static_cast(aNewId.Index)) = - theOldVec.Value(static_cast(anOldId.Index)); + const uint32_t aUID = theAccessor(anIdx).UID; + if (aUID > aMaxCounter) + { + aMaxCounter = aUID; + } } - } - }; + aNewStorage.SetNextNodeUIDCounter(theKind, aMaxCounter + 1); + }; - // Same logic for per-kind RefUID vectors. - auto transferRefUIDs = [&](const auto& theMap, - const NCollection_DynamicArray& theOldVec, - NCollection_DynamicArray& theNewVec) { - for (const auto& [anOldId, aNewId] : theMap.Items()) - { - if (anOldId.IsValidIn(theOldVec) - && theOldVec.Value(static_cast(anOldId.Index)).IsValid()) + recomputeNodeCounter(BRepGraph_NodeId::Kind::Vertex, + aNewStorage.NbVertices(), + [&](uint32_t i) -> const BRepGraphInc::VertexDef& { + return aNewStorage.Vertex(BRepGraph_VertexId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::Edge, + aNewStorage.NbEdges(), + [&](uint32_t i) -> const BRepGraphInc::EdgeDef& { + return aNewStorage.Edge(BRepGraph_EdgeId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::CoEdge, + aNewStorage.NbCoEdges(), + [&](uint32_t i) -> const BRepGraphInc::CoEdgeDef& { + return aNewStorage.CoEdge(BRepGraph_CoEdgeId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::Wire, + aNewStorage.NbWires(), + [&](uint32_t i) -> const BRepGraphInc::WireDef& { + return aNewStorage.Wire(BRepGraph_WireId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::Face, + aNewStorage.NbFaces(), + [&](uint32_t i) -> const BRepGraphInc::FaceDef& { + return aNewStorage.Face(BRepGraph_FaceId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::Shell, + aNewStorage.NbShells(), + [&](uint32_t i) -> const BRepGraphInc::ShellDef& { + return aNewStorage.Shell(BRepGraph_ShellId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::Solid, + aNewStorage.NbSolids(), + [&](uint32_t i) -> const BRepGraphInc::SolidDef& { + return aNewStorage.Solid(BRepGraph_SolidId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::Compound, + aNewStorage.NbCompounds(), + [&](uint32_t i) -> const BRepGraphInc::CompoundDef& { + return aNewStorage.Compound(BRepGraph_CompoundId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::CompSolid, + aNewStorage.NbCompSolids(), + [&](uint32_t i) -> const BRepGraphInc::CompSolidDef& { + return aNewStorage.CompSolid(BRepGraph_CompSolidId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::Product, + aNewStorage.NbProducts(), + [&](uint32_t i) -> const BRepGraphInc::ProductDef& { + return aNewStorage.Product(BRepGraph_ProductId(i)); + }); + recomputeNodeCounter(BRepGraph_NodeId::Kind::Occurrence, + aNewStorage.NbOccurrences(), + [&](uint32_t i) -> const BRepGraphInc::OccurrenceDef& { + return aNewStorage.Occurrence(BRepGraph_OccurrenceId(i)); + }); + + // Recompute per-kind ref counters as max(UID)+1 per surviving ref kind. + auto recomputeRefCounter = + [&](const BRepGraph_RefId::Kind theKind, const uint32_t theNbRefs, auto&& theAccessor) { + uint32_t aMaxCounter = 0; + for (uint32_t anIdx = 0; anIdx < theNbRefs; ++anIdx) { - theNewVec.ChangeValue(static_cast(aNewId.Index)) = - theOldVec.Value(static_cast(anOldId.Index)); + const uint32_t aUID = theAccessor(anIdx).UID; + if (aUID > aMaxCounter) + { + aMaxCounter = aUID; + } } - } - }; + aNewStorage.SetNextRefUIDCounter(theKind, aMaxCounter + 1); + }; - transferUIDs(aVertexMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Vertex), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Vertex)); - transferUIDs(anEdgeMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Edge), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Edge)); - transferUIDs(aWireMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Wire), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Wire)); - transferUIDs(aFaceMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Face), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Face)); - transferUIDs(aShellMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Shell), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Shell)); - transferUIDs(aSolidMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Solid), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Solid)); - transferUIDs(aCompoundMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Compound), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Compound)); - transferUIDs(aCompSolidMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::CompSolid), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::CompSolid)); - transferUIDs(aProductMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Product), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Product)); - transferUIDs(anOccurrenceMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::Occurrence), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::Occurrence)); - transferUIDs(aCoEdgeMap, - aGraphData->myIncStorage.UIDs(BRepGraph_NodeId::Kind::CoEdge), - aNewGraphData->myIncStorage.ChangeUIDs(BRepGraph_NodeId::Kind::CoEdge)); + recomputeRefCounter(BRepGraph_RefId::Kind::Shell, + aNewStorage.NbShellRefs(), + [&](uint32_t i) -> const BRepGraphInc::ShellRef& { + return aNewStorage.ShellRef(BRepGraph_ShellRefId(i)); + }); + recomputeRefCounter(BRepGraph_RefId::Kind::Face, + aNewStorage.NbFaceRefs(), + [&](uint32_t i) -> const BRepGraphInc::FaceRef& { + return aNewStorage.FaceRef(BRepGraph_FaceRefId(i)); + }); + recomputeRefCounter(BRepGraph_RefId::Kind::Wire, + aNewStorage.NbWireRefs(), + [&](uint32_t i) -> const BRepGraphInc::WireRef& { + return aNewStorage.WireRef(BRepGraph_WireRefId(i)); + }); + recomputeRefCounter(BRepGraph_RefId::Kind::Vertex, + aNewStorage.NbVertexRefs(), + [&](uint32_t i) -> const BRepGraphInc::VertexRef& { + return aNewStorage.VertexRef(BRepGraph_VertexRefId(i)); + }); + recomputeRefCounter(BRepGraph_RefId::Kind::Solid, + aNewStorage.NbSolidRefs(), + [&](uint32_t i) -> const BRepGraphInc::SolidRef& { + return aNewStorage.SolidRef(BRepGraph_SolidRefId(i)); + }); + recomputeRefCounter(BRepGraph_RefId::Kind::Child, + aNewStorage.NbChildRefs(), + [&](uint32_t i) -> const BRepGraphInc::ChildRef& { + return aNewStorage.ChildRef(BRepGraph_ChildRefId(i)); + }); + recomputeRefCounter(BRepGraph_RefId::Kind::Occurrence, + aNewStorage.NbOccurrenceRefs(), + [&](uint32_t i) -> const BRepGraphInc::OccurrenceRef& { + return aNewStorage.OccurrenceRef(BRepGraph_OccurrenceRefId(i)); + }); - // Transfer per-kind RefUID vectors. Builder allocates fresh RefUIDs during rebuild; - // overwrite them with the original UIDs from the old graph so that all RefIdFrom(refUID) - // lookups remain valid after compaction. - transferRefUIDs(aVertexRefMap, - aGraphData->myIncStorage.RefUIDs(BRepGraph_RefId::Kind::Vertex), - aNewGraphData->myIncStorage.ChangeRefUIDs(BRepGraph_RefId::Kind::Vertex)); - transferRefUIDs(aCoEdgeRefMap, - aGraphData->myIncStorage.RefUIDs(BRepGraph_RefId::Kind::CoEdge), - aNewGraphData->myIncStorage.ChangeRefUIDs(BRepGraph_RefId::Kind::CoEdge)); - transferRefUIDs(aWireRefMap, - aGraphData->myIncStorage.RefUIDs(BRepGraph_RefId::Kind::Wire), - aNewGraphData->myIncStorage.ChangeRefUIDs(BRepGraph_RefId::Kind::Wire)); - transferRefUIDs(aFaceRefMap, - aGraphData->myIncStorage.RefUIDs(BRepGraph_RefId::Kind::Face), - aNewGraphData->myIncStorage.ChangeRefUIDs(BRepGraph_RefId::Kind::Face)); - transferRefUIDs(aShellRefMap, - aGraphData->myIncStorage.RefUIDs(BRepGraph_RefId::Kind::Shell), - aNewGraphData->myIncStorage.ChangeRefUIDs(BRepGraph_RefId::Kind::Shell)); - transferRefUIDs(aSolidRefMap, - aGraphData->myIncStorage.RefUIDs(BRepGraph_RefId::Kind::Solid), - aNewGraphData->myIncStorage.ChangeRefUIDs(BRepGraph_RefId::Kind::Solid)); - transferRefUIDs(aChildRefMap, - aGraphData->myIncStorage.RefUIDs(BRepGraph_RefId::Kind::Child), - aNewGraphData->myIncStorage.ChangeRefUIDs(BRepGraph_RefId::Kind::Child)); - transferRefUIDs(anOccurrenceRefMap, - aGraphData->myIncStorage.RefUIDs(BRepGraph_RefId::Kind::Occurrence), - aNewGraphData->myIncStorage.ChangeRefUIDs(BRepGraph_RefId::Kind::Occurrence)); + // Preserve graph generation so inline UIDs remain valid after compaction. + aNewStorage.SetGeneration(aGraphData->myIncStorage.Generation()); + aNewStorage.SetGraphGUID(aGraphData->myIncStorage.GraphGUID()); - // Mark UID reverse index dirty so it is rebuilt from the transferred UID vectors - // on next NodeIdFrom()/Has() call. The reverse index was populated during - // Add/Add/etc. with intermediate UIDs that are now overwritten. - { - std::unique_lock aUIDLock(aNewGraphData->myUIDToNodeIdMutex); - aNewGraphData->myUIDToNodeIdDirty = true; - } - { - std::unique_lock aRefUIDLock(aNewGraphData->myRefUIDToRefIdMutex); - aNewGraphData->myRefUIDToRefIdDirty = true; - } + // Rebuild UID reverse indexes from inline UIDs (skipping removed entities). + aNewStorage.RebuildUIDReverseIndexes(); - aNewGraphData->myNextUIDCounter.store( - aGraphData->myNextUIDCounter.load(std::memory_order_relaxed), - std::memory_order_relaxed); - // Preserve graph generation so transferred UIDs remain valid after compaction. - aNewGraphData->myGeneration.store(aGraphData->myGeneration.load(std::memory_order_relaxed), - std::memory_order_relaxed); - aNewGraphData->myIsDone = true; + static const TCollection_AsciiString THE_COMPACT_REMAP_LABEL("Compact:Remap"); // Save layers before swap (default move would transfer empty layers from aNewGraph). BRepGraph_LayerRegistry aSavedLayerRegistry = std::move(theGraph.layerRegistry()); + BRepGraph_CacheRegistry aSavedCacheRegistry = std::move(theGraph.cacheRegistry()); // Transfer TShape-to-NodeId and NodeId-to-OriginalShape bindings: the rebuilt graph has none. - NCollection_DynamicArray> aTShapeBindings; - NCollection_DynamicArray> aOriginalBindings; + NCollection_LinearVector> aTShapeBindings; + NCollection_LinearVector> aOriginalBindings; aGraphData->myIncStorage.ForEachTShapeBinding( [&](const TopoDS_TShape* theTShape, const BRepGraph_NodeId& theNodeId) { aTShapeBindings.Append({theTShape, theNodeId}); @@ -1248,39 +1270,112 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aOriginalBindings.Append({theNodeId, theShape}); }); + // Build full ItemId->ItemId remap covering nodes, refs, and exact geometry reps (not mesh). + NCollection_FlatDataMap anItemRemap; + for (const auto& [anOldId, aNewId] : aVertexMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : anEdgeMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aCoEdgeMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aWireMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aFaceMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aShellMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aSolidMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aCompoundMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aCompSolidMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aProductMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : anOccurrenceMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aVertexRefMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aWireRefMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aFaceRefMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aShellRefMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aChildRefMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : aSolidRefMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + for (const auto& [anOldId, aNewId] : anOccurrenceRefMap.Items()) + { + anItemRemap.Bind(BRepGraph_ItemId(anOldId), BRepGraph_ItemId(aNewId)); + } + // RepId records are session-local and not part of ItemId remap. + + if (theOptions.CacheMode == BRepGraph_Compact::Options::CachePolicy::CopyFresh) + { + aSavedCacheRegistry.CopyFreshCachesTo(aNewGraph, + anItemRemap, + BRepGraph_CopyRemap::Mode::Compact); + aSavedCacheRegistry.Clear(); + } + // Swap. theGraph = std::move(aNewGraph); // Restore layers and notify about index remapping. theGraph.layerRegistry() = std::move(aSavedLayerRegistry); - // Direct transientCache() access is intentional here. - // Compact must clear stale slots keyed by old indices, then re-reserve the - // cache for the new compacted entity counts before later algorithm use. - theGraph.transientCache().Clear(); + if (theOptions.CacheMode == BRepGraph_Compact::Options::CachePolicy::Drop) { - BRepGraphInc_Storage& aStr = theGraph.incStorage(); - int aCounts[BRepGraph_TransientCache::THE_KIND_COUNT] = {}; - aCounts[static_cast(BRepGraph_NodeId::Kind::Vertex)] = aStr.NbVertices(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Edge)] = aStr.NbEdges(); - aCounts[static_cast(BRepGraph_NodeId::Kind::CoEdge)] = aStr.NbCoEdges(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Wire)] = aStr.NbWires(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Face)] = aStr.NbFaces(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Shell)] = aStr.NbShells(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Solid)] = aStr.NbSolids(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Compound)] = aStr.NbCompounds(); - aCounts[static_cast(BRepGraph_NodeId::Kind::CompSolid)] = aStr.NbCompSolids(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Product)] = aStr.NbProducts(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Occurrence)] = aStr.NbOccurrences(); - int aReservedKindCount = BRepGraph_TransientCache::THE_DEFAULT_RESERVED_KIND_COUNT; - const int aRegisteredKindCount = BRepGraph_CacheKindRegistry::NbRegistered(); - if (aRegisteredKindCount > aReservedKindCount) - { - aReservedKindCount = aRegisteredKindCount; - } - theGraph.transientCache().Reserve(aReservedKindCount, aCounts); + theGraph.cacheRegistry() = std::move(aSavedCacheRegistry); } - // Build unified remap map covering all 8 topology kinds. - NCollection_DataMap aRemapMap; + theGraph.initViewsAndRegistries(); + if (theOptions.CacheMode == BRepGraph_Compact::Options::CachePolicy::Drop) + { + theGraph.cacheRegistry().ClearAll(); + } + + // Migrate all layer data through CopyTo with full item remap. + theGraph.LayerRegistry().CopyLayersTo(theGraph, anItemRemap, BRepGraph_CopyRemap::Mode::Compact); + + // Build unified NodeId remap for TShape bindings and history recording. + NCollection_DataMap aRemapMap( + static_cast(theGraph.Topo().Gen().NbNodes()), + aPermAlloc); for (const auto& [anOldId, aNewId] : aVertexMap.Items()) { aRemapMap.Bind(anOldId, aNewId); @@ -1326,8 +1421,6 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const aRemapMap.Bind(anOldId, aNewId); } - theGraph.LayerRegistry().DispatchOnCompact(aRemapMap); - // Restore TShape-to-NodeId and NodeId-to-OriginalShape bindings, remapping NodeIds through // aRemapMap. Nodes that were removed (dead, no entry in aRemapMap) are simply dropped. for (const auto& [aTShape, aOldId] : aTShapeBindings) @@ -1347,22 +1440,41 @@ BRepGraph_Compact::Result BRepGraph_Compact::Perform(BRepGraph& theGraph, const } } - // Record history after swap so records survive in the new graph's history log. - // aRemapMap covers all topology kinds (Vertex..Occurrence + CoEdge). if (theOptions.HistoryMode) { - theGraph.History().SetEnabled(true); - for (NCollection_DataMap::Iterator anIt(aRemapMap); - anIt.More(); - anIt.Next()) + // Re-acquire history layer: CopyLayersTo(Compact) replaced the old instance. + BRepGraph_LayerHistory& aNewHistory = + *theGraph.LayerRegistry().Ensure(); + aNewHistory.SetEnabled(true); + aWireOldCoEdges.Clear(true); + aFaceNextWires.Clear(true); + aFaceOldWireRefs.Clear(true); + aCompChildren.Clear(true); + aCompOldChildRefs.Clear(true); + aCSSolids.Clear(true); + aCSOldSolidRefs.Clear(true); + + NCollection_LinearVector aNewOriginals(64); + NCollection_LinearVector aNewReplacements(64); + for (NCollection_DataMap::Iterator aMapIt(aRemapMap); + aMapIt.More(); + aMapIt.Next()) { - NCollection_DynamicArray aRepl; - aRepl.Append(anIt.Value()); - theGraph.History().Record(TCollection_AsciiString("Compact:Remap"), anIt.Key(), aRepl); + aNewOriginals.Append(aMapIt.Key()); + aNewReplacements.Append(aMapIt.Value()); + } + if (!aNewOriginals.IsEmpty()) + { + aNewHistory.RecordBatch(THE_COMPACT_REMAP_LABEL, + aNewOriginals.ToArray1(), + aNewReplacements.ToArray1()); } } theGraph.Editor().CommitMutation(); - theGraph.History().SetEnabled(wasHistoryEnabled); + // Re-acquire history (may be new instance after CopyLayersTo) to restore enabled state. + BRepGraph_LayerHistory& aFinalHistory = + *theGraph.LayerRegistry().Ensure(); + aFinalHistory.SetEnabled(wasHistoryEnabled); return aResult; } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.hxx index f87a1007e9..ba8ff4d053 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Compact.hxx @@ -15,7 +15,6 @@ #define _BRepGraph_Compact_HeaderFile #include - #include //! @brief Graph compaction algorithm that reclaims removed node slots. @@ -35,24 +34,33 @@ public: //! Configuration for compaction. struct Options { - bool HistoryMode = true; //!< Record index remapping in history. + enum class CachePolicy + { + Drop, //!< Keep registered cache services but clear transient entries. + CopyFresh //!< Copy fresh, remappable transient entries into the compacted graph. + }; + + bool HistoryMode = true; //!< Record index remapping in history. + CachePolicy CacheMode = CachePolicy::Drop; //!< Runtime cache migration policy. }; //! Result counters for diagnostics. struct Result { - int NbRemovedVertices = 0; - int NbRemovedEdges = 0; - int NbRemovedWires = 0; - int NbRemovedFaces = 0; - int NbRemovedShells = 0; - int NbRemovedSolids = 0; - int NbRemovedCompounds = 0; - int NbRemovedCompSolids = 0; - int NbRemovedSurfaces = 0; - int NbRemovedCurves = 0; - int NbNodesBefore = 0; - int NbNodesAfter = 0; + uint32_t NbRemovedVertices = 0; + uint32_t NbRemovedEdges = 0; + uint32_t NbRemovedWires = 0; + uint32_t NbRemovedFaces = 0; + uint32_t NbRemovedShells = 0; + uint32_t NbRemovedSolids = 0; + uint32_t NbRemovedCompounds = 0; + uint32_t NbRemovedCompSolids = 0; + uint32_t NbRemovedSurfaces = 0; + uint32_t NbRemovedCurves = 0; + uint32_t NbNodesBefore = 0; + uint32_t NbNodesAfter = 0; + uint32_t NbUnmappedActiveDefs = + 0; //!< Active defs not present in any remap (orphans + drop-outs). }; //! Run compaction with default options. @@ -67,7 +75,6 @@ public: [[nodiscard]] Standard_EXPORT static Result Perform(BRepGraph& theGraph, const Options& theOptions); -private: BRepGraph_Compact() = delete; }; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx index 30be605ef8..ef6080618a 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.cxx @@ -12,155 +12,141 @@ // 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 +using GeomPolicy = BRepGraph_Copy::GeomPolicy; +using MeshPolicy = BRepGraph_Copy::MeshPolicy; + namespace { -//! Copy geometry handle if theCopyGeom is true, otherwise return same handle. -occ::handle copySurface(const occ::handle& theSurf, bool theCopyGeom) +//! Copy geometry handle according to the policy. +//! Copy: deep-clone via Geom_Geometry::Copy(). +//! Share: return the same handle. +//! Drop: return null handle. +occ::handle copySurface(const occ::handle& theSurf, + GeomPolicy thePolicy) { - if (theSurf.IsNull() || !theCopyGeom) + if (theSurf.IsNull()) { return theSurf; } - return occ::down_cast(theSurf->Copy()); + switch (thePolicy) + { + case GeomPolicy::Copy: + return occ::down_cast(theSurf->Copy()); + case GeomPolicy::Share: + return theSurf; + case GeomPolicy::Drop: + return occ::handle(); + } + return theSurf; // unreachable } -occ::handle copyCurve(const occ::handle& theCrv, bool theCopyGeom) +occ::handle copyCurve(const occ::handle& theCrv, GeomPolicy thePolicy) { - if (theCrv.IsNull() || !theCopyGeom) + if (theCrv.IsNull()) { return theCrv; } - return occ::down_cast(theCrv->Copy()); + switch (thePolicy) + { + case GeomPolicy::Copy: + return occ::down_cast(theCrv->Copy()); + case GeomPolicy::Share: + return theCrv; + case GeomPolicy::Drop: + return occ::handle(); + } + return theCrv; // unreachable } -occ::handle copyPCurve(const occ::handle& theCrv, bool theCopyGeom) +occ::handle copyPCurve(const occ::handle& theCrv, GeomPolicy thePolicy) { - if (theCrv.IsNull() || !theCopyGeom) + if (theCrv.IsNull()) { return theCrv; } - return occ::down_cast(theCrv->Copy()); + switch (thePolicy) + { + case GeomPolicy::Copy: + return occ::down_cast(theCrv->Copy()); + case GeomPolicy::Share: + return theCrv; + case GeomPolicy::Drop: + return occ::handle(); + } + return theCrv; // unreachable } -template -void transferFreshCacheValues(const BRepGraph& theSrcGraph, - const TKeyId theSrcKey, - BRepGraph& theDstGraph, - const TKeyId theDstKey) +template +HandleT copyMeshHandle(const HandleT& theHandle, MeshPolicy thePolicy) { - for (auto aKindIt = theSrcGraph.Cache().CacheKindIter(theSrcKey); aKindIt.More(); aKindIt.Next()) + if (theHandle.IsNull()) { - const occ::handle aKind = aKindIt.Value(); - const occ::handle aValue = theSrcGraph.Cache().Get(theSrcKey, aKind); - if (!aValue.IsNull()) - { - theDstGraph.Cache().Set(theDstKey, aKind, aValue); - } + return theHandle; } + switch (thePolicy) + { + case MeshPolicy::Copy: + return theHandle->Copy(); + case MeshPolicy::Share: + return theHandle; + case MeshPolicy::Drop: + return HandleT(); + } + return theHandle; // unreachable } -//! Deferred cache-transfer queue. -//! -//! Pairs are recorded only for entities that actually carry at least one fresh cache -//! value on the source graph; entities with no cache are skipped, so the queue size is -//! O(cached entities), not O(graph size). -//! -//! We defer instead of transferring eagerly because the destination graph's SubtreeGen -//! continues to advance during construction (every Mut/AddPCurve propagates via the -//! reverse index). A Set() during construction captures an intermediate SubtreeGen -//! that Get() later mismatches. Draining after all mutations means Set() captures the -//! final SubtreeGen and the cache survives the copy. -struct DeferredCacheTransfers -{ - using NodePair = std::pair; - using RefPair = std::pair; - - NCollection_DynamicArray NodePairs; - NCollection_DynamicArray RefPairs; - - template - static bool srcHasAnyCache(const BRepGraph& theSrc, const TKeyId theKey) - { - return theSrc.Cache().CacheKindIter(theKey).More(); - } - - void DeferNode(const BRepGraph& theSrc, - const BRepGraph_NodeId theSrcNode, - const BRepGraph_NodeId theDstNode) - { - if (srcHasAnyCache(theSrc, theSrcNode)) - { - NodePairs.Append({theSrcNode, theDstNode}); - } - } - - void DeferRef(const BRepGraph& theSrc, - const BRepGraph_RefId theSrcRef, - const BRepGraph_RefId theDstRef) - { - if (srcHasAnyCache(theSrc, theSrcRef)) - { - RefPairs.Append({theSrcRef, theDstRef}); - } - } - - void Drain(const BRepGraph& theSrc, BRepGraph& theDst) const - { - for (const auto& aPair : NodePairs) - { - transferFreshCacheValues(theSrc, aPair.first, theDst, aPair.second); - } - for (const auto& aPair : RefPairs) - { - transferFreshCacheValues(theSrc, aPair.first, theDst, aPair.second); - } - } -}; +//================================================================================================= //! Memoised copy context shared across all ensure* free functions. //! -//! Source/result pointers that require friend access (incStorage, meshCache, data) +//! Source/result pointers that require friend access (incStorage, cacheRegistry, data) //! are pre-extracted inside the friend methods Perform / CopyNode before any //! ensure* function is called. struct GraphCopyContext { const BRepGraph& Source; - BRepGraph Result; - bool CopyGeom; - bool CopyMesh; - bool ReserveCache; - DeferredCacheTransfers Deferred; + BRepGraph& Result; + bool IsSelfCopy; + GeomPolicy GeomPol; + MeshPolicy MeshPol; const BRepGraphInc_Storage* SrcStorage = nullptr; BRepGraphInc_Storage* DstStorage = nullptr; - BRepGraph_MeshCacheStorage* DstMesh = nullptr; BRepGraph_Data* DstData = nullptr; NCollection_DataMap Vertices; NCollection_DataMap Edges; + NCollection_DataMap CoEdges; NCollection_DataMap Wires; NCollection_DataMap Faces; NCollection_DataMap Shells; @@ -170,80 +156,411 @@ struct GraphCopyContext NCollection_DataMap Products; NCollection_DataMap Occurrences; NCollection_DataMap OccurrenceRefs; + NCollection_FlatDataMap ItemRemap; explicit GraphCopyContext(const BRepGraph& theSrc, - bool theCopyGeom, - bool theCopyMesh, - bool theReserveCache) + BRepGraph& theDst, + bool theIsSelfCopy, + GeomPolicy theGeomPolicy, + MeshPolicy theMeshPolicy) : Source(theSrc), - CopyGeom(theCopyGeom), - CopyMesh(theCopyMesh), - ReserveCache(theReserveCache) + Result(theDst), + IsSelfCopy(theIsSelfCopy), + GeomPol(theGeomPolicy), + MeshPol(theMeshPolicy) { } }; // Forward declarations - needed for mutual recursion between ensure* functions. -BRepGraph_VertexId ensureVertex(GraphCopyContext& ctx, BRepGraph_VertexId srcId); -BRepGraph_EdgeId ensureEdge(GraphCopyContext& ctx, BRepGraph_EdgeId srcId); -BRepGraph_WireId ensureWire(GraphCopyContext& ctx, BRepGraph_WireId srcId); -BRepGraph_FaceId ensureFace(GraphCopyContext& ctx, BRepGraph_FaceId srcId); -BRepGraph_ShellId ensureShell(GraphCopyContext& ctx, BRepGraph_ShellId srcId); -BRepGraph_SolidId ensureSolid(GraphCopyContext& ctx, BRepGraph_SolidId srcId); -BRepGraph_CompoundId ensureCompound(GraphCopyContext& ctx, BRepGraph_CompoundId srcId); -BRepGraph_CompSolidId ensureCompSolid(GraphCopyContext& ctx, BRepGraph_CompSolidId srcId); -BRepGraph_ProductId ensureProduct(GraphCopyContext& ctx, BRepGraph_ProductId srcId); -BRepGraph_OccurrenceId ensureOccurrence(GraphCopyContext& ctx, BRepGraph_OccurrenceId srcId); -BRepGraph_OccurrenceRefId ensureOccurrenceRef(GraphCopyContext& ctx, - BRepGraph_OccurrenceRefId srcRefId); -void ensureNode(GraphCopyContext& ctx, BRepGraph_NodeId srcNodeId); -BRepGraph_NodeId mappedNode(const GraphCopyContext& ctx, BRepGraph_NodeId srcId); +BRepGraph_VertexId ensureVertex(GraphCopyContext& theCtx, BRepGraph_VertexId theSrcId); +BRepGraph_EdgeId ensureEdge(GraphCopyContext& theCtx, BRepGraph_EdgeId theSrcId); +BRepGraph_WireId ensureWire(GraphCopyContext& theCtx, BRepGraph_WireId theSrcId); +BRepGraph_FaceId ensureFace(GraphCopyContext& theCtx, BRepGraph_FaceId theSrcId); +BRepGraph_ShellId ensureShell(GraphCopyContext& theCtx, BRepGraph_ShellId theSrcId); +BRepGraph_SolidId ensureSolid(GraphCopyContext& theCtx, BRepGraph_SolidId theSrcId); +BRepGraph_CompoundId ensureCompound(GraphCopyContext& theCtx, BRepGraph_CompoundId theSrcId); +BRepGraph_CompSolidId ensureCompSolid(GraphCopyContext& theCtx, BRepGraph_CompSolidId theSrcId); +BRepGraph_ProductId ensureProduct(GraphCopyContext& theCtx, BRepGraph_ProductId theSrcId); +BRepGraph_OccurrenceId ensureOccurrence(GraphCopyContext& theCtx, BRepGraph_OccurrenceId theSrcId); +BRepGraph_OccurrenceRefId ensureOccurrenceRef(GraphCopyContext& theCtx, + BRepGraph_OccurrenceRefId theSrcRefId, + BRepGraph_ProductId theDstParentProductId); +void ensureNode(GraphCopyContext& theCtx, BRepGraph_NodeId theSrcNodeId); +BRepGraph_NodeId mappedNode(const GraphCopyContext& theCtx, BRepGraph_NodeId theSrcId); //================================================================================================= -BRepGraph_NodeId mappedNode(const GraphCopyContext& ctx, BRepGraph_NodeId srcId) +template +void bindItemRemap(NCollection_FlatDataMap& theItemRemap, + const SourceIdT theSource, + const TargetIdT theTarget) +{ + if (theSource.IsValid() && theTarget.IsValid()) + { + const BRepGraph_ItemId aSourceItem(theSource); + const BRepGraph_ItemId aTargetItem(theTarget); + if (theItemRemap.IsBound(aSourceItem)) + { + theItemRemap.ChangeFind(aSourceItem) = aTargetItem; + } + else + { + theItemRemap.Bind(aSourceItem, aTargetItem); + } + } +} + +//================================================================================================= + +template +void bindItemRemap(GraphCopyContext& theCtx, const SourceIdT theSource, const TargetIdT theTarget) +{ + bindItemRemap(theCtx.ItemRemap, theSource, theTarget); +} + +//================================================================================================= + +template +void setRemovedLike(const BRepGraphInc_Storage& theSourceStorage, + BRepGraphInc_Storage& theTargetStorage, + const SourceIdT theSource, + const TargetIdT theTarget) +{ + if (theSource.IsValid() && theTarget.IsValid()) + { + theTargetStorage.SetRemoved(theTarget, theSourceStorage.IsRemoved(theSource)); + } +} + +//================================================================================================= + +template +void bindTypedMap(NCollection_DataMap& theTypedMap, + GraphCopyContext& theCtx, + const SourceIdT theSource, + const TargetIdT theTarget) +{ + theTypedMap.Bind(theSource, theTarget); + bindItemRemap(theCtx, theSource, theTarget); +} + +//================================================================================================= + +template +BRepGraph_NodeId::Typed mappedItem( + const NCollection_FlatDataMap& theItemRemap, + const BRepGraph_NodeId::Typed theSource) +{ + if (!theSource.IsValid()) + { + return BRepGraph_NodeId::Typed(); + } + const BRepGraph_ItemId* aTarget = theItemRemap.Seek(BRepGraph_ItemId(theSource)); + if (aTarget == nullptr || !aTarget->IsNode()) + { + return BRepGraph_NodeId::Typed(); + } + return BRepGraph_NodeId::Typed::FromNodeId(aTarget->NodeId()); +} + +template +BRepGraph_RefId::Typed mappedItem( + const NCollection_FlatDataMap& theItemRemap, + const BRepGraph_RefId::Typed theSource) +{ + if (!theSource.IsValid()) + { + return BRepGraph_RefId::Typed(); + } + const BRepGraph_ItemId* aTarget = theItemRemap.Seek(BRepGraph_ItemId(theSource)); + if (aTarget == nullptr || !aTarget->IsReference()) + { + return BRepGraph_RefId::Typed(); + } + return BRepGraph_RefId::Typed::FromRefId(aTarget->RefId()); +} + +//================================================================================================= + +size_t itemCapacityUpperBound(const BRepGraphInc_Storage& theStorage) +{ + return static_cast(theStorage.NbVertices()) + theStorage.NbEdges() + + theStorage.NbCoEdges() + theStorage.NbWires() + theStorage.NbFaces() + + theStorage.NbShells() + theStorage.NbSolids() + theStorage.NbCompounds() + + theStorage.NbCompSolids() + theStorage.NbProducts() + theStorage.NbOccurrences() + + theStorage.NbShellRefs() + theStorage.NbFaceRefs() + theStorage.NbWireRefs() + + theStorage.NbVertexRefs() + theStorage.NbSolidRefs() + theStorage.NbChildRefs() + + theStorage.NbOccurrenceRefs() + theStorage.NbFaceSurfaces() + theStorage.NbEdgeCurves3D() + + theStorage.NbCoEdgeCurves2D() + theStorage.NbFaceTriangulations() + + theStorage.NbEdgePolygons3D() + theStorage.NbCoEdgePolygons2D() + + theStorage.NbCoEdgePolygonsOnTri(); +} + +//================================================================================================= + +template +void bindIdentityRange(const uint32_t theSourceCount, + const uint32_t theTargetCount, + NCollection_FlatDataMap& theItemRemap) +{ + const uint32_t aCount = std::min(theSourceCount, theTargetCount); + for (uint32_t anIndex = 0; anIndex < aCount; ++anIndex) + { + const IdT anId(anIndex); + if (anId.IsValid()) + { + bindItemRemap(theItemRemap, anId, anId); + } + } +} + +//================================================================================================= + +void bindIdentityItems(const BRepGraphInc_Storage& theSource, + const BRepGraphInc_Storage& theTarget, + NCollection_FlatDataMap& theItemRemap) +{ + theItemRemap.Reserve(itemCapacityUpperBound(theSource)); + bindIdentityRange(theSource.NbVertices(), + theTarget.NbVertices(), + theItemRemap); + bindIdentityRange(theSource.NbEdges(), theTarget.NbEdges(), theItemRemap); + bindIdentityRange(theSource.NbCoEdges(), theTarget.NbCoEdges(), theItemRemap); + bindIdentityRange(theSource.NbWires(), theTarget.NbWires(), theItemRemap); + bindIdentityRange(theSource.NbFaces(), theTarget.NbFaces(), theItemRemap); + bindIdentityRange(theSource.NbShells(), theTarget.NbShells(), theItemRemap); + bindIdentityRange(theSource.NbSolids(), theTarget.NbSolids(), theItemRemap); + bindIdentityRange(theSource.NbCompounds(), + theTarget.NbCompounds(), + theItemRemap); + bindIdentityRange(theSource.NbCompSolids(), + theTarget.NbCompSolids(), + theItemRemap); + bindIdentityRange(theSource.NbProducts(), + theTarget.NbProducts(), + theItemRemap); + bindIdentityRange(theSource.NbOccurrences(), + theTarget.NbOccurrences(), + theItemRemap); + bindIdentityRange(theSource.NbShellRefs(), + theTarget.NbShellRefs(), + theItemRemap); + bindIdentityRange(theSource.NbFaceRefs(), + theTarget.NbFaceRefs(), + theItemRemap); + bindIdentityRange(theSource.NbWireRefs(), + theTarget.NbWireRefs(), + theItemRemap); + bindIdentityRange(theSource.NbVertexRefs(), + theTarget.NbVertexRefs(), + theItemRemap); + bindIdentityRange(theSource.NbSolidRefs(), + theTarget.NbSolidRefs(), + theItemRemap); + bindIdentityRange(theSource.NbChildRefs(), + theTarget.NbChildRefs(), + theItemRemap); + bindIdentityRange(theSource.NbOccurrenceRefs(), + theTarget.NbOccurrenceRefs(), + theItemRemap); +} + +//================================================================================================= + +void copyCurve2DReps(const BRepGraphInc_Storage& theSource, + BRepGraphInc_Storage& theTarget, + GeomPolicy theGeomPolicy) +{ + for (BRepGraph_CoEdgeCurve2DRepId aRepId(0); aRepId.IsValid(theSource.NbCoEdgeCurves2D()); + ++aRepId) + { + const BRepGraph_CoEdgeCurve2DRepId aNewRepId = theTarget.AppendCoEdgeCurve2DRep(); + Standard_ASSERT_RAISE(aNewRepId == aRepId, "BRepGraph_Copy: unexpected curve2d rep id"); + BRepGraphInc::CoEdgeCurve2DRep& aNewUse = theTarget.ChangeCoEdgeCurve2DRep(aNewRepId); + aNewUse = theSource.CoEdgeCurve2DRep(aRepId); + aNewUse.Curve = copyPCurve(aNewUse.Curve, theGeomPolicy); + theTarget.SetRemoved(aNewRepId, theSource.IsRemoved(aRepId)); + } +} + +//================================================================================================= + +void copyPersistentMeshReps(const BRepGraphInc_Storage& theSource, + BRepGraphInc_Storage& theTarget, + MeshPolicy theMeshPolicy) +{ + for (BRepGraph_FaceTriangulationRepId aRepId(0); aRepId.IsValid(theSource.NbFaceTriangulations()); + ++aRepId) + { + const BRepGraph_FaceTriangulationRepId aNewRepId = theTarget.AppendFaceTriangulationRep(); + Standard_ASSERT_RAISE(aNewRepId == aRepId, "BRepGraph_Copy: unexpected triangulation rep id"); + BRepGraphInc::FaceTriangulationRep& aNewRep = theTarget.ChangeFaceTriangulationRep(aNewRepId); + aNewRep = theSource.FaceTriangulationRep(aRepId); + aNewRep.Triangulation = copyMeshHandle(aNewRep.Triangulation, theMeshPolicy); + theTarget.SetRemoved(aNewRepId, theSource.IsRemoved(aRepId)); + } + + for (BRepGraph_EdgePolygon3DRepId aRepId(0); aRepId.IsValid(theSource.NbEdgePolygons3D()); + ++aRepId) + { + const BRepGraph_EdgePolygon3DRepId aNewRepId = theTarget.AppendEdgePolygon3DRep(); + Standard_ASSERT_RAISE(aNewRepId == aRepId, "BRepGraph_Copy: unexpected polygon3d rep id"); + BRepGraphInc::EdgePolygon3DRep& aNewRep = theTarget.ChangeEdgePolygon3DRep(aNewRepId); + aNewRep = theSource.EdgePolygon3DRep(aRepId); + aNewRep.Polygon = copyMeshHandle(aNewRep.Polygon, theMeshPolicy); + theTarget.SetRemoved(aNewRepId, theSource.IsRemoved(aRepId)); + } + + for (BRepGraph_CoEdgePolygon2DRepId aRepId(0); aRepId.IsValid(theSource.NbCoEdgePolygons2D()); + ++aRepId) + { + const BRepGraph_CoEdgePolygon2DRepId aNewRepId = theTarget.AppendCoEdgePolygon2DRep(); + Standard_ASSERT_RAISE(aNewRepId == aRepId, "BRepGraph_Copy: unexpected polygon2d rep id"); + BRepGraphInc::CoEdgePolygon2DRep& aNewRep = theTarget.ChangeCoEdgePolygon2DRep(aNewRepId); + aNewRep = theSource.CoEdgePolygon2DRep(aRepId); + aNewRep.Polygon = copyMeshHandle(aNewRep.Polygon, theMeshPolicy); + theTarget.SetRemoved(aNewRepId, theSource.IsRemoved(aRepId)); + } + + for (BRepGraph_CoEdgePolygonOnTriRepId aRepId(0); + aRepId.IsValid(theSource.NbCoEdgePolygonsOnTri()); + ++aRepId) + { + const BRepGraph_CoEdgePolygonOnTriRepId aNewRepId = theTarget.AppendCoEdgePolygonOnTriRep(); + Standard_ASSERT_RAISE(aNewRepId == aRepId, + "BRepGraph_Copy: unexpected polygon-on-triangulation rep id"); + BRepGraphInc::CoEdgePolygonOnTriRep& aNewRep = theTarget.ChangeCoEdgePolygonOnTriRep(aNewRepId); + aNewRep = theSource.CoEdgePolygonOnTriRep(aRepId); + aNewRep.Polygon = copyMeshHandle(aNewRep.Polygon, theMeshPolicy); + theTarget.SetRemoved(aNewRepId, theSource.IsRemoved(aRepId)); + } +} + +//================================================================================================= + +void ensureTriangulationRep(GraphCopyContext& theCtx, + const BRepGraph_FaceTriangulationRepId theSrcRepId, + const BRepGraph_FaceId theDstFaceId) +{ + if (theCtx.MeshPol == MeshPolicy::Drop + || !theSrcRepId.IsValid(theCtx.SrcStorage->NbFaceTriangulations()) + || theCtx.SrcStorage->IsRemoved(theSrcRepId)) + { + return; + } + + theCtx.Result.Editor().Faces().SetPersistentTriangulation( + theDstFaceId, + copyMeshHandle(theCtx.SrcStorage->FaceTriangulationRep(theSrcRepId).Triangulation, + theCtx.MeshPol)); +} + +//================================================================================================= + +void ensurePolygon3DRep(GraphCopyContext& theCtx, + const BRepGraph_EdgePolygon3DRepId theSrcRepId, + const BRepGraph_EdgeId theDstEdgeId) +{ + if (theCtx.MeshPol == MeshPolicy::Drop + || !theSrcRepId.IsValid(theCtx.SrcStorage->NbEdgePolygons3D()) + || theCtx.SrcStorage->IsRemoved(theSrcRepId)) + { + return; + } + + theCtx.Result.Editor().Edges().SetPersistentPolygon3D( + theDstEdgeId, + copyMeshHandle(theCtx.SrcStorage->EdgePolygon3DRep(theSrcRepId).Polygon, theCtx.MeshPol)); +} + +//================================================================================================= + +void ensurePolygon2DRep(GraphCopyContext& theCtx, + const BRepGraph_CoEdgePolygon2DRepId theSrcRepId, + const BRepGraph_CoEdgeId theDstCoEdgeId) +{ + if (theCtx.MeshPol == MeshPolicy::Drop + || !theSrcRepId.IsValid(theCtx.SrcStorage->NbCoEdgePolygons2D()) + || theCtx.SrcStorage->IsRemoved(theSrcRepId)) + { + return; + } + + theCtx.Result.Editor().CoEdges().SetPersistentPolygon2D( + theDstCoEdgeId, + copyMeshHandle(theCtx.SrcStorage->CoEdgePolygon2DRep(theSrcRepId).Polygon, theCtx.MeshPol)); +} + +//================================================================================================= + +void ensurePolygonOnTriRep(GraphCopyContext& theCtx, + const BRepGraph_CoEdgePolygonOnTriRepId theSrcRepId, + const BRepGraph_CoEdgeId theDstCoEdgeId) +{ + if (theCtx.MeshPol == MeshPolicy::Drop + || !theSrcRepId.IsValid(theCtx.SrcStorage->NbCoEdgePolygonsOnTri()) + || theCtx.SrcStorage->IsRemoved(theSrcRepId)) + { + return; + } + + theCtx.Result.Editor().CoEdges().SetPersistentPolygonOnTri( + theDstCoEdgeId, + copyMeshHandle(theCtx.SrcStorage->CoEdgePolygonOnTriRep(theSrcRepId).Polygon, theCtx.MeshPol)); +} + +//================================================================================================= + +BRepGraph_NodeId mappedNode(const GraphCopyContext& theCtx, BRepGraph_NodeId theSrcId) { using Kind = BRepGraph_NodeId::Kind; - switch (srcId.NodeKind) + switch (theSrcId.NodeKind) { case Kind::Vertex: { - const BRepGraph_VertexId* p = ctx.Vertices.Seek(BRepGraph_VertexId(srcId.Index)); + const BRepGraph_VertexId* p = theCtx.Vertices.Seek(BRepGraph_VertexId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::Edge: { - const BRepGraph_EdgeId* p = ctx.Edges.Seek(BRepGraph_EdgeId(srcId.Index)); + const BRepGraph_EdgeId* p = theCtx.Edges.Seek(BRepGraph_EdgeId(theSrcId.Index)); + return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); + } + case Kind::CoEdge: { + const BRepGraph_CoEdgeId* p = theCtx.CoEdges.Seek(BRepGraph_CoEdgeId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::Wire: { - const BRepGraph_WireId* p = ctx.Wires.Seek(BRepGraph_WireId(srcId.Index)); + const BRepGraph_WireId* p = theCtx.Wires.Seek(BRepGraph_WireId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::Face: { - const BRepGraph_FaceId* p = ctx.Faces.Seek(BRepGraph_FaceId(srcId.Index)); + const BRepGraph_FaceId* p = theCtx.Faces.Seek(BRepGraph_FaceId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::Shell: { - const BRepGraph_ShellId* p = ctx.Shells.Seek(BRepGraph_ShellId(srcId.Index)); + const BRepGraph_ShellId* p = theCtx.Shells.Seek(BRepGraph_ShellId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::Solid: { - const BRepGraph_SolidId* p = ctx.Solids.Seek(BRepGraph_SolidId(srcId.Index)); + const BRepGraph_SolidId* p = theCtx.Solids.Seek(BRepGraph_SolidId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::Compound: { - const BRepGraph_CompoundId* p = ctx.Compounds.Seek(BRepGraph_CompoundId(srcId.Index)); + const BRepGraph_CompoundId* p = theCtx.Compounds.Seek(BRepGraph_CompoundId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::CompSolid: { - const BRepGraph_CompSolidId* p = ctx.CompSolids.Seek(BRepGraph_CompSolidId(srcId.Index)); + const BRepGraph_CompSolidId* p = + theCtx.CompSolids.Seek(BRepGraph_CompSolidId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::Product: { - const BRepGraph_ProductId* p = ctx.Products.Seek(BRepGraph_ProductId(srcId.Index)); + const BRepGraph_ProductId* p = theCtx.Products.Seek(BRepGraph_ProductId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } case Kind::Occurrence: { - const BRepGraph_OccurrenceId* p = ctx.Occurrences.Seek(BRepGraph_OccurrenceId(srcId.Index)); + const BRepGraph_OccurrenceId* p = + theCtx.Occurrences.Seek(BRepGraph_OccurrenceId(theSrcId.Index)); return p != nullptr ? BRepGraph_NodeId(*p) : BRepGraph_NodeId(); } default: @@ -253,295 +570,337 @@ BRepGraph_NodeId mappedNode(const GraphCopyContext& ctx, BRepGraph_NodeId srcId) //================================================================================================= -BRepGraph_VertexId ensureVertex(GraphCopyContext& ctx, BRepGraph_VertexId srcId) +BRepGraph_VertexId ensureVertex(GraphCopyContext& theCtx, BRepGraph_VertexId theSrcId) { - if (!srcId.IsValidIn(ctx.Source.Topo().Vertices())) + if (!theSrcId.IsValidIn(theCtx.Source.Topo().Vertices())) { return BRepGraph_VertexId(); } - const BRepGraph_VertexId* anExisting = ctx.Vertices.Seek(srcId); + // For self-copy, the mapping starts empty so Seek naturally returns null for unprocessed + // vertices. For external copy, Seek returns non-null for vertices already in the target + // graph (identity-mapped or previously processed). In both cases, returning the existing + // mapping is the "already processed" fast path. + const BRepGraph_VertexId* anExisting = theCtx.Vertices.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } - const BRepGraphInc::VertexDef& aVtx = ctx.Source.Topo().Vertices().Definition(srcId); - const BRepGraph_VertexId aNewId = ctx.Result.Editor().Vertices().Add(aVtx.Point, aVtx.Tolerance); - ctx.Vertices.Bind(srcId, aNewId); - ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + const BRepGraphInc::VertexDef& aVtx = theCtx.Source.Topo().Vertices().Definition(theSrcId); + const BRepGraph_VertexId aNewId = + theCtx.Result.Editor().Vertices().Add(aVtx.Point, aVtx.Tolerance); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + bindTypedMap(theCtx.Vertices, theCtx, theSrcId, aNewId); return aNewId; } //================================================================================================= -BRepGraph_EdgeId ensureEdge(GraphCopyContext& ctx, BRepGraph_EdgeId srcId) +BRepGraph_EdgeId ensureEdge(GraphCopyContext& theCtx, BRepGraph_EdgeId theSrcId) { - if (!srcId.IsValidIn(ctx.Source.Topo().Edges())) + if (!theSrcId.IsValidIn(theCtx.Source.Topo().Edges())) { return BRepGraph_EdgeId(); } - const BRepGraph_EdgeId* anExisting = ctx.Edges.Seek(srcId); + const BRepGraph_EdgeId* anExisting = theCtx.Edges.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } - const BRepGraphInc::EdgeDef& anEdge = ctx.Source.Topo().Edges().Definition(srcId); - const BRepGraph_VertexId aNewStart = - ensureVertex(ctx, BRepGraph_Tool::Edge::StartVertexId(ctx.Source, srcId)); - const BRepGraph_VertexId aNewEnd = - ensureVertex(ctx, BRepGraph_Tool::Edge::EndVertexId(ctx.Source, srcId)); + const BRepGraphInc::EdgeDef& anEdge = theCtx.Source.Topo().Edges().Definition(theSrcId); + const BRepGraph_VertexRefId aStartRefId = + BRepGraph_Tool::Edge::StartVertexId(theCtx.Source, theSrcId); + const BRepGraph_VertexRefId anEndRefId = + BRepGraph_Tool::Edge::EndVertexId(theCtx.Source, theSrcId); + const BRepGraph_VertexId aNewStart = ensureVertex( + theCtx, + aStartRefId.IsValid() ? theCtx.Source.Refs().Vertices().Entry(aStartRefId).ChildVertexId + : BRepGraph_VertexId()); + const BRepGraph_VertexId aNewEnd = ensureVertex( + theCtx, + anEndRefId.IsValid() ? theCtx.Source.Refs().Vertices().Entry(anEndRefId).ChildVertexId + : BRepGraph_VertexId()); - const occ::handle& aSrcCurve = BRepGraph_Tool::Edge::Curve(ctx.Source, srcId); - occ::handle aCurve = copyCurve(aSrcCurve, ctx.CopyGeom); + const occ::handle& aSrcCurve = BRepGraph_Tool::Edge::Curve(theCtx.Source, theSrcId); + occ::handle aCurve = copyCurve(aSrcCurve, theCtx.GeomPol); - const BRepGraph_EdgeId aNewId = ctx.Result.Editor().Edges().Add(aNewStart, - aNewEnd, - aCurve, - anEdge.ParamFirst, - anEdge.ParamLast, - anEdge.Tolerance); + const auto [aEdgeParamFirst, aEdgeParamLast] = + BRepGraph_Tool::Edge::Range(theCtx.Source, theSrcId); + + const BRepGraph_EdgeId aNewId = theCtx.Result.Editor().Edges().Add(aNewStart, + aNewEnd, + aCurve, + aEdgeParamFirst, + aEdgeParamLast, + anEdge.Tolerance); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + if (theCtx.MeshPol != MeshPolicy::Drop && anEdge.Polygon3DRepId.IsValid()) { - BRepGraph_MutGuard aG = ctx.Result.Editor().Edges().Mut(aNewId); - ctx.Result.Editor().Edges().SetDegenerate(aG, anEdge.IsDegenerate); - ctx.Result.Editor().Edges().SetSameParameter(aG, anEdge.SameParameter); - ctx.Result.Editor().Edges().SetSameRange(aG, anEdge.SameRange); + ensurePolygon3DRep(theCtx, anEdge.Polygon3DRepId, aNewId); } - ctx.Edges.Bind(srcId, aNewId); - ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + bindTypedMap(theCtx.Edges, theCtx, theSrcId, aNewId); + const BRepGraphInc::EdgeDef& aNewEdge = theCtx.Result.Topo().Edges().Definition(aNewId); + setRemovedLike(*theCtx.SrcStorage, + *theCtx.DstStorage, + anEdge.StartVertexRefId, + aNewEdge.StartVertexRefId); + setRemovedLike(*theCtx.SrcStorage, + *theCtx.DstStorage, + anEdge.EndVertexRefId, + aNewEdge.EndVertexRefId); + bindItemRemap(theCtx, anEdge.StartVertexRefId, aNewEdge.StartVertexRefId); + bindItemRemap(theCtx, anEdge.EndVertexRefId, aNewEdge.EndVertexRefId); return aNewId; } //================================================================================================= -BRepGraph_WireId ensureWire(GraphCopyContext& ctx, BRepGraph_WireId srcId) +BRepGraph_WireId ensureWire(GraphCopyContext& theCtx, BRepGraph_WireId theSrcId) { - if (!srcId.IsValidIn(ctx.Source.Topo().Wires())) + if (!theSrcId.IsValidIn(theCtx.Source.Topo().Wires())) { return BRepGraph_WireId(); } - const BRepGraph_WireId* anExisting = ctx.Wires.Seek(srcId); + const BRepGraph_WireId* anExisting = theCtx.Wires.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } - NCollection_DynamicArray> aWireEdges; - for (BRepGraph_RefsCoEdgeOfWire aCEIt(ctx.Source, srcId); aCEIt.More(); aCEIt.Next()) + NCollection_LinearVector aNewCoEdgeIds; + NCollection_LinearVector aSrcCoEdgeIds; + for (BRepGraph_CoEdgesOfWire aCEIt(theCtx.Source, theSrcId); aCEIt.More(); aCEIt.Next()) { - const BRepGraphInc::CoEdgeDef& aCoEdge = ctx.Source.Topo().CoEdges().Definition( - ctx.Source.Refs().CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId); - const BRepGraph_EdgeId aNewEdgeId = ensureEdge(ctx, aCoEdge.EdgeDefId); - aWireEdges.Append(std::make_pair(aNewEdgeId, aCoEdge.Orientation)); + const BRepGraphInc::CoEdgeDef& aCoEdge = + theCtx.Source.Topo().CoEdges().Definition(aCEIt.CurrentId()); + const BRepGraph_EdgeId aNewEdgeId = ensureEdge(theCtx, aCoEdge.ChildEdgeId); + const BRepGraph_CoEdgeId aNewCoEdgeId = + theCtx.Result.Editor().CoEdges().Add(aNewEdgeId, aCoEdge.Orientation); + theCtx.DstStorage->SetRemoved(aNewCoEdgeId, theCtx.SrcStorage->IsRemoved(aCEIt.CurrentId())); + aNewCoEdgeIds.Append(aNewCoEdgeId); + aSrcCoEdgeIds.Append(aCEIt.CurrentId()); + } + const BRepGraph_WireId aNewId = theCtx.Result.Editor().Wires().Add(aNewCoEdgeIds.ToArray1()); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + bindTypedMap(theCtx.Wires, theCtx, theSrcId, aNewId); + for (size_t anIdx = 0; anIdx < aSrcCoEdgeIds.Size(); ++anIdx) + { + bindTypedMap(theCtx.CoEdges, theCtx, aSrcCoEdgeIds.Value(anIdx), aNewCoEdgeIds.Value(anIdx)); } - const BRepGraph_WireId aNewId = ctx.Result.Editor().Wires().Add(aWireEdges); - ctx.Wires.Bind(srcId, aNewId); - ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); return aNewId; } //================================================================================================= -void ensurePCurvesForFace(GraphCopyContext& ctx, - BRepGraph_FaceId srcFaceId, - BRepGraph_FaceId newFaceId) +void ensurePCurvesForFace(GraphCopyContext& theCtx, + BRepGraph_FaceId theSrcFaceId, + BRepGraph_FaceId theNewFaceId) { - for (BRepGraph_RefsWireOfFace aWIt(ctx.Source, srcFaceId); aWIt.More(); aWIt.Next()) + for (BRepGraph_RefsWireOfFace aWIt(theCtx.Source, theSrcFaceId); aWIt.More(); aWIt.Next()) { - const BRepGraph_WireId aSrcWireId = ctx.Source.Refs().Wires().Entry(aWIt.CurrentId()).WireDefId; - for (BRepGraph_RefsCoEdgeOfWire aCEIt(ctx.Source, aSrcWireId); aCEIt.More(); aCEIt.Next()) + const BRepGraph_WireId aSrcWireId = + theCtx.Source.Refs().Wires().Entry(aWIt.CurrentId()).ChildWireId; + for (BRepGraph_CoEdgesOfWire aCEIt(theCtx.Source, aSrcWireId); aCEIt.More(); aCEIt.Next()) { - const BRepGraph_CoEdgeId aSrcCoEdgeId = - ctx.Source.Refs().CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId; - const BRepGraphInc::CoEdgeDef& aCoEdge = ctx.Source.Topo().CoEdges().Definition(aSrcCoEdgeId); - if (!aCoEdge.Curve2DRepId.IsValid()) + const BRepGraph_CoEdgeId aSrcCoEdgeId = aCEIt.CurrentId(); + const BRepGraphInc::CoEdgeDef& aCoEdge = + theCtx.Source.Topo().CoEdges().Definition(aSrcCoEdgeId); + if (aCoEdge.FaceId != theSrcFaceId) { continue; } - const BRepGraph_EdgeId* aNewEdge = ctx.Edges.Seek(aCoEdge.EdgeDefId); - if (aNewEdge == nullptr) + const BRepGraph_CoEdgeId* aNewCoEdge = theCtx.CoEdges.Seek(aSrcCoEdgeId); + if (aNewCoEdge == nullptr) { continue; } - const occ::handle& aSrcPC = - BRepGraph_Tool::CoEdge::PCurve(ctx.Source, aSrcCoEdgeId); - occ::handle aNewPC = copyPCurve(aSrcPC, ctx.CopyGeom); - ctx.Result.Editor().CoEdges().AddPCurve(*aNewEdge, - newFaceId, - aNewPC, - aCoEdge.ParamFirst, - aCoEdge.ParamLast, - aCoEdge.Orientation); + + if (aCoEdge.Curve2DRepId.IsValid() + && aCoEdge.Curve2DRepId.IsValid(theCtx.SrcStorage->NbCoEdgeCurves2D()) + && !theCtx.SrcStorage->IsRemoved(aCoEdge.Curve2DRepId)) + { + const BRepGraphInc::CoEdgeCurve2DRep& aSrcCurve2D = + theCtx.SrcStorage->CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + theCtx.Result.Editor().CoEdges().SetPCurve(*aNewCoEdge, + copyPCurve(aSrcCurve2D.Curve, theCtx.GeomPol), + aSrcCurve2D.ParamFirst, + aSrcCurve2D.ParamLast); + } + + // Set FaceId first so SetPersistentPolygonOnTri can resolve the triangulation + theCtx.Result.Editor().CoEdges().SetFaceId(*aNewCoEdge, theNewFaceId); + + if (theCtx.MeshPol != MeshPolicy::Drop && aCoEdge.Polygon2DRepId.IsValid()) + { + ensurePolygon2DRep(theCtx, aCoEdge.Polygon2DRepId, *aNewCoEdge); + } + + if (theCtx.MeshPol != MeshPolicy::Drop && aCoEdge.PolygonOnTriRepId.IsValid()) + { + ensurePolygonOnTriRep(theCtx, aCoEdge.PolygonOnTriRepId, *aNewCoEdge); + } } } } //================================================================================================= -BRepGraph_FaceId ensureFace(GraphCopyContext& ctx, BRepGraph_FaceId srcId) +BRepGraph_FaceId ensureFace(GraphCopyContext& theCtx, BRepGraph_FaceId theSrcId) { - if (!srcId.IsValidIn(ctx.Source.Topo().Faces())) + if (!theSrcId.IsValidIn(theCtx.Source.Topo().Faces())) { return BRepGraph_FaceId(); } - const BRepGraph_FaceId* anExisting = ctx.Faces.Seek(srcId); + const BRepGraph_FaceId* anExisting = theCtx.Faces.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } - const BRepGraphInc::FaceDef& aFace = ctx.Source.Topo().Faces().Definition(srcId); + const BRepGraphInc::FaceDef& aFace = theCtx.Source.Topo().Faces().Definition(theSrcId); - BRepGraph_WireId anOuterWire; - NCollection_DynamicArray anInnerWires; - for (BRepGraph_RefsWireOfFace aWRIt(ctx.Source, srcId); aWRIt.More(); aWRIt.Next()) + BRepGraph_WireId aFirstWire; + NCollection_LinearVector aNextWires; + for (BRepGraph_RefsWireOfFace aWRIt(theCtx.Source, theSrcId); aWRIt.More(); aWRIt.Next()) { - const BRepGraphInc::WireRef& aWR = ctx.Source.Refs().Wires().Entry(aWRIt.CurrentId()); - const BRepGraph_WireId aNewWire = ensureWire(ctx, aWR.WireDefId); - if (aWR.IsOuter) + const BRepGraphInc::WireRef& aWR = theCtx.Source.Refs().Wires().Entry(aWRIt.CurrentId()); + const BRepGraph_WireId aNewWire = ensureWire(theCtx, aWR.ChildWireId); + if (!aFirstWire.IsValid()) { - anOuterWire = aNewWire; + aFirstWire = aNewWire; } else { - anInnerWires.Append(aNewWire); + aNextWires.Append(aNewWire); } } - const occ::handle& aSrcSurf = BRepGraph_Tool::Face::Surface(ctx.Source, srcId); - occ::handle aSurf = copySurface(aSrcSurf, ctx.CopyGeom); + const occ::handle& aSrcSurf = + BRepGraph_Tool::Face::Surface(theCtx.Source, theSrcId); + occ::handle aSurf = copySurface(aSrcSurf, theCtx.GeomPol); const BRepGraph_FaceId aNewId = - ctx.Result.Editor().Faces().Add(aSurf, anOuterWire, anInnerWires, aFace.Tolerance); + theCtx.Result.Editor().Faces().Add(aSurf, aFirstWire, aNextWires.ToArray1(), aFace.Tolerance); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + if (theCtx.MeshPol != MeshPolicy::Drop && aFace.TriangulationRepId.IsValid()) { - BRepGraph_MutGuard aG = ctx.Result.Editor().Faces().Mut(aNewId); - ctx.Result.Editor().Faces().SetNaturalRestriction(aG, aFace.NaturalRestriction); - if (ctx.CopyMesh) - { - ctx.Result.Editor().Faces().SetTriangulationRep(aG, aFace.TriangulationRepId); - } + ensureTriangulationRep(theCtx, aFace.TriangulationRepId, aNewId); } - - if (ctx.CopyMesh) + bindTypedMap(theCtx.Faces, theCtx, theSrcId, aNewId); + const BRepGraphInc::FaceRelations& aSrcRel = theCtx.Source.Topo().Faces().Relations(theSrcId); + const BRepGraphInc::FaceRelations& aDstRel = theCtx.Result.Topo().Faces().Relations(aNewId); + const size_t aNbWireRefs = std::min(aSrcRel.WireRefIds.Size(), aDstRel.WireRefIds.Size()); + for (size_t anIdx = 0; anIdx < aNbWireRefs; ++anIdx) { - const BRepGraph_MeshCache::FaceMeshEntry* aCached = ctx.Source.Mesh().Faces().CachedMesh(srcId); - if (aCached != nullptr) - { - BRepGraph_MeshCache::FaceMeshEntry& aNewEntry = ctx.DstMesh->ChangeFaceMesh(aNewId); - aNewEntry = *aCached; - // Update StoredOwnGen to the destination face's generation so that freshness checks pass. - // The raw copy carries the source OwnGen, which differs from the newly created face. - aNewEntry.StoredOwnGen = ctx.DstStorage->Face(aNewId).OwnGen; - } + theCtx.DstStorage->SetRemoved(aDstRel.WireRefIds.Value(anIdx), + theCtx.SrcStorage->IsRemoved(aSrcRel.WireRefIds.Value(anIdx))); + bindItemRemap(theCtx, aSrcRel.WireRefIds.Value(anIdx), aDstRel.WireRefIds.Value(anIdx)); } - ctx.Faces.Bind(srcId, aNewId); - ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); - // PCurves require both the face and its edges to exist in the result. - ensurePCurvesForFace(ctx, srcId, aNewId); + ensurePCurvesForFace(theCtx, theSrcId, aNewId); return aNewId; } //================================================================================================= -BRepGraph_ShellId ensureShell(GraphCopyContext& ctx, BRepGraph_ShellId srcId) +BRepGraph_ShellId ensureShell(GraphCopyContext& theCtx, BRepGraph_ShellId theSrcId) { - if (!srcId.IsValidIn(ctx.Source.Topo().Shells())) + if (!theSrcId.IsValidIn(theCtx.Source.Topo().Shells())) { return BRepGraph_ShellId(); } - const BRepGraph_ShellId* anExisting = ctx.Shells.Seek(srcId); + const BRepGraph_ShellId* anExisting = theCtx.Shells.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } - const BRepGraphInc::ShellDef& aShellDef = ctx.Source.Topo().Shells().Definition(srcId); - const BRepGraph_ShellId aNewId = ctx.Result.Editor().Shells().Add(); - { - BRepGraph_MutGuard aG = ctx.Result.Editor().Shells().Mut(aNewId); - ctx.Result.Editor().Shells().SetIsClosed(aG, aShellDef.IsClosed); - } - ctx.Shells.Bind(srcId, aNewId); - ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + const BRepGraph_ShellId aNewId = theCtx.Result.Editor().Shells().Add(); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + bindTypedMap(theCtx.Shells, theCtx, theSrcId, aNewId); - for (BRepGraph_RefsFaceOfShell aFRIt(ctx.Source, srcId); aFRIt.More(); aFRIt.Next()) + for (BRepGraph_RefsFaceOfShell aFRIt(theCtx.Source, theSrcId); aFRIt.More(); aFRIt.Next()) { - const BRepGraphInc::FaceRef& aFR = ctx.Source.Refs().Faces().Entry(aFRIt.CurrentId()); - const BRepGraph_FaceId aNewFace = ensureFace(ctx, aFR.FaceDefId); - const BRepGraph_FaceRefId aNewFaceRefId = - ctx.Result.Editor().Shells().AddFace(aNewId, aNewFace, aFR.Orientation); - ctx.Deferred.DeferRef(ctx.Source, aFRIt.CurrentId(), aNewFaceRefId); + const BRepGraphInc::FaceRef& aFR = theCtx.Source.Refs().Faces().Entry(aFRIt.CurrentId()); + const BRepGraph_FaceId aNewFace = ensureFace(theCtx, aFR.ChildFaceId); + const BRepGraph_FaceRefId aNewRef = + theCtx.Result.Editor().Shells().Append(aNewId, aNewFace, aFR.Orientation); + theCtx.DstStorage->SetRemoved(aNewRef, theCtx.SrcStorage->IsRemoved(aFRIt.CurrentId())); + bindItemRemap(theCtx, aFRIt.CurrentId(), aNewRef); } return aNewId; } //================================================================================================= -BRepGraph_SolidId ensureSolid(GraphCopyContext& ctx, BRepGraph_SolidId srcId) +BRepGraph_SolidId ensureSolid(GraphCopyContext& theCtx, BRepGraph_SolidId theSrcId) { - if (!srcId.IsValidIn(ctx.Source.Topo().Solids())) + if (!theSrcId.IsValidIn(theCtx.Source.Topo().Solids())) { return BRepGraph_SolidId(); } - const BRepGraph_SolidId* anExisting = ctx.Solids.Seek(srcId); + const BRepGraph_SolidId* anExisting = theCtx.Solids.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } - const BRepGraph_SolidId aNewId = ctx.Result.Editor().Solids().Add(); - ctx.Solids.Bind(srcId, aNewId); - ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + const BRepGraph_SolidId aNewId = theCtx.Result.Editor().Solids().Add(); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + bindTypedMap(theCtx.Solids, theCtx, theSrcId, aNewId); - for (BRepGraph_RefsShellOfSolid aSRIt(ctx.Source, srcId); aSRIt.More(); aSRIt.Next()) + for (BRepGraph_RefsShellOfSolid aSRIt(theCtx.Source, theSrcId); aSRIt.More(); aSRIt.Next()) { - const BRepGraphInc::ShellRef& aSR = ctx.Source.Refs().Shells().Entry(aSRIt.CurrentId()); - const BRepGraph_ShellId aNewShell = ensureShell(ctx, aSR.ShellDefId); - const BRepGraph_ShellRefId aNewShellRefId = - ctx.Result.Editor().Solids().AddShell(aNewId, aNewShell, aSR.Orientation); - ctx.Deferred.DeferRef(ctx.Source, aSRIt.CurrentId(), aNewShellRefId); + const BRepGraphInc::ShellRef& aSR = theCtx.Source.Refs().Shells().Entry(aSRIt.CurrentId()); + const BRepGraph_ShellId aNewShell = ensureShell(theCtx, aSR.ChildShellId); + const BRepGraph_ShellRefId aNewRef = + theCtx.Result.Editor().Solids().Append(aNewId, aNewShell, aSR.Orientation); + theCtx.DstStorage->SetRemoved(aNewRef, theCtx.SrcStorage->IsRemoved(aSRIt.CurrentId())); + bindItemRemap(theCtx, aSRIt.CurrentId(), aNewRef); } return aNewId; } //================================================================================================= -void ensureNode(GraphCopyContext& ctx, BRepGraph_NodeId srcNodeId) +void ensureNode(GraphCopyContext& theCtx, BRepGraph_NodeId theSrcNodeId) { using Kind = BRepGraph_NodeId::Kind; - switch (srcNodeId.NodeKind) + switch (theSrcNodeId.NodeKind) { case Kind::Vertex: - ensureVertex(ctx, BRepGraph_VertexId(srcNodeId.Index)); + ensureVertex(theCtx, BRepGraph_VertexId(theSrcNodeId.Index)); break; case Kind::Edge: - ensureEdge(ctx, BRepGraph_EdgeId(srcNodeId.Index)); + ensureEdge(theCtx, BRepGraph_EdgeId(theSrcNodeId.Index)); + break; + case Kind::CoEdge: break; case Kind::Wire: - ensureWire(ctx, BRepGraph_WireId(srcNodeId.Index)); + ensureWire(theCtx, BRepGraph_WireId(theSrcNodeId.Index)); break; case Kind::Face: - ensureFace(ctx, BRepGraph_FaceId(srcNodeId.Index)); + ensureFace(theCtx, BRepGraph_FaceId(theSrcNodeId.Index)); break; case Kind::Shell: - ensureShell(ctx, BRepGraph_ShellId(srcNodeId.Index)); + ensureShell(theCtx, BRepGraph_ShellId(theSrcNodeId.Index)); break; case Kind::Solid: - ensureSolid(ctx, BRepGraph_SolidId(srcNodeId.Index)); + ensureSolid(theCtx, BRepGraph_SolidId(theSrcNodeId.Index)); break; case Kind::Compound: - ensureCompound(ctx, BRepGraph_CompoundId(srcNodeId.Index)); + ensureCompound(theCtx, BRepGraph_CompoundId(theSrcNodeId.Index)); break; case Kind::CompSolid: - ensureCompSolid(ctx, BRepGraph_CompSolidId(srcNodeId.Index)); + ensureCompSolid(theCtx, BRepGraph_CompSolidId(theSrcNodeId.Index)); break; case Kind::Product: - ensureProduct(ctx, BRepGraph_ProductId(srcNodeId.Index)); + ensureProduct(theCtx, BRepGraph_ProductId(theSrcNodeId.Index)); break; case Kind::Occurrence: - ensureOccurrence(ctx, BRepGraph_OccurrenceId(srcNodeId.Index)); + ensureOccurrence(theCtx, BRepGraph_OccurrenceId(theSrcNodeId.Index)); break; default: break; @@ -550,13 +909,13 @@ void ensureNode(GraphCopyContext& ctx, BRepGraph_NodeId srcNodeId) //================================================================================================= -BRepGraph_CompoundId ensureCompound(GraphCopyContext& ctx, BRepGraph_CompoundId srcId) +BRepGraph_CompoundId ensureCompound(GraphCopyContext& theCtx, BRepGraph_CompoundId theSrcId) { - if (!srcId.IsValidIn(ctx.Source.Topo().Compounds())) + if (!theSrcId.IsValidIn(theCtx.Source.Topo().Compounds())) { return BRepGraph_CompoundId(); } - const BRepGraph_CompoundId* anExisting = ctx.Compounds.Seek(srcId); + const BRepGraph_CompoundId* anExisting = theCtx.Compounds.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; @@ -565,20 +924,26 @@ BRepGraph_CompoundId ensureCompound(GraphCopyContext& ctx, BRepGraph_CompoundId // Pre-allocate an empty compound and bind it BEFORE recursing into children so a // self-referencing compound chain (A->B->A or A->A) terminates instead of recursing // infinitely. Children are appended one at a time after recursion resolves them. - const NCollection_DynamicArray aEmpty; - const BRepGraph_CompoundId aNewId = ctx.Result.Editor().Compounds().Add(aEmpty); - ctx.Compounds.Bind(srcId, aNewId); - ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + const NCollection_Array1 aEmpty; + const BRepGraph_CompoundId aNewId = theCtx.Result.Editor().Compounds().Add(aEmpty); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + bindTypedMap(theCtx.Compounds, theCtx, theSrcId, aNewId); - for (BRepGraph_RefsChildOfCompound aCRIt(ctx.Source, srcId); aCRIt.More(); aCRIt.Next()) + for (BRepGraph_RefsChildOfCompound aCRIt(theCtx.Source, theSrcId); aCRIt.More(); aCRIt.Next()) { - const BRepGraphInc::ChildRef& aChildRef = ctx.Source.Refs().Children().Entry(aCRIt.CurrentId()); - const BRepGraph_NodeId aSrcChild = aChildRef.ChildDefId; - ensureNode(ctx, aSrcChild); - const BRepGraph_NodeId aMapped = mappedNode(ctx, aSrcChild); + const BRepGraphInc::ChildRef& aChildRef = + theCtx.Source.Refs().Children().Entry(aCRIt.CurrentId()); + const BRepGraph_NodeId aSrcChild = aChildRef.ChildNodeId; + ensureNode(theCtx, aSrcChild); + const BRepGraph_NodeId aMapped = mappedNode(theCtx, aSrcChild); if (aMapped.IsValid()) { - (void)ctx.Result.Editor().Compounds().AddChild(aNewId, aMapped, aChildRef.Orientation); + const BRepGraph_ChildRefId aNewChildRef = + theCtx.Result.Editor().Compounds().Append(aNewId, aMapped, aChildRef.Orientation); + Standard_ASSERT_RAISE(aNewChildRef.IsValid(), + "BRepGraph_Copy: failed to copy compound child ref"); + theCtx.DstStorage->SetRemoved(aNewChildRef, theCtx.SrcStorage->IsRemoved(aCRIt.CurrentId())); + bindItemRemap(theCtx, aCRIt.CurrentId(), aNewChildRef); } } return aNewId; @@ -586,103 +951,116 @@ BRepGraph_CompoundId ensureCompound(GraphCopyContext& ctx, BRepGraph_CompoundId //================================================================================================= -BRepGraph_CompSolidId ensureCompSolid(GraphCopyContext& ctx, BRepGraph_CompSolidId srcId) +BRepGraph_CompSolidId ensureCompSolid(GraphCopyContext& theCtx, BRepGraph_CompSolidId theSrcId) { - if (!srcId.IsValidIn(ctx.Source.Topo().CompSolids())) + if (!theSrcId.IsValidIn(theCtx.Source.Topo().CompSolids())) { return BRepGraph_CompSolidId(); } - const BRepGraph_CompSolidId* anExisting = ctx.CompSolids.Seek(srcId); + const BRepGraph_CompSolidId* anExisting = theCtx.CompSolids.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } - NCollection_DynamicArray aSolidIds; - for (BRepGraph_RefsSolidOfCompSolid aSRIt(ctx.Source, srcId); aSRIt.More(); aSRIt.Next()) + NCollection_LinearVector aSolidIds; + for (BRepGraph_RefsSolidOfCompSolid aSRIt(theCtx.Source, theSrcId); aSRIt.More(); aSRIt.Next()) { const BRepGraph_SolidId aSrcSolid = - ctx.Source.Refs().Solids().Entry(aSRIt.CurrentId()).SolidDefId; - aSolidIds.Append(ensureSolid(ctx, aSrcSolid)); + theCtx.Source.Refs().Solids().Entry(aSRIt.CurrentId()).ChildSolidId; + aSolidIds.Append(ensureSolid(theCtx, aSrcSolid)); } - const BRepGraph_CompSolidId aNewId = ctx.Result.Editor().CompSolids().Add(aSolidIds); - ctx.CompSolids.Bind(srcId, aNewId); - ctx.Deferred.DeferNode(ctx.Source, srcId, aNewId); + const BRepGraph_CompSolidId aNewId = + theCtx.Result.Editor().CompSolids().Add(aSolidIds.ToArray1()); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + bindTypedMap(theCtx.CompSolids, theCtx, theSrcId, aNewId); + const BRepGraphInc::CompSolidRelations& aSrcRel = + theCtx.Source.Topo().CompSolids().Relations(theSrcId); + const BRepGraphInc::CompSolidRelations& aDstRel = + theCtx.Result.Topo().CompSolids().Relations(aNewId); + const size_t aNbSolidRefs = std::min(aSrcRel.SolidRefIds.Size(), aDstRel.SolidRefIds.Size()); + for (size_t anIdx = 0; anIdx < aNbSolidRefs; ++anIdx) + { + theCtx.DstStorage->SetRemoved(aDstRel.SolidRefIds.Value(anIdx), + theCtx.SrcStorage->IsRemoved(aSrcRel.SolidRefIds.Value(anIdx))); + bindItemRemap(theCtx, aSrcRel.SolidRefIds.Value(anIdx), aDstRel.SolidRefIds.Value(anIdx)); + } return aNewId; } //================================================================================================= -BRepGraph_OccurrenceRefId ensureOccurrenceRef(GraphCopyContext& ctx, - BRepGraph_OccurrenceRefId srcRefId) +BRepGraph_OccurrenceRefId ensureOccurrenceRef(GraphCopyContext& theCtx, + BRepGraph_OccurrenceRefId theSrcRefId, + BRepGraph_ProductId theDstParentProductId) { - const BRepGraph_OccurrenceRefId* anExisting = ctx.OccurrenceRefs.Seek(srcRefId); + const BRepGraph_OccurrenceRefId* anExisting = theCtx.OccurrenceRefs.Seek(theSrcRefId); if (anExisting != nullptr) { return *anExisting; } - const BRepGraphInc::OccurrenceRef& aSrcRef = ctx.SrcStorage->OccurrenceRef(srcRefId); - const BRepGraph_OccurrenceRefId aNewId = ctx.DstStorage->AppendOccurrenceRef(); - ctx.OccurrenceRefs.Bind(srcRefId, aNewId); - - BRepGraphInc::OccurrenceRef& aNewRef = ctx.DstStorage->ChangeOccurrenceRef(aNewId); - aNewRef.IsRemoved = aSrcRef.IsRemoved; - aNewRef.LocalLocation = aSrcRef.LocalLocation; - if (aSrcRef.OccurrenceDefId.IsValid()) + const BRepGraphInc::OccurrenceRef& aSrcRef = theCtx.SrcStorage->OccurrenceRef(theSrcRefId); + BRepGraph_OccurrenceId aNewOccurrenceId; + if (aSrcRef.ChildOccurrenceId.IsValid()) { - aNewRef.OccurrenceDefId = ensureOccurrence(ctx, aSrcRef.OccurrenceDefId); + aNewOccurrenceId = ensureOccurrence(theCtx, aSrcRef.ChildOccurrenceId); } - // ParentId is set by the caller (ensureProduct) after the ref is created. + const BRepGraph_OccurrenceRefId aNewId = + theCtx.DstStorage->AttachOccurrenceToProduct(theDstParentProductId, + aNewOccurrenceId, + aSrcRef.LocalLocation); + theCtx.OccurrenceRefs.Bind(theSrcRefId, aNewId); + bindItemRemap(theCtx, theSrcRefId, aNewId); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcRefId)); return aNewId; } //================================================================================================= -BRepGraph_OccurrenceId ensureOccurrence(GraphCopyContext& ctx, BRepGraph_OccurrenceId srcId) +BRepGraph_OccurrenceId ensureOccurrence(GraphCopyContext& theCtx, BRepGraph_OccurrenceId theSrcId) { - const BRepGraph_OccurrenceId* anExisting = ctx.Occurrences.Seek(srcId); + const BRepGraph_OccurrenceId* anExisting = theCtx.Occurrences.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } - const BRepGraphInc::OccurrenceDef& aSrcOcc = ctx.SrcStorage->Occurrence(srcId); - const BRepGraph_OccurrenceId aNewId = ctx.DstStorage->AppendOccurrence(); - ctx.Occurrences.Bind(srcId, aNewId); + const BRepGraphInc::OccurrenceDef& aSrcOcc = theCtx.SrcStorage->Occurrence(theSrcId); + const BRepGraph_OccurrenceId aNewId = theCtx.DstStorage->AppendOccurrence(); + bindTypedMap(theCtx.Occurrences, theCtx, theSrcId, aNewId); - BRepGraphInc::OccurrenceDef& aNewOcc = ctx.DstStorage->ChangeOccurrence(aNewId); - aNewOcc.IsRemoved = aSrcOcc.IsRemoved; - if (aSrcOcc.ChildDefId.IsValid()) + BRepGraphInc::OccurrenceDef& aNewOcc = theCtx.DstStorage->ChangeOccurrence(aNewId); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + if (aSrcOcc.ChildNodeId.IsValid()) { - ensureNode(ctx, aSrcOcc.ChildDefId); - aNewOcc.ChildDefId = mappedNode(ctx, aSrcOcc.ChildDefId); + ensureNode(theCtx, aSrcOcc.ChildNodeId); + aNewOcc.ChildNodeId = mappedNode(theCtx, aSrcOcc.ChildNodeId); } return aNewId; } //================================================================================================= -BRepGraph_ProductId ensureProduct(GraphCopyContext& ctx, BRepGraph_ProductId srcId) +BRepGraph_ProductId ensureProduct(GraphCopyContext& theCtx, BRepGraph_ProductId theSrcId) { - const BRepGraph_ProductId* anExisting = ctx.Products.Seek(srcId); + const BRepGraph_ProductId* anExisting = theCtx.Products.Seek(theSrcId); if (anExisting != nullptr) { return *anExisting; } // Bind before iterating refs so re-entrant calls (assembly cycles) short-circuit. - const BRepGraph_ProductId aNewId = ctx.DstStorage->AppendProduct(); - ctx.Products.Bind(srcId, aNewId); + const BRepGraph_ProductId aNewId = theCtx.DstStorage->AppendProduct(); + theCtx.DstStorage->SetRemoved(aNewId, theCtx.SrcStorage->IsRemoved(theSrcId)); + bindTypedMap(theCtx.Products, theCtx, theSrcId, aNewId); - const BRepGraphInc::ProductDef& aSrcProd = ctx.SrcStorage->Product(srcId); - for (const BRepGraph_OccurrenceRefId& aSrcRefId : aSrcProd.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& aSrcRefId : + theCtx.SrcStorage->ProductRelations(theSrcId).OccurrenceRefIds) { - const BRepGraph_OccurrenceRefId aNewRefId = ensureOccurrenceRef(ctx, aSrcRefId); - ctx.DstStorage->ChangeProduct(aNewId).OccurrenceRefIds.Append(aNewRefId); - // Set ParentId: the owning product is the parent of this occurrence reference. - ctx.DstStorage->ChangeOccurrenceRef(aNewRefId).ParentId = BRepGraph_NodeId(aNewId); + const BRepGraph_OccurrenceRefId aNewRefId = ensureOccurrenceRef(theCtx, aSrcRefId, aNewId); + Standard_ASSERT_RAISE(aNewRefId.IsValid(), "BRepGraph_Copy: failed to copy occurrence ref"); } return aNewId; } @@ -691,420 +1069,559 @@ BRepGraph_ProductId ensureProduct(GraphCopyContext& ctx, BRepGraph_ProductId src //================================================================================================= -void BRepGraph_Copy::reserveTransientCache(BRepGraph& theGraph) +bool BRepGraph_Copy::Perform(const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + GeomPolicy theGeomPolicy, + MeshPolicy theMeshPolicy, + CachePolicy theCachePolicy) { - BRepGraphInc_Storage& aStorage = theGraph.incStorage(); - int aCounts[BRepGraph_TransientCache::THE_KIND_COUNT] = {}; - aCounts[static_cast(BRepGraph_NodeId::Kind::Vertex)] = aStorage.NbVertices(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Edge)] = aStorage.NbEdges(); - aCounts[static_cast(BRepGraph_NodeId::Kind::CoEdge)] = aStorage.NbCoEdges(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Wire)] = aStorage.NbWires(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Face)] = aStorage.NbFaces(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Shell)] = aStorage.NbShells(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Solid)] = aStorage.NbSolids(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Compound)] = aStorage.NbCompounds(); - aCounts[static_cast(BRepGraph_NodeId::Kind::CompSolid)] = aStorage.NbCompSolids(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Product)] = aStorage.NbProducts(); - aCounts[static_cast(BRepGraph_NodeId::Kind::Occurrence)] = aStorage.NbOccurrences(); - int aReservedKindCount = BRepGraph_TransientCache::THE_DEFAULT_RESERVED_KIND_COUNT; - const int aRegisteredKindCount = BRepGraph_CacheKindRegistry::NbRegistered(); - if (aRegisteredKindCount > aReservedKindCount) + if (&theSourceGraph == &theTargetGraph) { - aReservedKindCount = aRegisteredKindCount; - } - theGraph.transientCache().Reserve(aReservedKindCount, aCounts); -} - -//================================================================================================= - -BRepGraph BRepGraph_Copy::Perform(const BRepGraph& theGraph, const bool theCopyGeom) -{ - BRepGraph aResult; - if (!theGraph.IsDone()) - { - return aResult; + return true; // self-copy: identity no-op } - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - DeferredCacheTransfers aDeferred; + if (theSourceGraph.IsEmpty()) + { + return false; + } + + // Non-empty target: use explicit mapping via GraphCopyContext (same as CopyNode). + // IDs in theTargetGraph will differ from theSourceGraph. + if (!theTargetGraph.IsEmpty()) + { + GraphCopyContext aCtx(theSourceGraph, theTargetGraph, false, theGeomPolicy, theMeshPolicy); + aCtx.SrcStorage = &theSourceGraph.incStorage(); + aCtx.DstStorage = &theTargetGraph.incStorage(); + aCtx.DstData = theTargetGraph.data(); + aCtx.ItemRemap.Reserve(itemCapacityUpperBound(theSourceGraph.incStorage())); + + // Copy all topological entities bottom-up. + for (BRepGraph_FullVertexIterator aVIt(theSourceGraph); aVIt.More(); aVIt.Next()) + { + ensureVertex(aCtx, aVIt.CurrentId()); + } + for (BRepGraph_FullEdgeIterator aEIt(theSourceGraph); aEIt.More(); aEIt.Next()) + { + ensureEdge(aCtx, aEIt.CurrentId()); + } + for (BRepGraph_FullWireIterator aWIt(theSourceGraph); aWIt.More(); aWIt.Next()) + { + ensureWire(aCtx, aWIt.CurrentId()); + } + for (BRepGraph_FullFaceIterator aFIt(theSourceGraph); aFIt.More(); aFIt.Next()) + { + ensureFace(aCtx, aFIt.CurrentId()); + } + for (BRepGraph_FullShellIterator aSIt(theSourceGraph); aSIt.More(); aSIt.Next()) + { + ensureShell(aCtx, aSIt.CurrentId()); + } + for (BRepGraph_FullSolidIterator aSoIt(theSourceGraph); aSoIt.More(); aSoIt.Next()) + { + ensureSolid(aCtx, aSoIt.CurrentId()); + } + for (BRepGraph_FullCompoundIterator aCIt(theSourceGraph); aCIt.More(); aCIt.Next()) + { + ensureCompound(aCtx, aCIt.CurrentId()); + } + for (BRepGraph_FullCompSolidIterator aCSIt(theSourceGraph); aCSIt.More(); aCSIt.Next()) + { + ensureCompSolid(aCtx, aCSIt.CurrentId()); + } + for (BRepGraph_FullProductIterator aPIt(theSourceGraph); aPIt.More(); aPIt.Next()) + { + ensureProduct(aCtx, aPIt.CurrentId()); + } + for (BRepGraph_FullOccurrenceIterator aOIt(theSourceGraph); aOIt.More(); aOIt.Next()) + { + ensureOccurrence(aCtx, aOIt.CurrentId()); + } + + // Build root product list. + if (!aCtx.Products.IsEmpty()) + { + NCollection_FlatMap aReferencedProducts; + for (BRepGraph_FullOccurrenceIterator anOccIt(theTargetGraph); anOccIt.More(); anOccIt.Next()) + { + const BRepGraph_OccurrenceId anOccId = anOccIt.CurrentId(); + const BRepGraphInc::OccurrenceDef& anOcc = aCtx.DstStorage->Occurrence(anOccId); + if (!aCtx.DstStorage->IsRemoved(anOccId) + && anOcc.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) + { + const BRepGraph_ProductId aChildProdId = + BRepGraph_ProductId::FromNodeId(anOcc.ChildNodeId); + if (aChildProdId.IsValidIn(theTargetGraph.Topo().Products())) + { + aReferencedProducts.Add(aChildProdId); + } + } + } + for (BRepGraph_FullProductIterator aProdIt(theTargetGraph); aProdIt.More(); aProdIt.Next()) + { + const BRepGraph_ProductId aProdId = aProdIt.CurrentId(); + if (!aCtx.DstStorage->IsRemoved(aProdId) && !aReferencedProducts.Contains(aProdId)) + { + aCtx.DstStorage->ChangeRootProductIds().Append(aProdId); + } + } + } + + aCtx.DstStorage->MarkUIDReverseIndexesDirty(); + aCtx.DstStorage->RebuildDerivedRelationsPreservingActiveCounts(); + theSourceGraph.LayerRegistry().CopyLayersTo(theTargetGraph, + aCtx.ItemRemap, + BRepGraph_CopyRemap::Mode::Copy); + if (theCachePolicy == CachePolicy::CopyFresh) + { + theSourceGraph.CacheRegistry().CopyFreshCachesTo(theTargetGraph, + aCtx.ItemRemap, + BRepGraph_CopyRemap::Mode::Copy); + } + return true; + } + + // Empty target: identity-mapped fast path (old index == new index). + + const bool doCopyMesh = theMeshPolicy != MeshPolicy::Drop; + const BRepGraph::RefsView& aRefs = theSourceGraph.Refs(); // Bottom-up graph rebuild via EditorView. - // Since this is a full copy, old index == new index (identity mapping). + // Since this is a full copy into an empty target, old index == new index (identity mapping). + copyCurve2DReps(theSourceGraph.incStorage(), theTargetGraph.incStorage(), theGeomPolicy); + if (doCopyMesh) + { + copyPersistentMeshReps(theSourceGraph.incStorage(), theTargetGraph.incStorage(), theMeshPolicy); + } // Vertices. - for (BRepGraph_FullVertexIterator aVertexIt(theGraph); aVertexIt.More(); aVertexIt.Next()) + for (BRepGraph_FullVertexIterator aVertexIt(theSourceGraph); aVertexIt.More(); aVertexIt.Next()) { const BRepGraph_VertexId aVertexId = aVertexIt.CurrentId(); - const BRepGraphInc::VertexDef& aVtx = theGraph.Topo().Vertices().Definition(aVertexId); - (void)aResult.Editor().Vertices().Add(aVtx.Point, aVtx.Tolerance); - aDeferred.DeferNode(theGraph, aVertexId, aVertexId); + const BRepGraphInc::VertexDef& aVtx = theSourceGraph.Topo().Vertices().Definition(aVertexId); + const BRepGraph_VertexId aNewVertexId = + theTargetGraph.Editor().Vertices().Add(aVtx.Point, aVtx.Tolerance); + Standard_ASSERT_RAISE(aNewVertexId == aVertexId, "BRepGraph_Copy: unexpected vertex id"); + theTargetGraph.incStorage().SetRemoved(aNewVertexId, + theSourceGraph.incStorage().IsRemoved(aVertexId)); } // Edges. - for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) + for (BRepGraph_FullEdgeIterator anEdgeIt(theSourceGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(anEdgeId); + const BRepGraphInc::EdgeDef& anEdge = theSourceGraph.Topo().Edges().Definition(anEdgeId); - const occ::handle& anEdgeSrcCurve = BRepGraph_Tool::Edge::Curve(theGraph, anEdgeId); - occ::handle aCurve = copyCurve(anEdgeSrcCurve, theCopyGeom); + const occ::handle& anEdgeSrcCurve = + BRepGraph_Tool::Edge::Curve(theSourceGraph, anEdgeId); + occ::handle aCurve = copyCurve(anEdgeSrcCurve, theGeomPolicy); - // Resolve vertex def ids via Tool helpers (identity mapping: old index == new index). - const BRepGraph_VertexId aStartVtxId = BRepGraph_Tool::Edge::StartVertexId(theGraph, anEdgeId); - const BRepGraph_VertexId anEndVtxId = BRepGraph_Tool::Edge::EndVertexId(theGraph, anEdgeId); + const BRepGraph_VertexRefId aStartRefId = + BRepGraph_Tool::Edge::StartVertexId(theSourceGraph, anEdgeId); + const BRepGraph_VertexRefId anEndRefId = + BRepGraph_Tool::Edge::EndVertexId(theSourceGraph, anEdgeId); + const BRepGraph_VertexId aStartVtxId = + aStartRefId.IsValid() ? theSourceGraph.Refs().Vertices().Entry(aStartRefId).ChildVertexId + : BRepGraph_VertexId(); + const BRepGraph_VertexId anEndVtxId = + anEndRefId.IsValid() ? theSourceGraph.Refs().Vertices().Entry(anEndRefId).ChildVertexId + : BRepGraph_VertexId(); - (void)aResult.Editor().Edges().Add(aStartVtxId, - anEndVtxId, - aCurve, - anEdge.ParamFirst, - anEdge.ParamLast, - anEdge.Tolerance); + const auto [aEdgePF, aEdgePL] = BRepGraph_Tool::Edge::Range(theSourceGraph, anEdgeId); - { - BRepGraph_MutGuard aNewEdge = aResult.Editor().Edges().Mut(anEdgeId); - aResult.Editor().Edges().SetDegenerate(aNewEdge, anEdge.IsDegenerate); - aResult.Editor().Edges().SetSameParameter(aNewEdge, anEdge.SameParameter); - aResult.Editor().Edges().SetSameRange(aNewEdge, anEdge.SameRange); - } - aDeferred.DeferNode(theGraph, anEdgeId, anEdgeId); + const BRepGraph_EdgeId aNewEdgeId = theTargetGraph.Editor().Edges().Add(aStartVtxId, + anEndVtxId, + aCurve, + aEdgePF, + aEdgePL, + anEdge.Tolerance); + Standard_ASSERT_RAISE(aNewEdgeId == anEdgeId, "BRepGraph_Copy: unexpected edge id"); + theTargetGraph.incStorage().SetRemoved(aNewEdgeId, + theSourceGraph.incStorage().IsRemoved(anEdgeId)); + const BRepGraphInc::EdgeDef& aNewEdge = theTargetGraph.incStorage().Edge(aNewEdgeId); + setRemovedLike(theSourceGraph.incStorage(), + theTargetGraph.incStorage(), + anEdge.StartVertexRefId, + aNewEdge.StartVertexRefId); + setRemovedLike(theSourceGraph.incStorage(), + theTargetGraph.incStorage(), + anEdge.EndVertexRefId, + aNewEdge.EndVertexRefId); + theTargetGraph.incStorage().ChangeEdge(anEdgeId).Polygon3DRepId = anEdge.Polygon3DRepId; } // Wires. - for (BRepGraph_FullWireIterator aWireIt(theGraph); aWireIt.More(); aWireIt.Next()) + for (BRepGraph_FullWireIterator aWireIt(theSourceGraph); aWireIt.More(); aWireIt.Next()) { - const BRepGraph_WireId aWireId = aWireIt.CurrentId(); - NCollection_DynamicArray> aWireEdges; - for (BRepGraph_RefsCoEdgeOfWire aCEIt(theGraph, aWireId); aCEIt.More(); aCEIt.Next()) + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); + NCollection_LinearVector aCoEdgeIds; + for (BRepGraph_CoEdgesOfWire aCEIt(theSourceGraph, aWireId); aCEIt.More(); aCEIt.Next()) { const BRepGraphInc::CoEdgeDef& aCoEdge = - theGraph.Topo().CoEdges().Definition(aRefs.CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId); - aWireEdges.Append(std::make_pair(aCoEdge.EdgeDefId, aCoEdge.Orientation)); + theSourceGraph.Topo().CoEdges().Definition(aCEIt.CurrentId()); + aCoEdgeIds.Append( + theTargetGraph.Editor().CoEdges().Add(aCoEdge.ChildEdgeId, aCoEdge.Orientation)); } - (void)aResult.Editor().Wires().Add(aWireEdges); - aDeferred.DeferNode(theGraph, aWireId, aWireId); + const BRepGraph_WireId aNewWireId = theTargetGraph.Editor().Wires().Add(aCoEdgeIds.ToArray1()); + Standard_ASSERT_RAISE(aNewWireId == aWireId, "BRepGraph_Copy: unexpected wire id"); + theTargetGraph.incStorage().SetRemoved(aNewWireId, + theSourceGraph.incStorage().IsRemoved(aWireId)); } // Faces. - for (BRepGraph_FullFaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) + for (BRepGraph_FullFaceIterator aFaceIt(theSourceGraph); aFaceIt.More(); aFaceIt.Next()) { const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - const BRepGraphInc::FaceDef& aFace = theGraph.Topo().Faces().Definition(aFaceId); + const BRepGraphInc::FaceDef& aFace = theSourceGraph.Topo().Faces().Definition(aFaceId); const occ::handle& aFaceSrcSurf = - BRepGraph_Tool::Face::Surface(theGraph, aFaceId); - occ::handle aSurf = copySurface(aFaceSrcSurf, theCopyGeom); + BRepGraph_Tool::Face::Surface(theSourceGraph, aFaceId); + occ::handle aSurf = copySurface(aFaceSrcSurf, theGeomPolicy); - // Get outer/inner wire def NodeIds via typed iterator. - BRepGraph_WireId anOuterWire; - NCollection_DynamicArray anInnerWires; + BRepGraph_WireId aFirstWire; + NCollection_LinearVector aNextWires; - for (BRepGraph_RefsWireOfFace aWRIt(theGraph, aFaceId); aWRIt.More(); aWRIt.Next()) + for (BRepGraph_RefsWireOfFace aWRIt(theSourceGraph, aFaceId); aWRIt.More(); aWRIt.Next()) { const BRepGraphInc::WireRef& aWR = aRefs.Wires().Entry(aWRIt.CurrentId()); - if (aWR.IsOuter) + if (!aFirstWire.IsValid()) { - anOuterWire = aWR.WireDefId; + aFirstWire = aWR.ChildWireId; } else { - anInnerWires.Append(aWR.WireDefId); + aNextWires.Append(aWR.ChildWireId); } } - (void)aResult.Editor().Faces().Add(aSurf, anOuterWire, anInnerWires, aFace.Tolerance); - + const BRepGraph_FaceId aNewFaceId = theTargetGraph.Editor().Faces().Add(aSurf, + aFirstWire, + aNextWires.ToArray1(), + aFace.Tolerance); + Standard_ASSERT_RAISE(aNewFaceId == aFaceId, "BRepGraph_Copy: unexpected face id"); + theTargetGraph.incStorage().SetRemoved(aNewFaceId, + theSourceGraph.incStorage().IsRemoved(aFaceId)); + const BRepGraphInc::FaceRelations& aSrcRel = theSourceGraph.incStorage().FaceRelations(aFaceId); + const BRepGraphInc::FaceRelations& aDstRel = + theTargetGraph.incStorage().FaceRelations(aNewFaceId); + const size_t aNbWireRefs = std::min(aSrcRel.WireRefIds.Size(), aDstRel.WireRefIds.Size()); + for (size_t anIdx = 0; anIdx < aNbWireRefs; ++anIdx) { - BRepGraph_MutGuard aNewFace = aResult.Editor().Faces().Mut(aFaceId); - aResult.Editor().Faces().SetNaturalRestriction(aNewFace, aFace.NaturalRestriction); - aResult.Editor().Faces().SetTriangulationRep(aNewFace, aFace.TriangulationRepId); + theTargetGraph.incStorage().SetRemoved( + aDstRel.WireRefIds.Value(anIdx), + theSourceGraph.incStorage().IsRemoved(aSrcRel.WireRefIds.Value(anIdx))); } - // Copy cached mesh data if present. - const BRepGraph_MeshCache::FaceMeshEntry* aCachedFace = - theGraph.Mesh().Faces().CachedMesh(aFaceId); - if (aCachedFace != nullptr) - { - BRepGraph_MeshCache::FaceMeshEntry& aNewEntry = aResult.meshCache().ChangeFaceMesh(aFaceId); - aNewEntry = *aCachedFace; - } - aDeferred.DeferNode(theGraph, aFaceId, aFaceId); + theTargetGraph.incStorage().ChangeFace(aFaceId).TriangulationRepId = aFace.TriangulationRepId; } - // PCurves via CoEdge data (after edges and faces are created). - for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) + Standard_ASSERT_RAISE(theTargetGraph.incStorage().NbCoEdges() + <= theSourceGraph.incStorage().NbCoEdges(), + "BRepGraph_Copy: unexpected coedge count after wire copy"); + while (theTargetGraph.incStorage().NbCoEdges() < theSourceGraph.incStorage().NbCoEdges()) { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIds = - theGraph.Topo().Edges().CoEdges(anEdgeId); - for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIds) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(aCoEdgeId); - if (!aCoEdge.Curve2DRepId.IsValid()) - { - continue; - } - - const occ::handle& aCoEdgeSrcPC = - BRepGraph_Tool::CoEdge::PCurve(theGraph, aCoEdgeId); - occ::handle aNewPC = copyPCurve(aCoEdgeSrcPC, theCopyGeom); - aResult.Editor().CoEdges().AddPCurve(anEdgeId, - aCoEdge.FaceDefId, - aNewPC, - aCoEdge.ParamFirst, - aCoEdge.ParamLast, - aCoEdge.Orientation); - } + const BRepGraph_CoEdgeId aNewId = theTargetGraph.incStorage().AppendCoEdge(); + Standard_ASSERT_RAISE(aNewId.Index + 1u == theTargetGraph.incStorage().NbCoEdges(), + "BRepGraph_Copy: unexpected coedge append id"); + } + for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(theSourceGraph.incStorage().NbCoEdges()); + ++aCoEdgeId) + { + theTargetGraph.incStorage().ChangeCoEdge(aCoEdgeId) = + theSourceGraph.incStorage().CoEdge(aCoEdgeId); + theTargetGraph.incStorage().SetRemoved(aCoEdgeId, + theSourceGraph.incStorage().IsRemoved(aCoEdgeId)); } // Shells. - for (BRepGraph_FullShellIterator aShellIt(theGraph); aShellIt.More(); aShellIt.Next()) + for (BRepGraph_FullShellIterator aShellIt(theSourceGraph); aShellIt.More(); aShellIt.Next()) { const BRepGraph_ShellId aShellId = aShellIt.CurrentId(); - BRepGraph_ShellId aNewShellId = aResult.Editor().Shells().Add(); - aDeferred.DeferNode(theGraph, aShellId, aShellId); + BRepGraph_ShellId aNewShellId = theTargetGraph.Editor().Shells().Add(); + Standard_ASSERT_RAISE(aNewShellId == aShellId, "BRepGraph_Copy: unexpected shell id"); + theTargetGraph.incStorage().SetRemoved(aNewShellId, + theSourceGraph.incStorage().IsRemoved(aShellId)); - for (BRepGraph_RefsFaceOfShell aFRIt(theGraph, aShellId); aFRIt.More(); aFRIt.Next()) + for (BRepGraph_RefsFaceOfShell aFRIt(theSourceGraph, aShellId); aFRIt.More(); aFRIt.Next()) { const BRepGraphInc::FaceRef& aFR = aRefs.Faces().Entry(aFRIt.CurrentId()); - const BRepGraph_FaceRefId aNewFaceRefId = - aResult.Editor().Shells().AddFace(aNewShellId, aFR.FaceDefId, aFR.Orientation); - aDeferred.DeferRef(theGraph, aFRIt.CurrentId(), aNewFaceRefId); + const BRepGraph_FaceRefId aNewRef = + theTargetGraph.Editor().Shells().Append(aNewShellId, aFR.ChildFaceId, aFR.Orientation); + theTargetGraph.incStorage().SetRemoved( + aNewRef, + theSourceGraph.incStorage().IsRemoved(aFRIt.CurrentId())); } } // Solids. - for (BRepGraph_FullSolidIterator aSolidIt(theGraph); aSolidIt.More(); aSolidIt.Next()) + for (BRepGraph_FullSolidIterator aSolidIt(theSourceGraph); aSolidIt.More(); aSolidIt.Next()) { const BRepGraph_SolidId aSolidId = aSolidIt.CurrentId(); - BRepGraph_SolidId aNewSolidId = aResult.Editor().Solids().Add(); - aDeferred.DeferNode(theGraph, aSolidId, aSolidId); + BRepGraph_SolidId aNewSolidId = theTargetGraph.Editor().Solids().Add(); + Standard_ASSERT_RAISE(aNewSolidId == aSolidId, "BRepGraph_Copy: unexpected solid id"); + theTargetGraph.incStorage().SetRemoved(aNewSolidId, + theSourceGraph.incStorage().IsRemoved(aSolidId)); - for (BRepGraph_RefsShellOfSolid aSRIt(theGraph, aSolidId); aSRIt.More(); aSRIt.Next()) + for (BRepGraph_RefsShellOfSolid aSRIt(theSourceGraph, aSolidId); aSRIt.More(); aSRIt.Next()) { const BRepGraphInc::ShellRef& aSR = aRefs.Shells().Entry(aSRIt.CurrentId()); - const BRepGraph_ShellRefId aNewShellRefId = - aResult.Editor().Solids().AddShell(aNewSolidId, aSR.ShellDefId, aSR.Orientation); - aDeferred.DeferRef(theGraph, aSRIt.CurrentId(), aNewShellRefId); + const BRepGraph_ShellRefId aNewRef = + theTargetGraph.Editor().Solids().Append(aNewSolidId, aSR.ChildShellId, aSR.Orientation); + theTargetGraph.incStorage().SetRemoved( + aNewRef, + theSourceGraph.incStorage().IsRemoved(aSRIt.CurrentId())); } } // Compounds. - for (BRepGraph_FullCompoundIterator aCompoundIt(theGraph); aCompoundIt.More(); aCompoundIt.Next()) + for (BRepGraph_FullCompoundIterator aCompoundIt(theSourceGraph); aCompoundIt.More(); + aCompoundIt.Next()) { const BRepGraph_CompoundId aCompoundId = aCompoundIt.CurrentId(); - NCollection_DynamicArray aChildNodeIds; - for (BRepGraph_RefsChildOfCompound aCRIt(theGraph, aCompoundId); aCRIt.More(); aCRIt.Next()) + NCollection_LinearVector aChildNodeIds; + for (BRepGraph_RefsChildOfCompound aCRIt(theSourceGraph, aCompoundId); aCRIt.More(); + aCRIt.Next()) { - aChildNodeIds.Append(aRefs.Children().Entry(aCRIt.CurrentId()).ChildDefId); + aChildNodeIds.Append(aRefs.Children().Entry(aCRIt.CurrentId()).ChildNodeId); + } + const BRepGraph_CompoundId aNewCompoundId = + theTargetGraph.Editor().Compounds().Add(aChildNodeIds.ToArray1()); + Standard_ASSERT_RAISE(aNewCompoundId == aCompoundId, "BRepGraph_Copy: unexpected compound id"); + theTargetGraph.incStorage().SetRemoved(aNewCompoundId, + theSourceGraph.incStorage().IsRemoved(aCompoundId)); + const BRepGraphInc::CompoundRelations& aSrcRel = + theSourceGraph.incStorage().CompoundRelations(aCompoundId); + const BRepGraphInc::CompoundRelations& aDstRel = + theTargetGraph.incStorage().CompoundRelations(aNewCompoundId); + const size_t aNbChildRefs = std::min(aSrcRel.ChildRefIds.Size(), aDstRel.ChildRefIds.Size()); + for (size_t anIdx = 0; anIdx < aNbChildRefs; ++anIdx) + { + theTargetGraph.incStorage().SetRemoved( + aDstRel.ChildRefIds.Value(anIdx), + theSourceGraph.incStorage().IsRemoved(aSrcRel.ChildRefIds.Value(anIdx))); } - (void)aResult.Editor().Compounds().Add(aChildNodeIds); - aDeferred.DeferNode(theGraph, aCompoundId, aCompoundId); } // CompSolids. - for (BRepGraph_FullCompSolidIterator aCompSolidIt(theGraph); aCompSolidIt.More(); + for (BRepGraph_FullCompSolidIterator aCompSolidIt(theSourceGraph); aCompSolidIt.More(); aCompSolidIt.Next()) { const BRepGraph_CompSolidId aCompSolidId = aCompSolidIt.CurrentId(); - NCollection_DynamicArray aSolidNodeIds; - for (BRepGraph_RefsSolidOfCompSolid aSRIt(theGraph, aCompSolidId); aSRIt.More(); aSRIt.Next()) + NCollection_LinearVector aSolidNodeIds; + for (BRepGraph_RefsSolidOfCompSolid aSRIt(theSourceGraph, aCompSolidId); aSRIt.More(); + aSRIt.Next()) { - aSolidNodeIds.Append(aRefs.Solids().Entry(aSRIt.CurrentId()).SolidDefId); + aSolidNodeIds.Append(aRefs.Solids().Entry(aSRIt.CurrentId()).ChildSolidId); + } + const BRepGraph_CompSolidId aNewCompSolidId = + theTargetGraph.Editor().CompSolids().Add(aSolidNodeIds.ToArray1()); + Standard_ASSERT_RAISE(aNewCompSolidId == aCompSolidId, + "BRepGraph_Copy: unexpected compsolid id"); + theTargetGraph.incStorage().SetRemoved(aNewCompSolidId, + theSourceGraph.incStorage().IsRemoved(aCompSolidId)); + const BRepGraphInc::CompSolidRelations& aSrcRel = + theSourceGraph.incStorage().CompSolidRelations(aCompSolidId); + const BRepGraphInc::CompSolidRelations& aDstRel = + theTargetGraph.incStorage().CompSolidRelations(aNewCompSolidId); + const size_t aNbSolidRefs = std::min(aSrcRel.SolidRefIds.Size(), aDstRel.SolidRefIds.Size()); + for (size_t anIdx = 0; anIdx < aNbSolidRefs; ++anIdx) + { + theTargetGraph.incStorage().SetRemoved( + aDstRel.SolidRefIds.Value(anIdx), + theSourceGraph.incStorage().IsRemoved(aSrcRel.SolidRefIds.Value(anIdx))); } - (void)aResult.Editor().CompSolids().Add(aSolidNodeIds); - aDeferred.DeferNode(theGraph, aCompSolidId, aCompSolidId); } // Products. - for (BRepGraph_FullProductIterator aProductIt(theGraph); aProductIt.More(); aProductIt.Next()) + for (BRepGraph_FullProductIterator aProductIt(theSourceGraph); aProductIt.More(); + aProductIt.Next()) { - const BRepGraph_ProductId aProductId = aProductIt.CurrentId(); - const BRepGraphInc::ProductDef& aSrcProd = theGraph.incStorage().Product(aProductId); - const BRepGraph_ProductId aNewId = aResult.incStorage().AppendProduct(); - aResult.incStorage().ChangeProduct(aNewId).OccurrenceRefIds = aSrcProd.OccurrenceRefIds; + const BRepGraph_ProductId aProductId = aProductIt.CurrentId(); + const BRepGraph_ProductId aNewId = theTargetGraph.incStorage().AppendProduct(); + Standard_ASSERT_RAISE(aNewId == aProductId, "BRepGraph_Copy: unexpected product id"); + theTargetGraph.incStorage().SetRemoved(aNewId, + theSourceGraph.incStorage().IsRemoved(aProductId)); } // Occurrences. - for (BRepGraph_FullOccurrenceIterator anOccurrenceIt(theGraph); anOccurrenceIt.More(); + for (BRepGraph_FullOccurrenceIterator anOccurrenceIt(theSourceGraph); anOccurrenceIt.More(); anOccurrenceIt.Next()) { const BRepGraph_OccurrenceId anOccurrenceId = anOccurrenceIt.CurrentId(); - const BRepGraphInc::OccurrenceDef& aSrcOcc = theGraph.incStorage().Occurrence(anOccurrenceId); - const BRepGraph_OccurrenceId aNewId = aResult.incStorage().AppendOccurrence(); - aResult.incStorage().ChangeOccurrence(aNewId).ChildDefId = aSrcOcc.ChildDefId; + const BRepGraphInc::OccurrenceDef& aSrcOcc = + theSourceGraph.incStorage().Occurrence(anOccurrenceId); + const BRepGraph_OccurrenceId aNewId = theTargetGraph.incStorage().AppendOccurrence(); + Standard_ASSERT_RAISE(aNewId == anOccurrenceId, "BRepGraph_Copy: unexpected occurrence id"); + theTargetGraph.incStorage().ChangeOccurrence(aNewId).ChildNodeId = aSrcOcc.ChildNodeId; + theTargetGraph.incStorage().SetRemoved(aNewId, + theSourceGraph.incStorage().IsRemoved(anOccurrenceId)); } - // OccurrenceRefs (carry LocalLocation, formerly stored as Placement on OccurrenceDef). - for (BRepGraph_FullOccurrenceRefIterator aRefIt(theGraph); aRefIt.More(); aRefIt.Next()) + // OccurrenceRefs (carry LocalLocation). + for (BRepGraph_FullOccurrenceRefIterator aRefIt(theSourceGraph); aRefIt.More(); aRefIt.Next()) { const BRepGraph_OccurrenceRefId aRefId = aRefIt.CurrentId(); - const BRepGraphInc::OccurrenceRef& aSrcRef = theGraph.incStorage().OccurrenceRef(aRefId); - const BRepGraph_OccurrenceRefId aNewId = aResult.incStorage().AppendOccurrenceRef(); - BRepGraphInc::OccurrenceRef& aNewRef = aResult.incStorage().ChangeOccurrenceRef(aNewId); - aNewRef.ParentId = aSrcRef.ParentId; - aNewRef.IsRemoved = aSrcRef.IsRemoved; - aNewRef.OccurrenceDefId = aSrcRef.OccurrenceDefId; - aNewRef.LocalLocation = aSrcRef.LocalLocation; + const BRepGraphInc::OccurrenceRef& aSrcRef = theSourceGraph.incStorage().OccurrenceRef(aRefId); + const BRepGraph_OccurrenceRefId aNewId = theTargetGraph.incStorage().AppendOccurrenceRef(); + BRepGraphInc::OccurrenceRef& aNewRef = theTargetGraph.incStorage().ChangeOccurrenceRef(aNewId); + theTargetGraph.incStorage().SetRemoved(aNewId, theSourceGraph.incStorage().IsRemoved(aRefId)); + aNewRef.ParentProductId = aSrcRef.ParentProductId; + aNewRef.ChildOccurrenceId = aSrcRef.ChildOccurrenceId; + aNewRef.LocalLocation = aSrcRef.LocalLocation; + } + for (BRepGraph_FullProductIterator aProductIt(theSourceGraph); aProductIt.More(); + aProductIt.Next()) + { + const BRepGraph_ProductId aProductId = aProductIt.CurrentId(); + theTargetGraph.incStorage().SetProductOccurrenceRefs( + aProductId, + theSourceGraph.incStorage().ProductRelations(aProductId).OccurrenceRefIds.ToArray1()); } - // Phase 3: Transfer UIDs (identity mapping - direct vector copy). - auto copyUIDs = [](const NCollection_DynamicArray& theSrc, - NCollection_DynamicArray& theDst) { - NCollection_DynamicArray::Iterator aDstIt(theDst); - NCollection_DynamicArray::Iterator anSrcIt(theSrc); - for (; anSrcIt.More() && aDstIt.More(); anSrcIt.Next(), aDstIt.Next()) - { - if (anSrcIt.Value().IsValid()) - { - aDstIt.ChangeValue() = anSrcIt.Value(); - } - } - }; - - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Vertex), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Vertex)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Edge), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Edge)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Wire), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Wire)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Face), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Face)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Shell), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Shell)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Solid), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Solid)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Compound), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Compound)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::CompSolid), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::CompSolid)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Product), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Product)); - copyUIDs(theGraph.incStorage().UIDs(BRepGraph_NodeId::Kind::Occurrence), - aResult.incStorage().ChangeUIDs(BRepGraph_NodeId::Kind::Occurrence)); - - aResult.data()->myNextUIDCounter.store( - theGraph.data()->myNextUIDCounter.load(std::memory_order_relaxed), - std::memory_order_relaxed); - aResult.data()->myGeneration.store(theGraph.data()->myGeneration.load(std::memory_order_relaxed), - std::memory_order_relaxed); - aResult.data()->myIsDone = true; - - // Build root product set: products not referenced as ChildDefId by any occurrence. + // Copy per-kind next counters from source to target storage so that future + // allocations continue from the correct values. + constexpr BRepGraph_NodeId::Kind aNodeKinds[] = {BRepGraph_NodeId::Kind::Solid, + BRepGraph_NodeId::Kind::Shell, + BRepGraph_NodeId::Kind::Face, + BRepGraph_NodeId::Kind::Wire, + BRepGraph_NodeId::Kind::Edge, + BRepGraph_NodeId::Kind::Vertex, + BRepGraph_NodeId::Kind::Compound, + BRepGraph_NodeId::Kind::CompSolid, + BRepGraph_NodeId::Kind::CoEdge, + BRepGraph_NodeId::Kind::Product, + BRepGraph_NodeId::Kind::Occurrence}; + for (const auto aNodeKind : aNodeKinds) { - NCollection_Map aReferencedProducts; - for (BRepGraph_FullOccurrenceIterator anOccIt(aResult); anOccIt.More(); anOccIt.Next()) + theTargetGraph.incStorage().SetNextNodeUIDCounter( + aNodeKind, + theSourceGraph.incStorage().NextNodeUIDCounter(aNodeKind)); + } + constexpr BRepGraph_RefId::Kind aRefKinds[] = {BRepGraph_RefId::Kind::Shell, + BRepGraph_RefId::Kind::Face, + BRepGraph_RefId::Kind::Wire, + BRepGraph_RefId::Kind::Vertex, + BRepGraph_RefId::Kind::Solid, + BRepGraph_RefId::Kind::Child, + BRepGraph_RefId::Kind::Occurrence}; + for (const auto aRefKind : aRefKinds) + { + theTargetGraph.incStorage().SetNextRefUIDCounter( + aRefKind, + theSourceGraph.incStorage().NextRefUIDCounter(aRefKind)); + } + + theTargetGraph.incStorage().SetGeneration(theSourceGraph.incStorage().Generation()); + theTargetGraph.incStorage().SetGraphGUID(theSourceGraph.incStorage().GraphGUID()); + theTargetGraph.incStorage().RebuildDerivedRelationsPreservingActiveCounts(); + + // Build root product set: products not referenced as ChildNodeId by any occurrence. + { + NCollection_FlatMap aReferencedProducts; + for (BRepGraph_FullOccurrenceIterator anOccIt(theTargetGraph); anOccIt.More(); anOccIt.Next()) { const BRepGraph_OccurrenceId anOccId = anOccIt.CurrentId(); - const BRepGraphInc::OccurrenceDef& anOcc = aResult.incStorage().Occurrence(anOccId); - if (!anOcc.IsRemoved && anOcc.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) + const BRepGraphInc::OccurrenceDef& anOcc = theTargetGraph.incStorage().Occurrence(anOccId); + if (!theTargetGraph.incStorage().IsRemoved(anOccId) + && anOcc.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) { const BRepGraph_ProductId aChildProductId = - BRepGraph_ProductId::FromNodeId(anOcc.ChildDefId); - if (aChildProductId.IsValidIn(aResult.Topo().Products())) + BRepGraph_ProductId::FromNodeId(anOcc.ChildNodeId); + if (aChildProductId.IsValidIn(theTargetGraph.Topo().Products())) { aReferencedProducts.Add(aChildProductId); } } } - for (BRepGraph_FullProductIterator aProdIt(aResult); aProdIt.More(); aProdIt.Next()) + for (BRepGraph_FullProductIterator aProdIt(theTargetGraph); aProdIt.More(); aProdIt.Next()) { - const BRepGraph_ProductId aProdId = aProdIt.CurrentId(); - const BRepGraphInc::ProductDef& aProd = aResult.incStorage().Product(aProdId); - if (!aProd.IsRemoved && !aReferencedProducts.Contains(aProdId)) + const BRepGraph_ProductId aProdId = aProdIt.CurrentId(); + if (!theTargetGraph.incStorage().IsRemoved(aProdId) && !aReferencedProducts.Contains(aProdId)) { - aResult.data()->myRootProductIds.Append(aProdId); + theTargetGraph.incStorage().ChangeRootProductIds().Append(aProdId); } } } - // Pre-allocate transient cache for lock-free parallel access on the copied graph. - reserveTransientCache(aResult); + theTargetGraph.incStorage().MarkUIDReverseIndexesDirty(); - // Drain deferred cache transfers AFTER all mutations: Set captures the final SubtreeGen, - // so subsequent Get calls match. - aDeferred.Drain(theGraph, aResult); + NCollection_FlatDataMap anItemRemap; + bindIdentityItems(theSourceGraph.incStorage(), theTargetGraph.incStorage(), anItemRemap); + theSourceGraph.LayerRegistry().CopyLayersTo(theTargetGraph, + anItemRemap, + BRepGraph_CopyRemap::Mode::Copy); + if (theCachePolicy == CachePolicy::CopyFresh) + { + theSourceGraph.CacheRegistry().CopyFreshCachesTo(theTargetGraph, + anItemRemap, + BRepGraph_CopyRemap::Mode::Copy); + } - return aResult; + return true; } //================================================================================================= -BRepGraph BRepGraph_Copy::CopyNode(const BRepGraph& theGraph, - const BRepGraph_NodeId theNodeId, - const bool theCopyGeom, - const bool theCopyMesh, - const bool theReserveCache) +BRepGraph_NodeId BRepGraph_Copy::CopyNode(const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + const BRepGraph_NodeId theNodeId, + GeomPolicy theGeomPolicy, + MeshPolicy theMeshPolicy, + CachePolicy theCachePolicy) { - if (!theGraph.IsDone()) + if (theSourceGraph.IsEmpty()) { - return BRepGraph(); + return BRepGraph_NodeId(); } - GraphCopyContext ctx(theGraph, theCopyGeom, theCopyMesh, theReserveCache); - ctx.SrcStorage = &theGraph.incStorage(); - ctx.DstStorage = &ctx.Result.incStorage(); - ctx.DstMesh = &ctx.Result.meshCache(); - ctx.DstData = ctx.Result.data(); + const bool isSelfCopy = (&theSourceGraph == &theTargetGraph); - ensureNode(ctx, theNodeId); + GraphCopyContext theCtx(theSourceGraph, theTargetGraph, isSelfCopy, theGeomPolicy, theMeshPolicy); + theCtx.SrcStorage = &theSourceGraph.incStorage(); + theCtx.DstStorage = &theTargetGraph.incStorage(); + theCtx.DstData = theTargetGraph.data(); + theCtx.ItemRemap.Reserve(itemCapacityUpperBound(theSourceGraph.incStorage())); - // Post-pass: remap OccurrenceRef.ParentId for any assembly entities that were copied. - // ensureProduct sets ParentId inline, but this guard handles other edge cases. - if (!ctx.OccurrenceRefs.IsEmpty()) - { - for (BRepGraph_FullOccurrenceRefIterator aRefIt(theGraph); aRefIt.More(); aRefIt.Next()) - { - const BRepGraph_OccurrenceRefId aSrcRefId = aRefIt.CurrentId(); - const BRepGraph_OccurrenceRefId* aDstRefPtr = ctx.OccurrenceRefs.Seek(aSrcRefId); - if (aDstRefPtr == nullptr) - { - continue; - } - const BRepGraphInc::OccurrenceRef& aSrcRef = ctx.SrcStorage->OccurrenceRef(aSrcRefId); - if (!aSrcRef.ParentId.IsValid()) - { - continue; - } - const BRepGraph_NodeId aMapped = mappedNode(ctx, aSrcRef.ParentId); - if (aMapped.IsValid()) - { - ctx.DstStorage->ChangeOccurrenceRef(*aDstRefPtr).ParentId = aMapped; - } - } - } + ensureNode(theCtx, theNodeId); // Build root product list when product entities were copied. - if (!ctx.Products.IsEmpty()) + if (!theCtx.Products.IsEmpty()) { - NCollection_Map aReferencedProducts; - for (BRepGraph_FullOccurrenceIterator anOccIt(ctx.Result); anOccIt.More(); anOccIt.Next()) + NCollection_FlatMap aReferencedProducts; + for (BRepGraph_FullOccurrenceIterator anOccIt(theTargetGraph); anOccIt.More(); anOccIt.Next()) { const BRepGraph_OccurrenceId anOccId = anOccIt.CurrentId(); - const BRepGraphInc::OccurrenceDef& anOcc = ctx.DstStorage->Occurrence(anOccId); - if (!anOcc.IsRemoved && anOcc.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) + const BRepGraphInc::OccurrenceDef& anOcc = theCtx.DstStorage->Occurrence(anOccId); + if (!theCtx.DstStorage->IsRemoved(anOccId) + && anOcc.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) { - const BRepGraph_ProductId aChildProdId = BRepGraph_ProductId::FromNodeId(anOcc.ChildDefId); - if (aChildProdId.IsValidIn(ctx.Result.Topo().Products())) + const BRepGraph_ProductId aChildProdId = BRepGraph_ProductId::FromNodeId(anOcc.ChildNodeId); + if (aChildProdId.IsValidIn(theTargetGraph.Topo().Products())) { aReferencedProducts.Add(aChildProdId); } } } - for (BRepGraph_FullProductIterator aProdIt(ctx.Result); aProdIt.More(); aProdIt.Next()) + for (BRepGraph_FullProductIterator aProdIt(theTargetGraph); aProdIt.More(); aProdIt.Next()) { - const BRepGraph_ProductId aProdId = aProdIt.CurrentId(); - const BRepGraphInc::ProductDef& aProd = ctx.DstStorage->Product(aProdId); - if (!aProd.IsRemoved && !aReferencedProducts.Contains(aProdId)) + const BRepGraph_ProductId aProdId = aProdIt.CurrentId(); + if (!theCtx.DstStorage->IsRemoved(aProdId) && !aReferencedProducts.Contains(aProdId)) { - ctx.DstData->myRootProductIds.Append(aProdId); + theCtx.DstStorage->ChangeRootProductIds().Append(aProdId); } } } - ctx.DstData->myIsDone = true; - if (ctx.ReserveCache) + theCtx.DstStorage->MarkUIDReverseIndexesDirty(); + + theCtx.DstStorage->RebuildDerivedRelationsPreservingActiveCounts(); + theSourceGraph.LayerRegistry().CopyLayersTo(theTargetGraph, + theCtx.ItemRemap, + BRepGraph_CopyRemap::Mode::Copy); + if (theCachePolicy == CachePolicy::CopyFresh && !isSelfCopy) { - reserveTransientCache(ctx.Result); + theSourceGraph.CacheRegistry().CopyFreshCachesTo(theTargetGraph, + theCtx.ItemRemap, + BRepGraph_CopyRemap::Mode::Copy); } - ctx.Deferred.Drain(theGraph, ctx.Result); - return std::move(ctx.Result); + + return mappedNode(theCtx, theNodeId); } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx index edf8d5e2de..27ed54cbb8 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Copy.hxx @@ -16,8 +16,7 @@ #include #include -#include - +#include #include //! @brief Graph-to-graph deep copy. @@ -25,19 +24,23 @@ //! Produces a new BRepGraph from an existing one in a single bottom-up pass, //! avoiding the 5-7 traversals of BRepTools_Modifier used by BRepBuilderAPI_Copy. //! -//! Two modes: -//! - theCopyGeom = true (deep): geometry handles are cloned, result is fully independent. -//! - theCopyGeom = false (light): geometry handles are shared, only topology is duplicated. +//! Two copy modes: +//! - External: source and target are different graphs. Target receives the copied data. +//! - Self-copy: source and target are the same graph. The specified sub-graph is +//! duplicated with new entity IDs; shared dependencies (geometry, vertices referenced +//! from outside the sub-graph) are preserved. //! -//! @note Unlike in-place mutation algorithms (Sewing, Deduplicate) which return a -//! Result struct with diagnostics, Copy and Transform return a BRepGraph directly -//! because they produce new graphs. Check IsDone() on the returned graph for success. +//! Geometry and mesh policies are controlled by the GeomPolicy and MeshPolicy enums. +//! +//! @note Check the return value for success: Perform returns bool, +//! CopyNode returns the mapped root NodeId (invalid on failure). //! //! ## Typical usage //! @code //! BRepGraph aGraph; -//! BRepGraph_Builder::Add(aGraph, myShape); -//! BRepGraph aCopy = BRepGraph_Copy::Perform(aGraph); +//! aGraph.Shapes().Add(myShape); +//! BRepGraph aCopy; +//! BRepGraph_Copy::Perform(aGraph, aCopy); //! TopoDS_Shape aShape = aCopy.Shapes().Shape(); //! @endcode class BRepGraph_Copy @@ -45,33 +48,78 @@ class BRepGraph_Copy public: DEFINE_STANDARD_ALLOC - //! Copy the entire graph. - //! @param[in] theGraph a pre-built BRepGraph (must have IsDone() == true) - //! @param[in] theCopyGeom if true (default), geometry handles are deep-copied; - //! if false, geometry is shared (only topology is duplicated) - //! @return a new BRepGraph with IsDone() == true on success, - //! or an empty graph with IsDone() == false on failure - [[nodiscard]] Standard_EXPORT static BRepGraph Perform(const BRepGraph& theGraph, - const bool theCopyGeom = true); + //! Policy for handling geometry handles (Geom_Curve, Geom_Surface, Geom2d_Curve). + enum class GeomPolicy + { + Copy, //!< Deep-clone geometry handles; result is fully independent + Share, //!< Reuse source geometry handles; only topology is duplicated + Drop //!< Pure topology: edges carry no curves, faces carry no surfaces + }; + + //! Policy for handling mesh data (Poly_Triangulation, Poly_Polygon3D, + //! Poly_PolygonOnTriangulation). + enum class MeshPolicy + { + Copy, //!< Deep-clone mesh data; independent result + Share, //!< Reuse source mesh handle references; no cloning + Drop //!< Discard all mesh data on copied entities + }; + + //! Policy for handling transient runtime cache services. + enum class CachePolicy + { + Drop, //!< Do not copy runtime cache services or entries. + CopyFresh //!< Copy fresh, remappable runtime cache entries. + }; + + //! Copy the entire source graph into the target graph. + //! + //! Self-copy (theSourceGraph == theTargetGraph): + //! Identity no-op, returns true immediately. + //! + //! External copy to empty target (theTargetGraph.IsEmpty()): + //! Uses identity-mapped fast path (old index == new index). + //! + //! External copy to non-empty target: + //! Uses explicit mapping; IDs in theTargetGraph will differ from theSourceGraph. + //! Entities from theSourceGraph are appended to theTargetGraph. + //! + //! @param[in] theSourceGraph a pre-built BRepGraph (must not be empty) + //! @param[in,out] theTargetGraph destination graph (may already contain data) + //! @param[in] theGeomPolicy geometry handle policy (default: Copy) + //! @param[in] theMeshPolicy mesh data policy (default: Copy) + //! @return true on success, false on failure (empty source) + Standard_EXPORT static bool Perform(const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + GeomPolicy theGeomPolicy = GeomPolicy::Copy, + MeshPolicy theMeshPolicy = MeshPolicy::Copy, + CachePolicy theCachePolicy = CachePolicy::Drop); //! Copy a single node sub-graph of any kind (Face, Shell, Solid, Wire, Edge, Vertex, etc.). - //! The new graph contains only the specified node and all entities it references. - //! @param[in] theGraph a pre-built BRepGraph - //! @param[in] theNodeId node identifier (any kind) - //! @param[in] theCopyGeom if true, geometry handles are deep-copied - //! @param[in] theCopyMesh if true, cached mesh entries are propagated to the result; - //! if false, mesh references are dropped on copied faces - //! @param[in] theReserveCache if true, pre-allocates transient cache - //! @return a new BRepGraph containing only the specified sub-graph - [[nodiscard]] Standard_EXPORT static BRepGraph CopyNode(const BRepGraph& theGraph, - const BRepGraph_NodeId theNodeId, - const bool theCopyGeom = true, - const bool theCopyMesh = true, - const bool theReserveCache = false); - -private: - //! Pre-allocate transient cache for lock-free parallel access. - static void reserveTransientCache(BRepGraph& theGraph); + //! The target graph receives the specified node and all entities it references. + //! + //! External copy (theSourceGraph != theTargetGraph): + //! New entities are appended to theTargetGraph. Entities already present + //! in theTargetGraph are reused (not duplicated). + //! + //! Self-copy (theSourceGraph == theTargetGraph): + //! The specified sub-graph is duplicated with new entity IDs within the same graph. + //! Shared dependencies (vertices, edges referenced from outside the sub-graph) + //! are preserved as-is. + //! + //! @param[in] theSourceGraph a pre-built BRepGraph + //! @param[in,out] theTargetGraph destination graph (may already contain data) + //! @param[in] theNodeId node identifier (any kind) + //! @param[in] theGeomPolicy geometry handle policy (default: Copy) + //! @param[in] theMeshPolicy mesh data policy (default: Copy) + //! @return the mapped root NodeId in theTargetGraph, or invalid NodeId on failure + [[nodiscard]] Standard_EXPORT static BRepGraph_NodeId CopyNode( + const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + const BRepGraph_NodeId theNodeId, + GeomPolicy theGeomPolicy = GeomPolicy::Copy, + MeshPolicy theMeshPolicy = MeshPolicy::Copy, + CachePolicy theCachePolicy = CachePolicy::Drop); BRepGraph_Copy() = delete; }; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CopyRemap.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CopyRemap.cxx new file mode 100644 index 0000000000..1288d057b9 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CopyRemap.cxx @@ -0,0 +1,76 @@ +// 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 + +#include +#include + +//================================================================================================= + +BRepGraph_CopyRemap::BRepGraph_CopyRemap(const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + const ItemMap& theItemRemap, + const Mode theMode) noexcept + : mySourceGraph(&theSourceGraph), + myTargetGraph(&theTargetGraph), + myItemRemap(&theItemRemap), + myMode(theMode) +{ +} + +//================================================================================================= + +const BRepGraph_ItemId* BRepGraph_CopyRemap::TargetItem(const BRepGraph_ItemId theSourceItem) const +{ + return theSourceItem.IsValid() ? myItemRemap->Seek(theSourceItem) : nullptr; +} + +//================================================================================================= + +BRepGraph_ItemId BRepGraph_CopyRemap::TargetItemOrInvalid( + const BRepGraph_ItemId theSourceItem) const +{ + const BRepGraph_ItemId* aTarget = TargetItem(theSourceItem); + return aTarget != nullptr ? *aTarget : BRepGraph_ItemId(); +} + +//================================================================================================= + +bool BRepGraph_CopyRemap::HasTargetItem(const BRepGraph_ItemId theSourceItem) const +{ + const BRepGraph_ItemId* aTarget = TargetItem(theSourceItem); + return aTarget != nullptr && aTarget->IsValid(); +} + +//================================================================================================= + +BRepGraph_ItemUID BRepGraph_CopyRemap::SourceUID(const BRepGraph_ItemId theSourceItem) const +{ + return mySourceGraph->UIDs().Of(theSourceItem); +} + +//================================================================================================= + +BRepGraph_ItemUID BRepGraph_CopyRemap::TargetUID(const BRepGraph_ItemId theTargetItem) const +{ + return myTargetGraph->UIDs().Of(theTargetItem); +} + +//================================================================================================= + +BRepGraph_ItemUID BRepGraph_CopyRemap::TargetUIDFromSource( + const BRepGraph_ItemId theSourceItem) const +{ + return TargetUID(TargetItemOrInvalid(theSourceItem)); +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CopyRemap.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CopyRemap.hxx new file mode 100644 index 0000000000..4a0a68e9c6 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_CopyRemap.hxx @@ -0,0 +1,98 @@ +// 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. + +#ifndef _BRepGraph_CopyRemap_HeaderFile +#define _BRepGraph_CopyRemap_HeaderFile + +#include +#include +#include +#include + +#include + +class BRepGraph; + +//! Immutable context passed to layer copy callbacks. +//! +//! The structural copy algorithm owns remap construction. Layers receive this +//! context and decide how to copy their own representation without exposing layer +//! details back to BRepGraph_Copy. +class BRepGraph_CopyRemap +{ +public: + DEFINE_STANDARD_ALLOC + + //! Distinguishes copy vs. compact migration semantics. + enum class Mode : std::uint8_t + { + Copy = 0, //!< Full graph copy: source and target are distinct graphs. + Compact = 1 //!< In-place compaction: layers migrate into the same (rebuilt) graph. + }; + + using ItemMap = NCollection_FlatDataMap; + + BRepGraph_CopyRemap(const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + const ItemMap& theItemRemap, + const Mode theMode) noexcept; + + //! Migration mode of this context. + [[nodiscard]] Mode CopyMode() const noexcept { return myMode; } + + //! True if this is a compaction migration (not a full copy). + [[nodiscard]] bool IsCompact() const noexcept { return myMode == Mode::Compact; } + + //! Source graph the copied layer is attached to. + [[nodiscard]] const BRepGraph& SourceGraph() const noexcept { return *mySourceGraph; } + + //! Target graph whose structural contents have already been copied. + [[nodiscard]] BRepGraph& TargetGraph() const noexcept { return *myTargetGraph; } + + //! Target graph as const. + [[nodiscard]] const BRepGraph& TargetGraphConst() const noexcept { return *myTargetGraph; } + + //! Source item id -> target item id map for copied definitions, refs, and reps. + [[nodiscard]] const ItemMap& Items() const noexcept { return *myItemRemap; } + + //! Return the target item for a source item, or null if the source item was not copied. + [[nodiscard]] Standard_EXPORT const BRepGraph_ItemId* TargetItem( + const BRepGraph_ItemId theSourceItem) const; + + //! Return the target item for a source item, or an invalid item id. + [[nodiscard]] Standard_EXPORT BRepGraph_ItemId + TargetItemOrInvalid(const BRepGraph_ItemId theSourceItem) const; + + //! Return true if the source item has a valid copied target item. + [[nodiscard]] Standard_EXPORT bool HasTargetItem(const BRepGraph_ItemId theSourceItem) const; + + //! Return source UID for a source item. + [[nodiscard]] Standard_EXPORT BRepGraph_ItemUID + SourceUID(const BRepGraph_ItemId theSourceItem) const; + + //! Return target UID for a target item. + [[nodiscard]] Standard_EXPORT BRepGraph_ItemUID + TargetUID(const BRepGraph_ItemId theTargetItem) const; + + //! Return target UID for a source item by source->target remap. + [[nodiscard]] Standard_EXPORT BRepGraph_ItemUID + TargetUIDFromSource(const BRepGraph_ItemId theSourceItem) const; + +private: + const BRepGraph* mySourceGraph = nullptr; + BRepGraph* myTargetGraph = nullptr; + const ItemMap* myItemRemap = nullptr; + Mode myMode = Mode::Copy; +}; + +#endif // _BRepGraph_CopyRemap_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx index 162a448441..9c21a72494 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Data.hxx @@ -14,36 +14,19 @@ #ifndef _BRepGraph_Data_HeaderFile #define _BRepGraph_Data_HeaderFile -#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 + +class BRepGraph; //! @brief Internal storage for BRepGraph (PIMPL). //! @@ -51,108 +34,23 @@ //! Access via myIncStorage.Edges, myIncStorage.Faces, etc. struct BRepGraph_Data { - occ::handle myAllocator; - //! Incidence-table storage - sole source of truth for all topology data, - //! original shapes, TShape->NodeId mapping, and UIDs. + //! original shapes, TShape->NodeId mapping, UIDs, and UID reverse indexes. BRepGraphInc_Storage myIncStorage; - //! UID system. - std::atomic myNextUIDCounter{ - 1}; //!< Starts at 1; counter=0 is BRepGraph_UID invalid sentinel. - std::atomic myGeneration{0}; - Standard_GUID myGraphGUID; //!< Random graph identity, generated at BRepGraph_Builder::Add(). + //! Registered graph layers. + BRepGraph_LayerRegistry myLayerRegistry; - //! History subsystem. - BRepGraph_History myHistoryLog; + //! Registered transient cache services. + BRepGraph_CacheRegistry myCacheRegistry; - bool myIsDone = false; - - //! Root product identifiers: products not referenced by any active occurrence. - //! Maintained incrementally by Editor/EditorView mutations. - NCollection_DynamicArray myRootProductIds; - - //! When true, markModified() only increments OwnGen + SubtreeGen and appends to - //! myDeferredModified - no mutex acquisition and no upward propagation. - std::atomic myDeferredMode{false}; - - //! Propagation wave counter. Incremented at the start of each - //! markModified() / markRefModified() call. markParentModified() - //! compares entity.LastPropWave against this to skip already-visited - //! parents in the same propagation wave (O(1) re-visit guard). - std::atomic myPropagationWave{0}; - - //! Recursion depth of EditorView::GenOps::RemoveSubgraph. Outermost call (depth==0) - //! triggers a single reverse-index rebuild after cascade so individual cascade-prune - //! steps avoid maintaining per-kind unbinds for every removed node. - uint32_t myRemoveSubgraphDepth = 0; - - //! NodeIds accumulated during deferred mode. Processed by EndDeferredInvalidation(). - NCollection_DynamicArray myDeferredModified; - - //! RefIds accumulated during deferred mode. Processed by EndDeferredInvalidation(). - NCollection_DynamicArray myDeferredRefModified; - - //! Gen-validated shape cache entry. - struct CachedShape - { - TopoDS_Shape Shape; - uint32_t StoredSubtreeGen = 0; - }; - - //! Thread-safe cache of reconstructed shapes with SubtreeGen validation. - mutable NCollection_DataMap myCurrentShapes; - mutable std::shared_mutex myCurrentShapesMutex; - - //! Lazy reverse lookup index for entity UIDs. - mutable NCollection_DataMap myUIDToNodeId; - mutable std::shared_mutex myUIDToNodeIdMutex; - mutable uint32_t myUIDToNodeIdGeneration = 0; - mutable bool myUIDToNodeIdDirty = true; - - //! Lazy reverse lookup index for reference UIDs. - mutable NCollection_DataMap myRefUIDToRefId; - mutable std::shared_mutex myRefUIDToRefIdMutex; - mutable uint32_t myRefUIDToRefIdGeneration = 0; - mutable bool myRefUIDToRefIdDirty = true; - - //! Cached mesh data storage (algorithm-derived, non-mutating). - //! Holds triangulation/polygon rep references written by BRepGraphMesh. - //! Does NOT trigger markModified() or mutation tracking. - BRepGraph_MeshCacheStorage myMeshCache; - - using ReconstructCache = NCollection_DataMap; - - //! Cached view objects (pointers set to owning BRepGraph in its constructor). + //! Stable top-level views. Nested views store graph-data context only. BRepGraph::TopoView myTopoView{nullptr}; BRepGraph::UIDsView myUIDsView{nullptr}; - BRepGraph::CacheView myCacheView{nullptr}; BRepGraph::RefsView myRefsView{nullptr}; BRepGraph::ShapesView myShapesView{nullptr}; BRepGraph::EditorView myEditorView{nullptr}; BRepGraph::MeshView myMeshView{nullptr}; - - BRepGraph_Data() - : myAllocator(new NCollection_IncAllocator), - myIncStorage(myAllocator), - myCurrentShapes(1, myAllocator), - myUIDToNodeId(1, myAllocator), - myRefUIDToRefId(1, myAllocator) - { - myHistoryLog.SetAllocator(myAllocator); - } - - explicit BRepGraph_Data(const occ::handle& theAlloc) - : myAllocator(!theAlloc.IsNull() - ? theAlloc - : occ::handle(new NCollection_IncAllocator)), - myIncStorage(myAllocator), - myCurrentShapes(1, myAllocator), - myUIDToNodeId(1, myAllocator), - myRefUIDToRefId(1, myAllocator) - { - myHistoryLog.SetAllocator(myAllocator); - } }; #endif // _BRepGraph_Data_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx index 6994157224..b608ad828a 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.cxx @@ -12,32 +12,109 @@ // 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 +namespace +{ +//! Redirect all OccurrenceDef.ChildNodeId entries that still point to theOldNodeId +//! to theNewNodeId. Snapshots the occurrence ref list before iterating because +//! SetChildNodeId modifies the underlying OccurrenceRefsOfNode vector. +void redirectOccurrenceChildren(BRepGraph& theGraph, + const BRepGraph_NodeId theOldNodeId, + const BRepGraph_NodeId theNewNodeId) +{ + const NCollection_LinearVector& aOccRefs = + theGraph.Topo().Gen().OccurrenceRefIds(theOldNodeId); + if (aOccRefs.IsEmpty()) + { + return; + } + // Snapshot - SetChildNodeId modifies the underlying vector. + NCollection_LinearVector aSnapshot(aOccRefs.Size()); + for (const BRepGraph_OccurrenceRefId& aRefId : aOccRefs) + { + aSnapshot.Append(aRefId); + } + for (const BRepGraph_OccurrenceRefId& aOccRefId : aSnapshot) + { + if (!aOccRefId.IsValid() || theGraph.Refs().Gen().IsRemoved(aOccRefId)) + { + continue; + } + const BRepGraph_OccurrenceId aOccId = + theGraph.Refs().Occurrences().Entry(aOccRefId).ChildOccurrenceId; + if (!aOccId.IsValid() || aOccId.IsRemoved(theGraph)) + { + continue; + } + if (theGraph.Topo().Occurrences().Definition(aOccId).ChildNodeId == theOldNodeId) + { + theGraph.Editor().Occurrences().SetChildNodeId(aOccId, theNewNodeId); + } + } +} + +} // namespace + +//================================================================================================= + +void BRepGraph_Deduplicate::CanonicalizeWireOrders(BRepGraph& theGraph, Result& theResult) +{ + BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + for (BRepGraph_WireId aWireId(0); aWireId.IsValid(aStorage.NbWires()); ++aWireId) + { + if (aStorage.IsRemoved(aWireId)) + { + continue; + } + + const BRepGraphInc_Storage::WireCoEdgeOrderStatus aStatus = + aStorage.CanonicalizeWireCoEdgeOrderStatus(aWireId); + switch (aStatus) + { + case BRepGraphInc_Storage::WireCoEdgeOrderStatus::Reordered: + ++theResult.NbReorderedWires; + theGraph.markModified(aWireId); + break; + case BRepGraphInc_Storage::WireCoEdgeOrderStatus::ToleranceOrdered: + ++theResult.NbToleranceOrderedWires; + theGraph.markModified(aWireId); + break; + case BRepGraphInc_Storage::WireCoEdgeOrderStatus::Partial: + ++theResult.NbPartialOrderedWires; + theGraph.markModified(aWireId); + break; + default: + break; + } + } +} + //================================================================================================= BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theGraph) @@ -51,179 +128,187 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG const Options& theOptions) { Result aResult; - if (!theGraph.IsDone()) + if (theGraph.IsEmpty()) { return aResult; } BRepGraph_DeferredScope aDeferredScope(theGraph); - const bool wasHistoryEnabled = theGraph.History().IsEnabled(); - theGraph.History().SetEnabled(theOptions.HistoryMode); + BRepGraph_LayerHistory& aHistory = *theGraph.LayerRegistry().Ensure(); + const bool wasHistoryEnabled = aHistory.IsEnabled(); + aHistory.SetEnabled(theOptions.HistoryMode); GeomHash_SurfaceHasher aSurfHasher(theOptions.CompTolerance, theOptions.HashTolerance); GeomHash_CurveHasher aCurveHasher(theOptions.CompTolerance, theOptions.HashTolerance); const occ::handle aTmpAlloc = new NCollection_IncAllocator(); + using ActiveVertexList = NCollection_LinearVector>; + using EdgeIdList = NCollection_LinearVector; + using WireIdList = NCollection_LinearVector; + using FaceIdList = NCollection_LinearVector; - // Deduplicate surfaces by comparing Handle pointers on FaceDefs. - // Map: surface handle -> canonical face index (first face that owns it). - NCollection_DataMap, BRepGraph_FaceId, GeomHash_SurfaceHasher> - aSurfToCanonicalFace(aSurfHasher, std::max(1, theGraph.Topo().Faces().Nb() * 2), aTmpAlloc); - // Map: face id -> canonical face id (for faces whose surface should be replaced). - NCollection_DataMap aSurfRewriteMap( - std::max(1, theGraph.Topo().Faces().Nb()), - aTmpAlloc); - - for (BRepGraph_FullFaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - if (theGraph.Topo().Faces().Definition(aFaceId).IsRemoved) - { - continue; - } - if (!BRepGraph_Tool::Face::HasSurface(theGraph, aFaceId)) - { - continue; - } + // Deduplicate surfaces by comparing Handle pointers on FaceDefs. + // Map: surface handle -> canonical face index (first face that owns it). + NCollection_DataMap, BRepGraph_FaceId, GeomHash_SurfaceHasher> + aSurfToCanonicalFace( + aSurfHasher, + std::max(1, static_cast(theGraph.Topo().Faces().Nb()) * 2), + aTmpAlloc); + // Map: face id -> canonical face id (for faces whose surface should be replaced). + NCollection_DataMap aSurfRewriteMap( + std::max(1, theGraph.Topo().Faces().Nb()), + aTmpAlloc); - const occ::handle& aFaceSurf = BRepGraph_Tool::Face::Surface(theGraph, aFaceId); - const BRepGraph_FaceId* aCanonFaceId = aSurfToCanonicalFace.Seek(aFaceSurf); - if (aCanonFaceId == nullptr) + for (BRepGraph_FullFaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) { - aSurfToCanonicalFace.Bind(aFaceSurf, aFaceId); - } - else if (*aCanonFaceId != aFaceId) - { - aSurfRewriteMap.Bind(aFaceId, *aCanonFaceId); - } - } - - // Deduplicate curves by comparing Handle pointers on EdgeDefs. - NCollection_DataMap, BRepGraph_EdgeId, GeomHash_CurveHasher> - aCurveToCanonicalEdge(aCurveHasher, std::max(1, theGraph.Topo().Edges().Nb() * 2), aTmpAlloc); - NCollection_DataMap aCurveRewriteMap( - std::max(1, theGraph.Topo().Edges().Nb()), - aTmpAlloc); - - for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - if (theGraph.Topo().Edges().Definition(anEdgeId).IsRemoved) - { - continue; - } - if (!BRepGraph_Tool::Edge::HasCurve(theGraph, anEdgeId)) - { - continue; - } - - const occ::handle& anEdgeCurve = BRepGraph_Tool::Edge::Curve(theGraph, anEdgeId); - const BRepGraph_EdgeId* aCanonEdgeId = aCurveToCanonicalEdge.Seek(anEdgeCurve); - if (aCanonEdgeId == nullptr) - { - aCurveToCanonicalEdge.Bind(anEdgeCurve, anEdgeId); - } - else if (*aCanonEdgeId != anEdgeId) - { - aCurveRewriteMap.Bind(anEdgeId, *aCanonEdgeId); - } - } - - aResult.NbCanonicalSurfaces = theGraph.Topo().Faces().Nb() - aSurfRewriteMap.Length(); - aResult.NbCanonicalCurves = theGraph.Topo().Edges().Nb() - aCurveRewriteMap.Length(); - - if (theOptions.AnalyzeOnly && !theOptions.MergeEntitiesWhenSafe) - { - theGraph.History().SetEnabled(wasHistoryEnabled); - return aResult; - } - - if (!theOptions.AnalyzeOnly) - { - // Rewrite face surfaces: replace duplicate surface handles with canonical ones. - for (NCollection_DataMap::Iterator anIt(aSurfRewriteMap); - anIt.More(); - anIt.Next()) - { - const BRepGraph_FaceId aFaceId = anIt.Key(); - const BRepGraph_FaceId aCanonFaceId = anIt.Value(); - const BRepGraph_SurfaceRepId aCanonSurfRepId = - theGraph.Topo().Faces().Definition(aCanonFaceId).SurfaceRepId; - const BRepGraph_SurfaceRepId aCurrentSurfRepId = - theGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId; - - // Skip if already canonical (idempotency: avoid re-recording same rewrite). - if (aCurrentSurfRepId == aCanonSurfRepId) + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + if (aFaceId.IsRemoved(theGraph)) + { + continue; + } + if (!BRepGraph_Tool::Face::HasSurface(theGraph, aFaceId)) { continue; } - BRepGraph_MutGuard aFaceDef = theGraph.Editor().Faces().Mut(aFaceId); - theGraph.Editor().Faces().SetSurfaceRepId(aFaceDef, aCanonSurfRepId); - ++aResult.NbSurfaceRewrites; - aResult.AffectedFaces.Append(aFaceId); - - NCollection_DynamicArray aRepl; - aRepl.Append(aCanonFaceId); - theGraph.History().Record(TCollection_AsciiString("Dedup:CanonicalizeSurface"), - aFaceId, - aRepl); - ++aResult.NbHistoryRecords; + const occ::handle& aFaceSurf = BRepGraph_Tool::Face::Surface(theGraph, aFaceId); + const BRepGraph_FaceId* aCanonFaceId = aSurfToCanonicalFace.Seek(aFaceSurf); + if (aCanonFaceId == nullptr) + { + aSurfToCanonicalFace.Bind(aFaceSurf, aFaceId); + } + else if (*aCanonFaceId != aFaceId) + { + aSurfRewriteMap.Bind(aFaceId, *aCanonFaceId); + } } - // Rewrite edge curves: replace duplicate curve handles with canonical ones. - for (NCollection_DataMap::Iterator anIt(aCurveRewriteMap); - anIt.More(); - anIt.Next()) - { - const BRepGraph_EdgeId anEdgeId = anIt.Key(); - const BRepGraph_EdgeId aCanonEdgeId = anIt.Value(); - const BRepGraph_Curve3DRepId aCanonCurveRepId = - theGraph.Topo().Edges().Definition(aCanonEdgeId).Curve3DRepId; - const BRepGraph_Curve3DRepId aCurrentCurveRepId = - theGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId; + // Deduplicate curves by comparing Handle pointers on EdgeDefs. + NCollection_DataMap, BRepGraph_EdgeId, GeomHash_CurveHasher> + aCurveToCanonicalEdge( + aCurveHasher, + std::max(1, static_cast(theGraph.Topo().Edges().Nb()) * 2), + aTmpAlloc); + NCollection_DataMap aCurveRewriteMap( + std::max(1, theGraph.Topo().Edges().Nb()), + aTmpAlloc); - // Skip if already canonical (idempotency: avoid re-recording same rewrite). - if (aCurrentCurveRepId == aCanonCurveRepId) + for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) + { + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + if (anEdgeId.IsRemoved(theGraph)) + { + continue; + } + if (!BRepGraph_Tool::Edge::HasCurve(theGraph, anEdgeId)) { continue; } - BRepGraph_MutGuard anEdgeDef = theGraph.Editor().Edges().Mut(anEdgeId); - theGraph.Editor().Edges().SetCurve3DRepId(anEdgeDef, aCanonCurveRepId); - ++aResult.NbCurveRewrites; - aResult.AffectedEdges.Append(anEdgeId); - - NCollection_DynamicArray aRepl; - aRepl.Append(aCanonEdgeId); - theGraph.History().Record(TCollection_AsciiString("Dedup:CanonicalizeCurve"), - anEdgeId, - aRepl); - ++aResult.NbHistoryRecords; + const occ::handle& anEdgeCurve = BRepGraph_Tool::Edge::Curve(theGraph, anEdgeId); + const BRepGraph_EdgeId* aCanonEdgeId = aCurveToCanonicalEdge.Seek(anEdgeCurve); + if (aCanonEdgeId == nullptr) + { + aCurveToCanonicalEdge.Bind(anEdgeCurve, anEdgeId); + } + else if (*aCanonEdgeId != anEdgeId) + { + aCurveRewriteMap.Bind(anEdgeId, *aCanonEdgeId); + } } - } // end if (!theOptions.AnalyzeOnly) for geometry rewrites + aResult.NbCanonicalSurfaces = + theGraph.Topo().Faces().Nb() - static_cast(aSurfRewriteMap.Size()); + aResult.NbCanonicalCurves = + theGraph.Topo().Edges().Nb() - static_cast(aCurveRewriteMap.Size()); + + if (theOptions.AnalyzeOnly && !theOptions.MergeEntitiesWhenSafe) + { + aHistory.SetEnabled(wasHistoryEnabled); + return aResult; + } + + if (!theOptions.AnalyzeOnly) + { + // Rewrite face surfaces: replace duplicate surface handles with canonical ones. + NCollection_LinearVector aSurfRepl(4); + for (NCollection_DataMap::Iterator anIt(aSurfRewriteMap); + anIt.More(); + anIt.Next()) + { + const BRepGraph_FaceId aFaceId = anIt.Key(); + const BRepGraph_FaceId aCanonFaceId = anIt.Value(); + const occ::handle& aCanonSurf = + BRepGraph_Tool::Face::Surface(theGraph, aCanonFaceId); + + BRepGraph_MutGuard aFaceDef = theGraph.Editor().Faces().Mut(aFaceId); + theGraph.Editor().Faces().SetSurface(aFaceId, aCanonSurf); + ++aResult.NbSurfaceRewrites; + aResult.AffectedFaces.Append(aFaceId); + + aSurfRepl.Clear(false); + aSurfRepl.Append(aCanonFaceId); + aHistory.Record(TCollection_AsciiString("Dedup:CanonicalizeSurface"), + aFaceId, + aSurfRepl.ToArray1()); + ++aResult.NbHistoryRecords; + } + + // Rewrite edge curves: replace duplicate curve handles with canonical ones. + NCollection_LinearVector aCurveRepl(4); + for (NCollection_DataMap::Iterator anIt(aCurveRewriteMap); + anIt.More(); + anIt.Next()) + { + const BRepGraph_EdgeId anEdgeId = anIt.Key(); + const BRepGraph_EdgeId aCanonEdgeId = anIt.Value(); + const occ::handle& aCanonCurve = + BRepGraph_Tool::Edge::Curve(theGraph, aCanonEdgeId); + if (aCanonCurve.IsNull()) + { + continue; + } + const auto [aFirst, aLast] = BRepGraph_Tool::Edge::Range(theGraph, anEdgeId); + + theGraph.Editor().Edges().SetCurve(anEdgeId, aCanonCurve, aFirst, aLast); + ++aResult.NbCurveRewrites; + aResult.AffectedEdges.Append(anEdgeId); + + aCurveRepl.Clear(false); + aCurveRepl.Append(aCanonEdgeId); + aHistory.Record(TCollection_AsciiString("Dedup:CanonicalizeCurve"), + anEdgeId, + aCurveRepl.ToArray1()); + ++aResult.NbHistoryRecords; + } + + } // end if (!theOptions.AnalyzeOnly) for geometry rewrites + + } // end geometry dedup scope - frees aSurfToCanonicalFace, aSurfRewriteMap, + // aCurveToCanonicalEdge, aCurveRewriteMap // Definition merge phases (Vertex -> Edge -> Wire -> Face). if (!theOptions.MergeEntitiesWhenSafe) { aResult.IsEntityMergeApplied = false; - theGraph.History().SetEnabled(wasHistoryEnabled); + aHistory.SetEnabled(wasHistoryEnabled); return aResult; } + aTmpAlloc->Reset(false); + // Phase 1: Vertex Merging via KDTree range search. { const double aTol = theOptions.HashTolerance; // Collect active vertices: (point, graph id) pairs. - const uint32_t aNbVertices = theGraph.Topo().Vertices().Nb(); - NCollection_DynamicArray> aActiveVertices(256, aTmpAlloc); + const uint32_t aNbVertices = theGraph.Topo().Vertices().Nb(); + ActiveVertexList aActiveVertices(256); for (BRepGraph_FullVertexIterator aVertexIt(theGraph); aVertexIt.More(); aVertexIt.Next()) { - const BRepGraph_VertexId aVertexId = aVertexIt.CurrentId(); - const BRepGraphInc::VertexDef& aVtx = theGraph.Topo().Vertices().Definition(aVertexId); - if (aVtx.IsRemoved) + const BRepGraph_VertexId aVertexId = aVertexIt.CurrentId(); + if (aVertexId.IsRemoved(theGraph)) { continue; } @@ -231,19 +316,31 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG std::make_pair(BRepGraph_Tool::Vertex::Pnt(theGraph, aVertexId), aVertexId)); } - // Build KDTree from active vertex points - O(n log n). - const int aNbActive = static_cast(aActiveVertices.Size()); - NCollection_Array1 aPointsArr(0, std::max(0, aNbActive - 1)); - int i = 0; - for (const auto& aVertex : aActiveVertices) - { - aPointsArr.SetValue(i++, aVertex.first); - } + // Sort by descending vertex tolerance so high-tolerance vertices iterate + // as base first. Their search radius (max(aTol, aBaseVtxTol)) is at least + // their own tolerance, which dominates the per-pair acceptance distance + // max(baseTol, candTol). This guarantees every potentially-mergeable pair + // is examined exactly once with a sufficient radius - no global widening. + std::sort(aActiveVertices.begin(), + aActiveVertices.end(), + [&](const std::pair& theA, + const std::pair& theB) { + return BRepGraph_Tool::Vertex::Tolerance(theGraph, theA.second) + > BRepGraph_Tool::Vertex::Tolerance(theGraph, theB.second); + }); + // Build KDTree from active vertex points - O(n log n). + const size_t aNbActive = aActiveVertices.Size(); NCollection_KDTree aTree; - if (!aPointsArr.IsEmpty()) + if (aNbActive > 0) { - aTree.Build(aPointsArr); + gp_Pnt* aPointsBuf = static_cast(aTmpAlloc->Allocate(aNbActive * sizeof(gp_Pnt))); + size_t i = 0; + for (const auto& aVertex : aActiveVertices) + { + aPointsBuf[i++] = aVertex.first; + } + aTree.Build(aPointsBuf, aNbActive); } // Canonical vertex map: old graph id -> canonical graph id. @@ -262,22 +359,33 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG const gp_Pnt aBaseVtxPnt = BRepGraph_Tool::Vertex::Pnt(theGraph, aBaseVtxId); const double aBaseVtxTol = BRepGraph_Tool::Vertex::Tolerance(theGraph, aBaseVtxId); - aTree.ForEachInRange(aBaseVtxPnt, aTol, [&](size_t theResultIdx) { + aTree.ForEachInRange(aBaseVtxPnt, std::max(aTol, aBaseVtxTol), [&](size_t theResultIdx) { const size_t anArrayIdx = theResultIdx - 1; if (anArrayIdx <= aLocalIdx) { return; // skip self and already-processed } - const BRepGraph_VertexId aCandVtxId = aActiveVertices.Value(anArrayIdx).second; - if (aCanonicalVertex.IsBound(aCandVtxId)) + const BRepGraph_VertexId aCandVtxId = aActiveVertices.Value(anArrayIdx).second; + BRepGraph_VertexId aEffectiveCanon = aCandVtxId; + // Resolve through any prior canonical bindings (path compression with cycle guard). + for (size_t aHop = 0; aHop < 64 && aCanonicalVertex.IsBound(aEffectiveCanon); ++aHop) + { + const BRepGraph_VertexId aNext = aCanonicalVertex.Find(aEffectiveCanon); + if (aNext == aEffectiveCanon) + { + break; + } + aEffectiveCanon = aNext; + } + if (aEffectiveCanon == aBaseVtxId) { return; } - const double aCandVtxTol = BRepGraph_Tool::Vertex::Tolerance(theGraph, aCandVtxId); + const double aCandVtxTol = BRepGraph_Tool::Vertex::Tolerance(theGraph, aEffectiveCanon); const double aMaxTol = std::max(aBaseVtxTol, aCandVtxTol); - if (aBaseVtxPnt.Distance(BRepGraph_Tool::Vertex::Pnt(theGraph, aCandVtxId)) <= aMaxTol) + if (aBaseVtxPnt.Distance(BRepGraph_Tool::Vertex::Pnt(theGraph, aEffectiveCanon)) <= aMaxTol) { aCanonicalVertex.Bind(aCandVtxId, aBaseVtxId); } @@ -303,7 +411,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); BRepGraph_MutGuard anEdge = theGraph.Editor().Edges().Mut(anEdgeId); - if (anEdge->IsRemoved) + if (anEdgeId.IsRemoved(theGraph)) { continue; } @@ -312,79 +420,81 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG { BRepGraph_MutGuard aStartRef = theGraph.Editor().Vertices().MutRef(anEdge->StartVertexRefId); - if (aStartRef->VertexDefId == anOldVertexId) + if (aStartRef->ChildVertexId == anOldVertexId) { - theGraph.Editor().Vertices().SetRefVertexDefId(aStartRef, aCanonVertexId); + theGraph.Editor().Vertices().SetRefChildVertexId(aStartRef, aCanonVertexId); } } if (anEdge->EndVertexRefId.IsValid()) { BRepGraph_MutGuard anEndRef = theGraph.Editor().Vertices().MutRef(anEdge->EndVertexRefId); - if (anEndRef->VertexDefId == anOldVertexId) + if (anEndRef->ChildVertexId == anOldVertexId) { - theGraph.Editor().Vertices().SetRefVertexDefId(anEndRef, aCanonVertexId); - } - } - for (const BRepGraph_VertexRefId& anInternalRefId : anEdge->InternalVertexRefIds) - { - BRepGraph_MutGuard anInternalRef = - theGraph.Editor().Vertices().MutRef(anInternalRefId); - if (anInternalRef->VertexDefId == anOldVertexId) - { - theGraph.Editor().Vertices().SetRefVertexDefId(anInternalRef, aCanonVertexId); + theGraph.Editor().Vertices().SetRefChildVertexId(anEndRef, aCanonVertexId); } } } - // Update faces that directly reference the old vertex via FaceDef.VertexRefIds. - for (BRepGraph_FullFaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) + // Redirect CompoundDef.ChildRefIds that still point to the old vertex. + for (BRepGraph_Iterator aCompIt(theGraph); aCompIt.More(); + aCompIt.Next()) { - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - const BRepGraphInc::FaceDef& aFaceDef = theGraph.Topo().Faces().Definition(aFaceId); - if (aFaceDef.IsRemoved) + const BRepGraph_CompoundId aCompId = aCompIt.CurrentId(); + if (aCompId.IsRemoved(theGraph)) { continue; } - for (const BRepGraph_VertexRefId& aVRefId : aFaceDef.VertexRefIds) + for (BRepGraph_RefsChildOfCompound aRefIt(theGraph, aCompId); aRefIt.More(); + aRefIt.Next()) { - BRepGraph_MutGuard aVRef = - theGraph.Editor().Vertices().MutRef(aVRefId); - if (aVRef->VertexDefId == anOldVertexId) + const BRepGraphInc::ChildRef& aCR = + theGraph.Refs().Children().Entry(aRefIt.CurrentId()); + if (!theGraph.Refs().Gen().IsRemoved(aRefIt.CurrentId()) && aCR.ChildNodeId == anOldId) { - theGraph.Editor().Vertices().SetRefVertexDefId(aVRef, aCanonVertexId); + BRepGraph_MutGuard aMutCR = + theGraph.Editor().Gen().MutChildRef(aRefIt.CurrentId()); + theGraph.Editor().Gen().SetChildRefChildNodeId(aMutCR, aCanonId); } } } + // Redirect OccurrenceDef.ChildNodeId entries that still point to the old vertex. + redirectOccurrenceChildren(theGraph, anOldId, aCanonId); + // Mark non-canonical as removed. - theGraph.Editor().Gen().RemoveNode(anOldId, aCanonId); + theGraph.Editor().Gen().ReplaceNode(anOldId, aCanonId); + aHistory.RecordReplaced(TCollection_AsciiString("Dedup:MergeVertex"), anOldId, aCanonId); - NCollection_DynamicArray aRepl; - aRepl.Append(aCanonId); - theGraph.History().Record(TCollection_AsciiString("Dedup:MergeVertex"), anOldId, aRepl); ++aResult.NbHistoryRecords; ++aResult.NbMergedVertices; } } else { - aResult.NbMergedVertices = aCanonicalVertex.Length(); + aResult.NbMergedVertices = static_cast(aCanonicalVertex.Size()); } } + if (!theOptions.AnalyzeOnly) + { + CanonicalizeWireOrders(theGraph, aResult); + } + + aTmpAlloc->Reset(false); + // Phase 2: Edge Merging. { - // Key: (canonical Curve3d pointer, canonical StartVertexDefId, canonical EndVertexDefId). + // Key: (canonical Curve3d handle, canonical StartChildVertexId, canonical EndChildVertexId). struct EdgeKey { - const Geom_Curve* CurvePtr; - BRepGraph_VertexId StartVtx; - BRepGraph_VertexId EndVtx; + occ::handle Curve; + BRepGraph_VertexId StartVtx; + BRepGraph_VertexId EndVtx; bool operator==(const EdgeKey& theOther) const { - return CurvePtr == theOther.CurvePtr && StartVtx == theOther.StartVtx + return Curve == theOther.Curve && StartVtx == theOther.StartVtx && EndVtx == theOther.EndVtx; } }; @@ -394,7 +504,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG size_t operator()(const EdgeKey& theKey) const noexcept { size_t aCombination[3]; - aCombination[0] = std::hash{}(theKey.CurvePtr); + aCombination[0] = NCollection_DefaultHasher>{}(theKey.Curve); aCombination[1] = opencascade::hash(theKey.StartVtx); aCombination[2] = opencascade::hash(theKey.EndVtx); return opencascade::hashBytes(aCombination, sizeof(aCombination)); @@ -403,26 +513,32 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG bool operator()(const EdgeKey& theA, const EdgeKey& theB) const { return theA == theB; } }; - NCollection_DataMap, EdgeKeyHasher> - anEdgeGroups(std::max(1, theGraph.Topo().Edges().Nb()), aTmpAlloc); + NCollection_DataMap anEdgeGroups( + std::max(1, theGraph.Topo().Edges().Nb()), + aTmpAlloc); for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(anEdgeId); - if (anEdge.IsRemoved || !BRepGraph_Tool::Edge::HasCurve(theGraph, anEdgeId)) + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + if (anEdgeId.IsRemoved(theGraph) || !BRepGraph_Tool::Edge::HasCurve(theGraph, anEdgeId)) { continue; } - // Use canonical (forward) key: use raw pointer as a stable identity. EdgeKey aKey; - aKey.CurvePtr = BRepGraph_Tool::Edge::Curve(theGraph, anEdgeId).get(); - const BRepGraph_VertexId aStartVtxId = + aKey.Curve = BRepGraph_Tool::Edge::Curve(theGraph, anEdgeId); + const BRepGraph_VertexRefId aStartRefId = BRepGraph_Tool::Edge::StartVertexId(theGraph, anEdgeId); - const BRepGraph_VertexId anEndVtxId = BRepGraph_Tool::Edge::EndVertexId(theGraph, anEdgeId); - aKey.StartVtx = aStartVtxId; - aKey.EndVtx = anEndVtxId; + const BRepGraph_VertexRefId anEndRefId = + BRepGraph_Tool::Edge::EndVertexId(theGraph, anEdgeId); + const BRepGraph_VertexId aStartVtxId = + aStartRefId.IsValid() ? theGraph.Refs().Vertices().Entry(aStartRefId).ChildVertexId + : BRepGraph_VertexId(); + const BRepGraph_VertexId anEndVtxId = + anEndRefId.IsValid() ? theGraph.Refs().Vertices().Entry(anEndRefId).ChildVertexId + : BRepGraph_VertexId(); + aKey.StartVtx = aStartVtxId; + aKey.EndVtx = anEndVtxId; // Normalize: always use min vertex index first for undirected matching. if (aKey.StartVtx > aKey.EndVtx) @@ -430,20 +546,19 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG std::swap(aKey.StartVtx, aKey.EndVtx); } - anEdgeGroups.TryBind(aKey, NCollection_DynamicArray()); + anEdgeGroups.TryBind(aKey, EdgeIdList()); anEdgeGroups.ChangeFind(aKey).Append(anEdgeId); } NCollection_DataMap aCanonicalEdge( - std::max(1, theGraph.Topo().Edges().Nb()), + std::max(1, theGraph.Topo().Edges().Nb()), aTmpAlloc); - for (NCollection_DataMap, EdgeKeyHasher>:: - Iterator aGroupIter(anEdgeGroups); + for (NCollection_DataMap::Iterator aGroupIter(anEdgeGroups); aGroupIter.More(); aGroupIter.Next()) { - const NCollection_DynamicArray& aGroup = aGroupIter.Value(); + const EdgeIdList& aGroup = aGroupIter.Value(); if (aGroup.Size() < 2) { continue; @@ -458,11 +573,13 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG const BRepGraphInc::EdgeDef& aCandEdge = theGraph.Topo().Edges().Definition(aCandEdgeId); // Compare parameter ranges within tolerance. - if (std::abs(aCanonEdge.ParamFirst - aCandEdge.ParamFirst) > theOptions.CompTolerance) + const auto [aCanonFirst, aCanonLast] = BRepGraph_Tool::Edge::Range(theGraph, aCanonEdgeId); + const auto [aCandFirst, aCandLast] = BRepGraph_Tool::Edge::Range(theGraph, aCandEdgeId); + if (std::abs(aCanonFirst - aCandFirst) > theOptions.CompTolerance) { continue; } - if (std::abs(aCanonEdge.ParamLast - aCandEdge.ParamLast) > theOptions.CompTolerance) + if (std::abs(aCanonLast - aCandLast) > theOptions.CompTolerance) { continue; } @@ -479,6 +596,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG if (!theOptions.AnalyzeOnly) { + WireIdList aWires(64); for (NCollection_DataMap::Iterator anIt(aCanonicalEdge); anIt.More(); anIt.Next()) @@ -490,43 +608,99 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG // Determine if the non-canonical edge is reversed relative to canonical. // Resolve vertex def ids for reversal check using Tool helpers. - const BRepGraph_NodeId aCanonStart = + const BRepGraph_VertexRefId aCanonStartRef = BRepGraph_Tool::Edge::StartVertexId(theGraph, aCanonEdgeId); - const BRepGraph_NodeId aCanonEnd = + const BRepGraph_VertexRefId aCanonEndRef = BRepGraph_Tool::Edge::EndVertexId(theGraph, aCanonEdgeId); - const BRepGraph_NodeId anOldStart = + const BRepGraph_VertexRefId anOldStartRef = BRepGraph_Tool::Edge::StartVertexId(theGraph, anOldEdgeId); - const BRepGraph_NodeId anOldEnd = BRepGraph_Tool::Edge::EndVertexId(theGraph, anOldEdgeId); - const bool isReversed = (aCanonStart == anOldEnd && aCanonEnd == anOldStart); + const BRepGraph_VertexRefId anOldEndRef = + BRepGraph_Tool::Edge::EndVertexId(theGraph, anOldEdgeId); + const BRepGraph_NodeId aCanonStart = + aCanonStartRef.IsValid() + ? BRepGraph_NodeId(theGraph.Refs().Vertices().Entry(aCanonStartRef).ChildVertexId) + : BRepGraph_NodeId(); + const BRepGraph_NodeId aCanonEnd = + aCanonEndRef.IsValid() + ? BRepGraph_NodeId(theGraph.Refs().Vertices().Entry(aCanonEndRef).ChildVertexId) + : BRepGraph_NodeId(); + const BRepGraph_NodeId anOldStart = + anOldStartRef.IsValid() + ? BRepGraph_NodeId(theGraph.Refs().Vertices().Entry(anOldStartRef).ChildVertexId) + : BRepGraph_NodeId(); + const BRepGraph_NodeId anOldEnd = + anOldEndRef.IsValid() + ? BRepGraph_NodeId(theGraph.Refs().Vertices().Entry(anOldEndRef).ChildVertexId) + : BRepGraph_NodeId(); + bool isReversed = false; + // Self-loop edges cannot be reversed in the sense that matters for wire + // replacement - after vertex merging, both ends could resolve to the + // same canonical vertex, incorrectly satisfying the reversal condition. + if (!(aCanonStart == aCanonEnd || anOldStart == anOldEnd)) + { + isReversed = (aCanonStart == anOldEnd && aCanonEnd == anOldStart); + } - // Replace in wires. - const NCollection_DynamicArray& aWires = - theGraph.Topo().Edges().Wires(anOldEdgeId); + // Replace in wires - copy wire list before iterating because ReplaceEdge + // mutates myEdgeToWires[oldEdgeId.Index] via eraseSwapLast. + aWires.Clear(false); + for (BRepGraph_WiresOfEdge aWireIt = theGraph.Topo().Edges().WiresOf(anOldEdgeId); + aWireIt.More(); + aWireIt.Next()) + { + aWires.Append(aWireIt.CurrentId()); + } for (const BRepGraph_WireId& aWireId : aWires) { theGraph.Editor().Wires().ReplaceEdge(aWireId, anOldEdgeId, aCanonEdgeId, isReversed); } - // ReplaceEdge() above rebinds all CoEdgeDef.EdgeDefId entries from anOldEdgeId + // ReplaceEdge() above rebinds all CoEdgeDef.ChildEdgeId entries from anOldEdgeId // to aCanonEdgeId (and updates the reverse CoEdgesOfEdge index), so // theGraph.Topo().Edges().CoEdges(anOldEdgeId) is always empty at this point. // PCurve handles are preserved through the CoEdge rebinding. - theGraph.Editor().Gen().RemoveNode(anOldId, aCanonId); + // Redirect CompoundDef.ChildRefIds that still point to the old edge. + for (BRepGraph_Iterator aCompIt(theGraph); aCompIt.More(); + aCompIt.Next()) + { + const BRepGraph_CompoundId aCompId = aCompIt.CurrentId(); + if (aCompId.IsRemoved(theGraph)) + { + continue; + } + for (BRepGraph_RefsChildOfCompound aRefIt(theGraph, aCompId); aRefIt.More(); + aRefIt.Next()) + { + const BRepGraphInc::ChildRef& aCR = + theGraph.Refs().Children().Entry(aRefIt.CurrentId()); + if (!theGraph.Refs().Gen().IsRemoved(aRefIt.CurrentId()) && aCR.ChildNodeId == anOldId) + { + BRepGraph_MutGuard aMutCR = + theGraph.Editor().Gen().MutChildRef(aRefIt.CurrentId()); + theGraph.Editor().Gen().SetChildRefChildNodeId(aMutCR, aCanonId); + } + } + } + + // Redirect OccurrenceDef.ChildNodeId entries that still point to the old edge. + redirectOccurrenceChildren(theGraph, anOldId, aCanonId); + + theGraph.Editor().Gen().ReplaceNode(anOldId, aCanonId); + aHistory.RecordReplaced(TCollection_AsciiString("Dedup:MergeEdge"), anOldId, aCanonId); - NCollection_DynamicArray aRepl; - aRepl.Append(aCanonId); - theGraph.History().Record(TCollection_AsciiString("Dedup:MergeEdge"), anOldId, aRepl); ++aResult.NbHistoryRecords; ++aResult.NbMergedEdges; } } else { - aResult.NbMergedEdges = aCanonicalEdge.Length(); + aResult.NbMergedEdges = static_cast(aCanonicalEdge.Size()); } } + aTmpAlloc->Reset(false); + // Phase 3: Wire Merging. { // Hash wire by its ordered coedge sequence (edge index + sense from CoEdgeDef). @@ -535,12 +709,12 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG size_t operator()(const BRepGraph_WireId theWireId, const BRepGraph& theGraph) const { size_t aHash = 0; - for (BRepGraph_RefsCoEdgeOfWire aCEIt(theGraph, theWireId); aCEIt.More(); aCEIt.Next()) + for (BRepGraph_CoEdgesOfWire aCEIt(theGraph, theWireId); aCEIt.More(); aCEIt.Next()) { - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition( - theGraph.Refs().CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId); + const BRepGraphInc::CoEdgeDef& aCoEdge = + theGraph.Topo().CoEdges().Definition(aCEIt.CurrentId()); size_t aEntryHash[2]; - aEntryHash[0] = opencascade::hash(aCoEdge.EdgeDefId); + aEntryHash[0] = opencascade::hash(aCoEdge.ChildEdgeId); aEntryHash[1] = opencascade::hash(static_cast(aCoEdge.Orientation)); aHash ^= opencascade::hashBytes(aEntryHash, sizeof(aEntryHash)) + 0x9e3779b9 + (aHash << 6) + (aHash >> 2); @@ -549,31 +723,32 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG } }; + NCollection_LinearVector aWireACoEdges(64); + NCollection_LinearVector aWireBCoEdges(64); + auto wiresEqual = [&](const BRepGraph_WireId theA, const BRepGraph_WireId theB) -> bool { - NCollection_DynamicArray aWireACoEdges; - NCollection_DynamicArray aWireBCoEdges; - for (BRepGraph_RefsCoEdgeOfWire aCEIt(theGraph, theA); aCEIt.More(); aCEIt.Next()) + aWireACoEdges.Clear(false); + aWireBCoEdges.Clear(false); + for (BRepGraph_CoEdgesOfWire aCEIt(theGraph, theA); aCEIt.More(); aCEIt.Next()) { - aWireACoEdges.Append(theGraph.Refs().CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId); + aWireACoEdges.Append(aCEIt.CurrentId()); } - for (BRepGraph_RefsCoEdgeOfWire aCEIt(theGraph, theB); aCEIt.More(); aCEIt.Next()) + for (BRepGraph_CoEdgesOfWire aCEIt(theGraph, theB); aCEIt.More(); aCEIt.Next()) { - aWireBCoEdges.Append(theGraph.Refs().CoEdges().Entry(aCEIt.CurrentId()).CoEdgeDefId); + aWireBCoEdges.Append(aCEIt.CurrentId()); } if (aWireACoEdges.Size() != aWireBCoEdges.Size()) { return false; } - NCollection_DynamicArray::Iterator anItA(aWireACoEdges); - NCollection_DynamicArray::Iterator anItB(aWireBCoEdges); - for (; anItA.More(); anItA.Next(), anItB.Next()) + for (size_t anIdx = 0; anIdx < aWireACoEdges.Size(); ++anIdx) { const BRepGraphInc::CoEdgeDef& aCoEdgeA = - theGraph.Topo().CoEdges().Definition(anItA.Value()); + theGraph.Topo().CoEdges().Definition(aWireACoEdges.Value(anIdx)); const BRepGraphInc::CoEdgeDef& aCoEdgeB = - theGraph.Topo().CoEdges().Definition(anItB.Value()); - if (aCoEdgeA.EdgeDefId != aCoEdgeB.EdgeDefId + theGraph.Topo().CoEdges().Definition(aWireBCoEdges.Value(anIdx)); + if (aCoEdgeA.ChildEdgeId != aCoEdgeB.ChildEdgeId || aCoEdgeA.Orientation != aCoEdgeB.Orientation) { return false; @@ -582,34 +757,32 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG return true; }; - NCollection_DataMap> aWireHashBuckets( - std::max(1, theGraph.Topo().Wires().Nb()), + NCollection_DataMap aWireHashBuckets( + std::max(1, theGraph.Topo().Wires().Nb()), aTmpAlloc); WireHash aHasher; for (BRepGraph_FullWireIterator aWireIt(theGraph); aWireIt.More(); aWireIt.Next()) { - const BRepGraph_WireId aWireId = aWireIt.CurrentId(); - const BRepGraphInc::WireDef& aWire = theGraph.Topo().Wires().Definition(aWireId); - if (aWire.IsRemoved) + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); + if (aWireId.IsRemoved(theGraph)) { continue; } const size_t aH = aHasher(aWireId, theGraph); - aWireHashBuckets.TryBind(aH, NCollection_DynamicArray()); + aWireHashBuckets.TryBind(aH, WireIdList()); aWireHashBuckets.ChangeFind(aH).Append(aWireId); } NCollection_DataMap aCanonicalWire( - std::max(1, theGraph.Topo().Wires().Nb()), + std::max(1, theGraph.Topo().Wires().Nb()), aTmpAlloc); - for (NCollection_DataMap>::Iterator - aBucketIter(aWireHashBuckets); + for (NCollection_DataMap::Iterator aBucketIter(aWireHashBuckets); aBucketIter.More(); aBucketIter.Next()) { - const NCollection_DynamicArray& aBucket = aBucketIter.Value(); + const WireIdList& aBucket = aBucketIter.Value(); for (size_t aBaseIdx = 0; aBaseIdx < aBucket.Size(); ++aBaseIdx) { const BRepGraph_WireId aBaseWireId = aBucket.Value(aBaseIdx); @@ -648,87 +821,76 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG // Redirect FaceDef.WireRefIds that still point to the old wire. for (BRepGraph_FullFaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - const BRepGraphInc::FaceDef& aFaceDef = theGraph.Topo().Faces().Definition(aFaceId); - if (aFaceDef.IsRemoved) + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + if (aFaceId.IsRemoved(theGraph)) { continue; } - for (const BRepGraph_WireRefId& aWireRefId : aFaceDef.WireRefIds) + for (const BRepGraph_WireRefId& aWireRefId : + theGraph.Topo().Faces().Relations(aFaceId).WireRefIds) { const BRepGraphInc::WireRef& aWireRef = theGraph.Refs().Wires().Entry(aWireRefId); - if (!aWireRef.IsRemoved && aWireRef.WireDefId == anOldWireId) + if (!theGraph.Refs().Gen().IsRemoved(aWireRefId) && aWireRef.ChildWireId == anOldWireId) { BRepGraph_MutGuard aMutWireRef = theGraph.Editor().Wires().MutRef(aWireRefId); - theGraph.Editor().Wires().SetRefWireDefId(aMutWireRef, aCanonWireId); + theGraph.Editor().Wires().SetRefChildWireId(aMutWireRef, aCanonWireId); } } } - // Redirect ShellDef.AuxChildRefIds and SolidDef.AuxChildRefIds that still - // point to the old wire as a non-face child. - for (BRepGraph_FullShellIterator aShellIt(theGraph); aShellIt.More(); aShellIt.Next()) + // Redirect CompoundDef.ChildRefIds that still point to the old wire. + for (BRepGraph_Iterator aCompIt(theGraph); aCompIt.More(); + aCompIt.Next()) { - const BRepGraph_ShellId aShellId = aShellIt.CurrentId(); - if (theGraph.Topo().Shells().Definition(aShellId).IsRemoved) + const BRepGraph_CompoundId aCompId = aCompIt.CurrentId(); + if (aCompId.IsRemoved(theGraph)) { continue; } - for (BRepGraph_RefsChildOfShell aRefIt(theGraph, aShellId); aRefIt.More(); aRefIt.Next()) + for (BRepGraph_RefsChildOfCompound aRefIt(theGraph, aCompId); aRefIt.More(); + aRefIt.Next()) { const BRepGraphInc::ChildRef& aCR = theGraph.Refs().Children().Entry(aRefIt.CurrentId()); - if (!aCR.IsRemoved && aCR.ChildDefId == anOldId) + if (!theGraph.Refs().Gen().IsRemoved(aRefIt.CurrentId()) && aCR.ChildNodeId == anOldId) { BRepGraph_MutGuard aMutCR = theGraph.Editor().Gen().MutChildRef(aRefIt.CurrentId()); - theGraph.Editor().Gen().SetChildRefChildDefId(aMutCR, aCanonId); - } - } - } - for (BRepGraph_FullSolidIterator aSolidIt(theGraph); aSolidIt.More(); aSolidIt.Next()) - { - const BRepGraph_SolidId aSolidId = aSolidIt.CurrentId(); - if (theGraph.Topo().Solids().Definition(aSolidId).IsRemoved) - { - continue; - } - for (BRepGraph_RefsChildOfSolid aRefIt(theGraph, aSolidId); aRefIt.More(); aRefIt.Next()) - { - const BRepGraphInc::ChildRef& aCR = - theGraph.Refs().Children().Entry(aRefIt.CurrentId()); - if (!aCR.IsRemoved && aCR.ChildDefId == anOldId) - { - BRepGraph_MutGuard aMutCR = - theGraph.Editor().Gen().MutChildRef(aRefIt.CurrentId()); - theGraph.Editor().Gen().SetChildRefChildDefId(aMutCR, aCanonId); + theGraph.Editor().Gen().SetChildRefChildNodeId(aMutCR, aCanonId); } } } - theGraph.Editor().Gen().RemoveNode(anOldId, aCanonId); + // Redirect OccurrenceDef.ChildNodeId entries that still point to the old wire. + redirectOccurrenceChildren(theGraph, anOldId, aCanonId); + + theGraph.Editor().Gen().ReplaceNode(anOldId, aCanonId); + aHistory.RecordReplaced(TCollection_AsciiString("Dedup:MergeWire"), anOldId, aCanonId); + ++aResult.NbHistoryRecords; ++aResult.NbMergedWires; } } else { - aResult.NbMergedWires = aCanonicalWire.Length(); + aResult.NbMergedWires = static_cast(aCanonicalWire.Size()); } } + aTmpAlloc->Reset(false); + // Phase 4: Face Merging. { struct FaceKey { - const Geom_Surface* SurfPtr; - size_t WireHash; + occ::handle Surface; + size_t WireHash; bool operator==(const FaceKey& theOther) const { - return SurfPtr == theOther.SurfPtr && WireHash == theOther.WireHash; + return Surface == theOther.Surface && WireHash == theOther.WireHash; } }; @@ -737,7 +899,7 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG size_t operator()(const FaceKey& theKey) const noexcept { size_t aCombination[2]; - aCombination[0] = std::hash{}(theKey.SurfPtr); + aCombination[0] = NCollection_DefaultHasher>{}(theKey.Surface); aCombination[1] = theKey.WireHash; return opencascade::hashBytes(aCombination, sizeof(aCombination)); } @@ -751,48 +913,43 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG for (BRepGraph_RefsWireOfFace aWireIt(theGraph, theFaceId); aWireIt.More(); aWireIt.Next()) { const BRepGraphInc::WireRef& aWR = theGraph.Refs().Wires().Entry(aWireIt.CurrentId()); - if (aWR.IsOuter) - { - aHash ^= opencascade::hash(aWR.WireDefId); - } - else - { - aHash ^= opencascade::hash(aWR.WireDefId) + 0x9e3779b9; - } + aHash ^= opencascade::hash(aWR.ChildWireId); } return aHash; }; - NCollection_DataMap, FaceKeyHasher> - aFaceGroups(std::max(1, theGraph.Topo().Faces().Nb()), aTmpAlloc); + NCollection_DataMap aFaceGroups( + std::max(1, theGraph.Topo().Faces().Nb()), + aTmpAlloc); for (BRepGraph_FullFaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - const BRepGraphInc::FaceDef& aFace = theGraph.Topo().Faces().Definition(aFaceId); - if (aFace.IsRemoved || !BRepGraph_Tool::Face::HasSurface(theGraph, aFaceId)) + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + if (aFaceId.IsRemoved(theGraph) || !BRepGraph_Tool::Face::HasSurface(theGraph, aFaceId)) { continue; } FaceKey aKey; - aKey.SurfPtr = BRepGraph_Tool::Face::Surface(theGraph, aFaceId).get(); + aKey.Surface = BRepGraph_Tool::Face::Surface(theGraph, aFaceId); aKey.WireHash = faceWireHash(aFaceId); - aFaceGroups.TryBind(aKey, NCollection_DynamicArray()); + aFaceGroups.TryBind(aKey, FaceIdList()); aFaceGroups.ChangeFind(aKey).Append(aFaceId); } NCollection_DataMap aCanonicalFace( - std::max(1, theGraph.Topo().Faces().Nb()), + std::max(1, theGraph.Topo().Faces().Nb()), aTmpAlloc); - for (NCollection_DataMap, FaceKeyHasher>:: - Iterator aGroupIter(aFaceGroups); + WireIdList aCanonOuter(64); + WireIdList aCandOuter(64); + + for (NCollection_DataMap::Iterator aGroupIter(aFaceGroups); aGroupIter.More(); aGroupIter.Next()) { - const NCollection_DynamicArray& aGroup = aGroupIter.Value(); + const FaceIdList& aGroup = aGroupIter.Value(); if (aGroup.Size() < 2) { continue; @@ -812,6 +969,56 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG continue; } + // Verify wire equality - two faces sharing the same surface and wire hash + // are not mergeable unless they also have the same wire topology. + if (![&]() -> bool { + aCanonOuter.Clear(false); + aCandOuter.Clear(false); + for (BRepGraph_RefsWireOfFace aWIt(theGraph, aCanonFaceId); aWIt.More(); aWIt.Next()) + { + const BRepGraphInc::WireRef& aWR = theGraph.Refs().Wires().Entry(aWIt.CurrentId()); + if (aWR.ChildWireId.IsRemoved(theGraph)) + { + continue; + } + aCanonOuter.Append(aWR.ChildWireId); + } + for (BRepGraph_RefsWireOfFace aWIt(theGraph, aCandFaceId); aWIt.More(); aWIt.Next()) + { + const BRepGraphInc::WireRef& aWR = theGraph.Refs().Wires().Entry(aWIt.CurrentId()); + if (aWR.ChildWireId.IsRemoved(theGraph)) + { + continue; + } + aCandOuter.Append(aWR.ChildWireId); + } + // Sort wires for order-independent comparison. + auto sortWires = [](WireIdList& theWires) { + std::sort(theWires.begin(), + theWires.end(), + [](const BRepGraph_WireId& a, const BRepGraph_WireId& b) { + return a.Index < b.Index; + }); + }; + sortWires(aCanonOuter); + sortWires(aCandOuter); + if (aCanonOuter.Size() != aCandOuter.Size()) + { + return false; + } + for (size_t i = 0; i < aCanonOuter.Size(); ++i) + { + if (aCanonOuter.Value(i) != aCandOuter.Value(i)) + { + return false; + } + } + return true; + }()) + { + continue; + } + aCanonicalFace.Bind(aCandFaceId, aCanonFaceId); } } @@ -833,44 +1040,69 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG { const BRepGraph_FaceRefId aFaceRefId = aFaceRefIt.CurrentId(); const BRepGraphInc::FaceRef& aFaceRef = theGraph.Refs().Faces().Entry(aFaceRefId); - if (!aFaceRef.IsRemoved && aFaceRef.FaceDefId == anOldFaceId) + if (!theGraph.Refs().Gen().IsRemoved(aFaceRefId) && aFaceRef.ChildFaceId == anOldFaceId) { BRepGraph_MutGuard aMutFaceRef = theGraph.Editor().Faces().MutRef(aFaceRefId); - theGraph.Editor().Faces().SetRefFaceDefId(aMutFaceRef, aCanonFaceId); + theGraph.Editor().Faces().SetRefFaceId(aMutFaceRef, aCanonFaceId); } } - // Redirect CoEdgeDef.FaceDefId entries that point to the old face to the canonical one. - // This must happen before RemoveNode, otherwise compact will produce dangling FaceDefId - // refs (CoEdges with invalid FaceDefId but live Curve2DRepId - orphaned PCurve state). + // Redirect CoEdgeDef.FaceId entries that point to the old face to the canonical one. + // This must happen before RemoveNode, otherwise compact will produce dangling FaceId + // refs (CoEdges with invalid FaceId but live Curve2DRepId - orphaned PCurve state). for (BRepGraph_FullCoEdgeIterator aCEIt(theGraph); aCEIt.More(); aCEIt.Next()) { const BRepGraph_CoEdgeId aCEId = aCEIt.CurrentId(); - if (theGraph.Topo().CoEdges().Definition(aCEId).IsRemoved) + if (BRepGraph_NodeId(aCEId).IsRemoved(theGraph)) { continue; } - if (theGraph.Topo().CoEdges().Definition(aCEId).FaceDefId == anOldFaceId) + if (theGraph.Topo().CoEdges().Definition(aCEId).FaceId == anOldFaceId) { BRepGraph_MutGuard aMutCE = theGraph.Editor().CoEdges().Mut(aCEId); - theGraph.Editor().CoEdges().SetFaceDefId(aMutCE, aCanonFaceId); + theGraph.Editor().CoEdges().SetFaceId(aMutCE, aCanonFaceId); } } - theGraph.Editor().Gen().RemoveNode(anOldId, aCanonId); + // Redirect CompoundDef.ChildRefIds that still point to the old face. + for (BRepGraph_Iterator aCompIt(theGraph); aCompIt.More(); + aCompIt.Next()) + { + const BRepGraph_CompoundId aCompId = aCompIt.CurrentId(); + if (aCompId.IsRemoved(theGraph)) + { + continue; + } + for (BRepGraph_RefsChildOfCompound aRefIt(theGraph, aCompId); aRefIt.More(); + aRefIt.Next()) + { + const BRepGraphInc::ChildRef& aCR = + theGraph.Refs().Children().Entry(aRefIt.CurrentId()); + if (!theGraph.Refs().Gen().IsRemoved(aRefIt.CurrentId()) && aCR.ChildNodeId == anOldId) + { + BRepGraph_MutGuard aMutCR = + theGraph.Editor().Gen().MutChildRef(aRefIt.CurrentId()); + theGraph.Editor().Gen().SetChildRefChildNodeId(aMutCR, aCanonId); + } + } + } + + // Redirect OccurrenceDef.ChildNodeId entries that still point to the old face. + // Snapshot first - SetChildNodeId modifies the underlying OccurrenceRefsOfNode vector. + redirectOccurrenceChildren(theGraph, anOldId, aCanonId); + + theGraph.Editor().Gen().ReplaceNode(anOldId, aCanonId); + aHistory.RecordReplaced(TCollection_AsciiString("Dedup:MergeFace"), anOldId, aCanonId); - NCollection_DynamicArray aRepl; - aRepl.Append(aCanonId); - theGraph.History().Record(TCollection_AsciiString("Dedup:MergeFace"), anOldId, aRepl); ++aResult.NbHistoryRecords; ++aResult.NbMergedFaces; } } else { - aResult.NbMergedFaces = aCanonicalFace.Length(); + aResult.NbMergedFaces = static_cast(aCanonicalFace.Size()); } } @@ -878,6 +1110,6 @@ BRepGraph_Deduplicate::Result BRepGraph_Deduplicate::Perform(BRepGraph& theG && (aResult.NbMergedVertices > 0 || aResult.NbMergedEdges > 0 || aResult.NbMergedWires > 0 || aResult.NbMergedFaces > 0); - theGraph.History().SetEnabled(wasHistoryEnabled); + aHistory.SetEnabled(wasHistoryEnabled); return aResult; } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.hxx index 2387356bbf..e725884c57 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Deduplicate.hxx @@ -15,9 +15,8 @@ #define _BRepGraph_Deduplicate_HeaderFile #include - #include -#include +#include #include #include @@ -46,23 +45,26 @@ public: //! Result counters for diagnostics and tests. struct Result { - int NbCanonicalSurfaces = 0; - int NbCanonicalCurves = 0; - int NbSurfaceRewrites = 0; - int NbCurveRewrites = 0; - int NbNullifiedSurfaces = 0; - int NbNullifiedCurves = 0; - int NbHistoryRecords = 0; - bool IsEntityMergeApplied = false; + uint32_t NbCanonicalSurfaces = 0; + uint32_t NbCanonicalCurves = 0; + uint32_t NbSurfaceRewrites = 0; + uint32_t NbCurveRewrites = 0; + uint32_t NbNullifiedSurfaces = 0; + uint32_t NbNullifiedCurves = 0; + uint32_t NbHistoryRecords = 0; + bool IsEntityMergeApplied = false; //! Topology definition merge counters (active when MergeEntitiesWhenSafe = true). - int NbMergedVertices = 0; - int NbMergedEdges = 0; - int NbMergedWires = 0; - int NbMergedFaces = 0; + uint32_t NbMergedVertices = 0; + uint32_t NbMergedEdges = 0; + uint32_t NbMergedWires = 0; + uint32_t NbMergedFaces = 0; + uint32_t NbReorderedWires = 0; + uint32_t NbToleranceOrderedWires = 0; + uint32_t NbPartialOrderedWires = 0; - NCollection_DynamicArray AffectedFaces; //!< Faces whose SurfNodeId changed. - NCollection_DynamicArray AffectedEdges; //!< Edges whose CurveNodeId changed. + NCollection_LinearVector AffectedFaces; //!< Faces whose SurfNodeId changed. + NCollection_LinearVector AffectedEdges; //!< Edges whose CurveNodeId changed. }; //! Run deduplication on a built graph. @@ -77,8 +79,10 @@ public: [[nodiscard]] Standard_EXPORT static Result Perform(BRepGraph& theGraph, const Options& theOptions); -private: BRepGraph_Deduplicate() = delete; + +private: + static void CanonicalizeWireOrders(BRepGraph& theGraph, Result& theResult); }; #endif // _BRepGraph_Deduplicate_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DeferredScope.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DeferredScope.hxx index 5f6a1cfc06..2808e6827b 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DeferredScope.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DeferredScope.hxx @@ -23,8 +23,8 @@ //! followed by CommitMutation validation. Guarantees exception-safe cleanup: //! when this guard owns deferred mode, it is always closed and boundary checks //! are executed at scope exit. EndDeferredInvalidation() batch-propagates -//! SubtreeGen upward, then CommitMutation() validates reverse-index consistency -//! and active-entity counts. +//! SubtreeGen upward, then CommitMutation() validates relation consistency and +//! active-entity counts. //! //! Re-entrant: if deferred mode is already active (e.g., nested guard), //! the inner guard is a no-op. Only the outermost guard flushes and commits, @@ -55,10 +55,12 @@ public: myOwnsScope(!theGraph.Editor().IsDeferredMode()) { if (myOwnsScope) + { myGraph.Editor().BeginDeferredInvalidation(); + } } - //! End deferred invalidation and validate reverse index + active counts. + //! End deferred invalidation and validate relations + active counts. ~BRepGraph_DeferredScope() { if (myOwnsScope) diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DefsIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DefsIterator.hxx index 3ea84451e3..91971ad85d 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DefsIterator.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_DefsIterator.hxx @@ -17,8 +17,9 @@ #include #include #include - #include +#include +#include //! @brief Single-level typed iterators over active child definitions. //! @@ -44,21 +45,11 @@ struct BaseTraits using RefEntry = RefEntryT; using ChildId = ChildIdT; using ChildDef = ChildDefT; + + static constexpr bool THE_IS_DIRECT = std::is_same_v; }; -template -inline const BRepGraphInc::BaseDef* childBaseDef(const BRepGraph& theGraph, - const ChildIdT theChildId) -{ - return theGraph.Topo().Gen().TopoEntity(BRepGraph_NodeId(theChildId)); -} - -inline const BRepGraphInc::BaseDef* childBaseDef(const BRepGraph& theGraph, - const BRepGraph_NodeId theChildId) -{ - return theGraph.Topo().Gen().TopoEntity(theChildId); -} - +//! Traits for iterating over shell children of a solid. struct ShellOfSolidTraits : public BaseTraits& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Solids().Definition(theParent).ShellRefIds; + return theGraph.Topo().Solids().Relations(theParent).ShellRefIds; } static const BRepGraphInc::ShellRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -84,7 +74,7 @@ struct ShellOfSolidTraits : public BaseTraits& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Shells().Definition(theParent).FaceRefIds; + return theGraph.Topo().Shells().Relations(theParent).FaceRefIds; } static const BRepGraphInc::FaceRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -118,7 +108,7 @@ struct FaceOfShellTraits : public BaseTraits -{ - static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) - { - return theParent.IsValid(theGraph.Topo().Shells().Nb()) - && !theGraph.Topo().Shells().Definition(theParent).IsRemoved; - } - - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, - const ParentId theParent) - { - return theGraph.Topo().Shells().Definition(theParent).AuxChildRefIds; - } - - static const BRepGraphInc::ChildRef& Ref(const BRepGraph& theGraph, const RefId theRefId) - { - return theGraph.Refs().Children().Entry(theRefId); - } - - static ChildId ChildIdOf(const BRepGraph&, const BRepGraphInc::ChildRef& theRef) - { - return theRef.ChildDefId; - } - - static const ChildDef& Child(const BRepGraph& theGraph, const ChildId theChildId) - { - return *theGraph.Topo().Gen().TopoEntity(theChildId); - } -}; - +//! Traits for iterating over wire children of a face. struct WireOfFaceTraits : public BaseTraits& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Faces().Definition(theParent).WireRefIds; + return theGraph.Topo().Faces().Relations(theParent).WireRefIds; } static const BRepGraphInc::WireRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -186,7 +142,7 @@ struct WireOfFaceTraits : public BaseTraits -{ - static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) - { - return theParent.IsValid(theGraph.Topo().Faces().Nb()) - && !theGraph.Topo().Faces().Definition(theParent).IsRemoved; - } - - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, - const ParentId theParent) - { - return theGraph.Topo().Faces().Definition(theParent).VertexRefIds; - } - - static const BRepGraphInc::VertexRef& Ref(const BRepGraph& theGraph, const RefId theRefId) - { - return theGraph.Refs().Vertices().Entry(theRefId); - } - - static ChildId ChildIdOf(const BRepGraph&, const BRepGraphInc::VertexRef& theRef) - { - return theRef.VertexDefId; - } - - static const ChildDef& Child(const BRepGraph& theGraph, const ChildId theChildId) - { - return theGraph.Topo().Vertices().Definition(theChildId); - } -}; - +//! Traits for iterating over coedge children of a wire (direct, no ref indirection). struct CoEdgeOfWireTraits : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().Wires().Nb()) - && !theGraph.Topo().Wires().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().Wires().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Wires().Definition(theParent).CoEdgeRefIds; + return theGraph.Topo().Wires().Relations(theParent).CoEdgeIds; } - static const BRepGraphInc::CoEdgeRef& Ref(const BRepGraph& theGraph, const RefId theRefId) + static const BRepGraphInc::CoEdgeDef& Ref(const BRepGraph& theGraph, const RefId theRefId) { - return theGraph.Refs().CoEdges().Entry(theRefId); - } - - static ChildId ChildIdOf(const BRepGraph&, const BRepGraphInc::CoEdgeRef& theRef) - { - return theRef.CoEdgeDefId; + return theGraph.Topo().CoEdges().Definition(theRefId); } static const ChildDef& Child(const BRepGraph& theGraph, const ChildId theChildId) @@ -263,43 +180,37 @@ struct CoEdgeOfWireTraits : public BaseTraits { static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().Wires().Nb()) - && !theGraph.Topo().Wires().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().Wires().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Wires().Definition(theParent).CoEdgeRefIds; + return theGraph.Topo().Wires().Relations(theParent).CoEdgeIds; } - static const BRepGraphInc::CoEdgeRef& Ref(const BRepGraph& theGraph, const RefId theRefId) + static const BRepGraphInc::CoEdgeDef& Ref(const BRepGraph& theGraph, const RefId theRefId) { - return theGraph.Refs().CoEdges().Entry(theRefId); + return theGraph.Topo().CoEdges().Definition(theRefId); } - static ChildId ChildIdOf(const BRepGraph& theGraph, const BRepGraphInc::CoEdgeRef& theRef) + static ChildId ChildIdOf(const BRepGraph& theGraph, const BRepGraphInc::CoEdgeDef& theRef) { - const BRepGraph_CoEdgeId aCoEdgeId = theRef.CoEdgeDefId; - if (!aCoEdgeId.IsValid(theGraph.Topo().CoEdges().Nb())) + const BRepGraph_EdgeId aEdgeId = theRef.ChildEdgeId; + if (!aEdgeId.IsValid(theGraph.Topo().Edges().Nb()) || aEdgeId.IsRemoved(theGraph)) { return ChildId(); } - - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(aCoEdgeId); - if (aCoEdge.IsRemoved) - { - return ChildId(); - } - return aCoEdge.EdgeDefId; + return aEdgeId; } static const ChildDef& Child(const BRepGraph& theGraph, const ChildId theChildId) @@ -308,6 +219,7 @@ struct EdgeOfWireTraits : public BaseTraits& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().CompSolids().Definition(theParent).SolidRefIds; + return theGraph.Topo().CompSolids().Relations(theParent).SolidRefIds; } static const BRepGraphInc::SolidRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -333,7 +244,7 @@ struct SolidOfCompSolidTraits : public BaseTraits -{ - static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) - { - return theParent.IsValid(theGraph.Topo().Solids().Nb()) - && !theGraph.Topo().Solids().Definition(theParent).IsRemoved; - } - - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, - const ParentId theParent) - { - return theGraph.Topo().Solids().Definition(theParent).AuxChildRefIds; - } - - static const BRepGraphInc::ChildRef& Ref(const BRepGraph& theGraph, const RefId theRefId) - { - return theGraph.Refs().Children().Entry(theRefId); - } - - static ChildId ChildIdOf(const BRepGraph&, const BRepGraphInc::ChildRef& theRef) - { - return theRef.ChildDefId; - } - - static const ChildDef& Child(const BRepGraph& theGraph, const ChildId theChildId) - { - return *theGraph.Topo().Gen().TopoEntity(theChildId); - } -}; - +//! Traits for iterating over child nodes of a compound. struct ChildOfCompoundTraits : public BaseTraits& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Compounds().Definition(theParent).ChildRefIds; + return theGraph.Topo().Compounds().Relations(theParent).ChildRefIds; } static const BRepGraphInc::ChildRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -401,7 +278,7 @@ struct ChildOfCompoundTraits : public BaseTraits& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Products().Definition(theParent).OccurrenceRefIds; + return theGraph.Topo().Products().Relations(theParent).OccurrenceRefIds; } static const BRepGraphInc::OccurrenceRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -435,7 +312,7 @@ struct OccurrenceOfProductTraits : public BaseTraits(myRefIds->Size()); + if constexpr (std::is_convertible_v) + { + myNbRefs = theGraph.Topo().Gen().Nb(BRepGraph_NodeId(RefId()).NodeKind); + } + else + { + myNbRefs = theGraph.Refs().Gen().Nb(BRepGraph_RefId(RefId()).RefKind); + } + + if constexpr (TraitsT::THE_IS_DIRECT) + { + myNbChildren = myNbRefs; + } + else + { + if constexpr (!std::is_same_v) + { + myNbChildren = theGraph.Topo().Gen().Nb(BRepGraph_NodeId(ChildId()).NodeKind); + } + } skipRemoved(); } @@ -476,12 +373,27 @@ public: [[nodiscard]] ChildId CurrentId() const { - return TraitsT::ChildIdOf(myGraph, - TraitsT::Ref(myGraph, myRefIds->Value(static_cast(myIndex)))); + const RefId aRefId = myRefIds->Value(static_cast(myIndex)); + const typename TraitsT::RefEntry& aRef = TraitsT::Ref(myGraph, aRefId); + if constexpr (TraitsT::THE_IS_DIRECT) + { + return aRefId; + } + else + { + return TraitsT::ChildIdOf(myGraph, aRef); + } } [[nodiscard]] const ChildDef& Current() const { return TraitsT::Child(myGraph, CurrentId()); } + //! Returns the reference/coedge entry that carries the current child relation. + [[nodiscard]] RefId CurrentRefId() const + { + return myRefIds != nullptr && myIndex < myLength ? myRefIds->Value(static_cast(myIndex)) + : RefId(); + } + [[nodiscard]] uint32_t Index() const { return myIndex; } //! Returns an STL-compatible iterator for range-based for loops. @@ -498,13 +410,28 @@ private: { while (myRefIds != nullptr && myIndex < myLength) { - const typename TraitsT::RefEntry& aRef = - TraitsT::Ref(myGraph, myRefIds->Value(static_cast(myIndex))); - if (!aRef.IsRemoved) + const RefId aRefId = myRefIds->Value(static_cast(myIndex)); + if (aRefId.IsValid(myNbRefs) && !aRefId.IsRemoved(myGraph)) { - const BRepGraphInc::BaseDef* aChildDef = - childBaseDef(myGraph, TraitsT::ChildIdOf(myGraph, aRef)); - if (aChildDef != nullptr && !aChildDef->IsRemoved) + const ChildId aChildId = [&]() { + if constexpr (TraitsT::THE_IS_DIRECT) + { + return ChildId(aRefId); + } + else + { + const typename TraitsT::RefEntry& aRef = TraitsT::Ref(myGraph, aRefId); + return TraitsT::ChildIdOf(myGraph, aRef); + } + }(); + if constexpr (std::is_same_v) + { + if (myGraph.Topo().Gen().IsActive(aChildId)) + { + return; + } + } + else if (aChildId.IsValid(myNbChildren) && !aChildId.IsRemoved(myGraph)) { return; } @@ -514,14 +441,16 @@ private: } const BRepGraph& myGraph; - const NCollection_DynamicArray* myRefIds = nullptr; - uint32_t myIndex = 0; - uint32_t myLength = 0; + const NCollection_LinearVector* myRefIds = nullptr; + uint32_t myIndex = 0; + uint32_t myLength = 0; + uint32_t myNbRefs = 0; + uint32_t myNbChildren = 0; }; -//! @brief Direct active vertex children of an edge. +//! @brief Direct active boundary vertex children of an edge. //! -//! Iteration order is start vertex, end vertex, then internal/external vertices. +//! Iteration order is start vertex, then end vertex. class DefsVertexOfEdge { public: @@ -531,14 +460,15 @@ public: DefsVertexOfEdge(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdgeId) : myGraph(theGraph) { - if (!theEdgeId.IsValid(theGraph.Topo().Edges().Nb()) - || theGraph.Topo().Edges().Definition(theEdgeId).IsRemoved) + if (!theEdgeId.IsValid(theGraph.Topo().Edges().Nb()) || theEdgeId.IsRemoved(theGraph)) { return; } - myEdge = &theGraph.Topo().Edges().Definition(theEdgeId); - myLength = 2u + static_cast(myEdge->InternalVertexRefIds.Size()); + myEdge = &theGraph.Topo().Edges().Definition(theEdgeId); + myLength = 2u; + myNbVertexRefs = theGraph.Refs().Vertices().Nb(); + myNbVertices = theGraph.Topo().Vertices().Nb(); skipRemoved(); } @@ -552,7 +482,7 @@ public: [[nodiscard]] ChildId CurrentId() const { - return myGraph.Refs().Vertices().Entry(currentRefId()).VertexDefId; + return myGraph.Refs().Vertices().Entry(currentRefId()).ChildVertexId; } [[nodiscard]] const ChildDef& Current() const @@ -560,6 +490,12 @@ public: return myGraph.Topo().Vertices().Definition(CurrentId()); } + //! Returns the start/end vertex reference entry that carries the current child relation. + [[nodiscard]] BRepGraph_VertexRefId CurrentRefId() const + { + return More() ? currentRefId() : BRepGraph_VertexRefId(); + } + [[nodiscard]] uint32_t Index() const { return myIndex; } //! Returns an STL-compatible iterator for range-based for loops. @@ -578,11 +514,7 @@ private: { return myEdge->StartVertexRefId; } - if (theIndex == 1) - { - return myEdge->EndVertexRefId; - } - return myEdge->InternalVertexRefIds.Value(static_cast(theIndex - 2)); + return myEdge->EndVertexRefId; } [[nodiscard]] BRepGraph_VertexRefId currentRefId() const { return refIdAt(myIndex); } @@ -592,27 +524,26 @@ private: while (myEdge != nullptr && myIndex < myLength) { const BRepGraph_VertexRefId aRefId = refIdAt(myIndex); - if (aRefId.IsValid()) + if (aRefId.IsValid(myNbVertexRefs) && !myGraph.Refs().Gen().IsRemoved(aRefId)) { const BRepGraphInc::VertexRef& aRef = myGraph.Refs().Vertices().Entry(aRefId); - if (!aRef.IsRemoved) + if (!aRef.ChildVertexId.IsValid(myNbVertices) || aRef.ChildVertexId.IsRemoved(myGraph)) { - const BRepGraphInc::BaseDef* aChildDef = - myGraph.Topo().Gen().TopoEntity(BRepGraph_NodeId(aRef.VertexDefId)); - if (aChildDef != nullptr && !aChildDef->IsRemoved) - { - return; - } + ++myIndex; + continue; } + return; } ++myIndex; } } const BRepGraph& myGraph; - const BRepGraphInc::EdgeDef* myEdge = nullptr; - uint32_t myIndex = 0; - uint32_t myLength = 0; + const BRepGraphInc::EdgeDef* myEdge = nullptr; + uint32_t myIndex = 0; + uint32_t myLength = 0; + uint32_t myNbVertexRefs = 0; + uint32_t myNbVertices = 0; }; } // namespace BRepGraph_DefsIterator @@ -621,18 +552,12 @@ using BRepGraph_DefsShellOfSolid = BRepGraph_DefsIterator::DefsOfParent; using BRepGraph_DefsFaceOfShell = BRepGraph_DefsIterator::DefsOfParent; -using BRepGraph_DefsChildOfShell = - BRepGraph_DefsIterator::DefsOfParent; using BRepGraph_DefsEdgeOfWire = BRepGraph_DefsIterator::DefsOfParent; using BRepGraph_DefsWireOfFace = BRepGraph_DefsIterator::DefsOfParent; -using BRepGraph_DefsVertexOfFace = - BRepGraph_DefsIterator::DefsOfParent; using BRepGraph_DefsCoEdgeOfWire = BRepGraph_DefsIterator::DefsOfParent; -using BRepGraph_DefsChildOfSolid = - BRepGraph_DefsIterator::DefsOfParent; using BRepGraph_DefsSolidOfCompSolid = BRepGraph_DefsIterator::DefsOfParent; using BRepGraph_DefsChildOfCompound = @@ -641,4 +566,4 @@ using BRepGraph_DefsOccurrenceOfProduct = BRepGraph_DefsIterator::DefsOfParent; using BRepGraph_DefsVertexOfEdge = BRepGraph_DefsIterator::DefsVertexOfEdge; -#endif // _BRepGraph_DefsIterator_HeaderFile \ No newline at end of file +#endif // _BRepGraph_DefsIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.cxx index ee3f520938..aeb4d6414d 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.cxx @@ -12,30 +12,33 @@ // 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 #include #include #include @@ -46,9 +49,33 @@ #include +#include "../BRepGraphInc/BRepGraphInc_WireOrder.pxx" + namespace { +template +bool containsRelationId(const NCollection_LinearVector& theIds, const IdT theId) +{ + for (const IdT& anId : theIds) + { + if (anId == theId) + { + return true; + } + } + return false; +} + +template +void appendUniqueRelationId(NCollection_LinearVector& theIds, const IdT theId) +{ + if (!containsRelationId(theIds, theId)) + { + theIds.Append(theId); + } +} + bool isValidOccurrenceChildKind(const BRepGraph_NodeId::Kind theNodeKind) { return theNodeKind == BRepGraph_NodeId::Kind::Product @@ -75,23 +102,53 @@ bool hasOtherActiveParent(const BRepGraph& theGraph, return false; } -void removeFromRootProducts(NCollection_DynamicArray& theRoots, +void removeFromRootProducts(NCollection_LinearVector& theRoots, const BRepGraph_ProductId theProduct) { - NCollection_DynamicArray aFiltered; - for (const BRepGraph_ProductId& aRoot : theRoots) + size_t aWriteIdx = 0; + for (size_t aReadIdx = 0; aReadIdx < theRoots.Size(); ++aReadIdx) { + const BRepGraph_ProductId aRoot = theRoots.Value(aReadIdx); if (aRoot != theProduct) { - aFiltered.Append(aRoot); + if (aWriteIdx != aReadIdx) + { + theRoots.ChangeValue(aWriteIdx) = aRoot; + } + ++aWriteIdx; } } - theRoots = std::move(aFiltered); + while (theRoots.Size() > aWriteIdx) + { + theRoots.EraseLast(); + } +} + +void clearCoEdgeFaceScopedRepresentations(BRepGraphInc_Storage& theStorage, + BRepGraphInc::CoEdgeDef& theCoEdge) +{ + if (theCoEdge.Curve2DRepId.IsValid(theStorage.NbCoEdgeCurves2D())) + { + theStorage.MarkRemoved(theCoEdge.Curve2DRepId); + } + theCoEdge.Curve2DRepId = BRepGraph_CoEdgeCurve2DRepId(); + + if (theCoEdge.Polygon2DRepId.IsValid(theStorage.NbCoEdgePolygons2D())) + { + theStorage.MarkRemoved(theCoEdge.Polygon2DRepId); + } + theCoEdge.Polygon2DRepId = BRepGraph_CoEdgePolygon2DRepId(); + + if (theCoEdge.PolygonOnTriRepId.IsValid(theStorage.NbCoEdgePolygonsOnTri())) + { + theStorage.MarkRemoved(theCoEdge.PolygonOnTriRepId); + } + theCoEdge.PolygonOnTriRepId = BRepGraph_CoEdgePolygonOnTriRepId(); } BRepGraph_NodeId refChildNode(const BRepGraph& theGraph, const BRepGraph_RefId theRef) { - return theGraph.Refs().ChildNode(theRef); + return theGraph.Refs().Gen().ChildNode(theRef); } bool hasAnyActiveUsage(const BRepGraph& theGraph, const BRepGraph_NodeId theChild) @@ -101,7 +158,7 @@ bool hasAnyActiveUsage(const BRepGraph& theGraph, const BRepGraph_NodeId theChil return false; } - // DirectParents enumerates every active parent via the reverse index across + // DirectParents enumerates every active parent via relation storage across // all ref kinds plus the structural Edge->CoEdge and Product->Occurrence // links; "any direct parent exists" is exactly "any active usage exists". BRepGraph_ParentExplorer anExp(theGraph, @@ -124,6 +181,137 @@ RefIdT findOrderedRef(const NCollection_DynamicArray& theRefIds, const R return RefIdT(); } +template +RefIdT findOrderedRef(const NCollection_LinearVector& theRefIds, const RefIdT theRefId) +{ + for (const RefIdT& aRefId : theRefIds) + { + if (aRefId == theRefId) + { + return theRefId; + } + } + return RefIdT(); +} + +template +bool containsOrderedRef(const NCollection_DynamicArray& theRefIds, const RefIdT theRefId) +{ + return findOrderedRef(theRefIds, theRefId).IsValid(); +} + +template +bool containsOrderedRef(const NCollection_LinearVector& theRefIds, const RefIdT theRefId) +{ + return findOrderedRef(theRefIds, theRefId).IsValid(); +} + +bool isOccurrenceDefOwnedByAnyProduct(const BRepGraphInc_Storage& theStorage, + const BRepGraph_OccurrenceId theOccurrence) +{ + if (!theOccurrence.IsValid(theStorage.NbOccurrences())) + { + return false; + } + for (BRepGraph_ProductId aProductId = BRepGraph_ProductId::Start(); + aProductId.IsValid(theStorage.NbProducts()); + ++aProductId) + { + if (!aProductId.IsValid(theStorage.NbProducts())) + { + continue; + } + if (theStorage.IsRemoved(aProductId)) + { + continue; + } + for (const BRepGraph_OccurrenceRefId& aRefId : + theStorage.ProductRelations(aProductId).OccurrenceRefIds) + { + if (!aRefId.IsValid(theStorage.NbOccurrenceRefs())) + { + continue; + } + if (!theStorage.IsRemoved(aRefId) + && theStorage.OccurrenceRef(aRefId).ChildOccurrenceId == theOccurrence) + { + return true; + } + } + } + return false; +} + +bool productHasOccurrenceDef(const BRepGraphInc_Storage& theStorage, + const BRepGraph_ProductId theProduct, + const BRepGraph_OccurrenceId theOccurrence) +{ + if (!theProduct.IsValid(theStorage.NbProducts()) + || !theOccurrence.IsValid(theStorage.NbOccurrences())) + { + return false; + } + if (theStorage.IsRemoved(theProduct)) + { + return false; + } + for (const BRepGraph_OccurrenceRefId& aRefId : + theStorage.ProductRelations(theProduct).OccurrenceRefIds) + { + if (!aRefId.IsValid(theStorage.NbOccurrenceRefs())) + { + continue; + } + if (!theStorage.IsRemoved(aRefId) + && theStorage.OccurrenceRef(aRefId).ChildOccurrenceId == theOccurrence) + { + return true; + } + } + return false; +} + +template +void forEachId(const NCollection_DynamicArray& theIds, FuncT&& theFunc) +{ + for (const IdT& anId : theIds) + { + theFunc(anId); + } +} + +template +void forEachId(const NCollection_LinearVector& theIds, FuncT&& theFunc) +{ + for (const IdT& anId : theIds) + { + theFunc(anId); + } +} + +bool wireHasActiveEdgeThroughAnotherCoEdge(const BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theWire, + const BRepGraph_EdgeId theEdge, + const BRepGraph_CoEdgeId theExcludedCoEdge) +{ + if (!theWire.IsValid(theStorage.NbWires()) || theStorage.IsRemoved(theWire)) + { + return false; + } + for (const BRepGraph_CoEdgeId& aCoEdgeId : theStorage.WireRelations(theWire).CoEdgeIds) + { + if (aCoEdgeId == theExcludedCoEdge || !aCoEdgeId.IsValid(theStorage.NbCoEdges())) + { + continue; + } + if (!theStorage.IsRemoved(aCoEdgeId) && theStorage.CoEdge(aCoEdgeId).ChildEdgeId == theEdge) + { + return true; + } + } + return false; +} + template void eraseOrderedRef(const RefIdT theRefId, NCollection_DynamicArray& theRefIds) { @@ -144,24 +332,236 @@ void eraseOrderedRef(const RefIdT theRefId, NCollection_DynamicArray& th } } +bool isRefOwnedByAnyParent(const BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexRefId theRefId) +{ + if (!theRefId.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(theRefId)) + { + return false; + } + const BRepGraph_VertexId aVertexId = theStorage.VertexRef(theRefId).ChildVertexId; + if (!aVertexId.IsValid(theStorage.NbVertices())) + { + return false; + } + for (const BRepGraph_EdgeId& anEdgeId : theStorage.VertexRelations(aVertexId).EdgeIds) + { + if (!anEdgeId.IsValid(theStorage.NbEdges()) || theStorage.IsRemoved(anEdgeId)) + { + continue; + } + const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(anEdgeId); + if (anEdge.StartVertexRefId == theRefId || anEdge.EndVertexRefId == theRefId) + { + return true; + } + } + return false; +} + +bool isRefOwnedByAnyParent(const BRepGraphInc_Storage& theStorage, + const BRepGraph_WireRefId theRefId) +{ + if (!theRefId.IsValid(theStorage.NbWireRefs()) || theStorage.IsRemoved(theRefId)) + { + return false; + } + const BRepGraph_FaceId aParentId = theStorage.WireRef(theRefId).ParentFaceId; + return aParentId.IsValid(theStorage.NbFaces()) && !theStorage.IsRemoved(aParentId) + && containsRelationId(theStorage.FaceRelations(aParentId).WireRefIds, theRefId); +} + +bool isRefOwnedByAnyParent(const BRepGraphInc_Storage& theStorage, + const BRepGraph_FaceRefId theRefId) +{ + if (!theRefId.IsValid(theStorage.NbFaceRefs()) || theStorage.IsRemoved(theRefId)) + { + return false; + } + const BRepGraph_ShellId aParentId = theStorage.FaceRef(theRefId).ParentShellId; + return aParentId.IsValid(theStorage.NbShells()) && !theStorage.IsRemoved(aParentId) + && containsRelationId(theStorage.ShellRelations(aParentId).FaceRefIds, theRefId); +} + +bool isRefOwnedByAnyParent(const BRepGraphInc_Storage& theStorage, + const BRepGraph_ShellRefId theRefId) +{ + if (!theRefId.IsValid(theStorage.NbShellRefs()) || theStorage.IsRemoved(theRefId)) + { + return false; + } + const BRepGraph_SolidId aParentId = theStorage.ShellRef(theRefId).ParentSolidId; + return aParentId.IsValid(theStorage.NbSolids()) && !theStorage.IsRemoved(aParentId) + && containsRelationId(theStorage.SolidRelations(aParentId).ShellRefIds, theRefId); +} + +bool isRefOwnedByAnyParent(const BRepGraphInc_Storage& theStorage, + const BRepGraph_SolidRefId theRefId) +{ + if (!theRefId.IsValid(theStorage.NbSolidRefs()) || theStorage.IsRemoved(theRefId)) + { + return false; + } + const BRepGraph_CompSolidId aParentId = theStorage.SolidRef(theRefId).ParentCompSolidId; + return aParentId.IsValid(theStorage.NbCompSolids()) && !theStorage.IsRemoved(aParentId) + && containsRelationId(theStorage.CompSolidRelations(aParentId).SolidRefIds, theRefId); +} + +bool isRefOwnedByAnyParent(const BRepGraphInc_Storage& theStorage, + const BRepGraph_ChildRefId theRefId) +{ + if (!theRefId.IsValid(theStorage.NbChildRefs()) || theStorage.IsRemoved(theRefId)) + { + return false; + } + const BRepGraph_CompoundId aParentId = theStorage.ChildRef(theRefId).ParentCompoundId; + return aParentId.IsValid(theStorage.NbCompounds()) && !theStorage.IsRemoved(aParentId) + && containsRelationId(theStorage.CompoundRelations(aParentId).ChildRefIds, theRefId); +} + +bool isRefOwnedByAnyParent(const BRepGraphInc_Storage& theStorage, + const BRepGraph_OccurrenceRefId theRefId) +{ + if (!theRefId.IsValid(theStorage.NbOccurrenceRefs()) || theStorage.IsRemoved(theRefId)) + { + return false; + } + const BRepGraph_ProductId aParentId = theStorage.OccurrenceRef(theRefId).ParentProductId; + return aParentId.IsValid(theStorage.NbProducts()) && !theStorage.IsRemoved(aParentId) + && containsRelationId(theStorage.ProductRelations(aParentId).OccurrenceRefIds, theRefId); +} + +bool isExpectedParentKindForRef(const BRepGraph_NodeId theParent, + const BRepGraph_RefId::Kind theRefKind) +{ + switch (theRefKind) + { + case BRepGraph_RefId::Kind::Vertex: + return theParent.NodeKind == BRepGraph_NodeId::Kind::Edge; + case BRepGraph_RefId::Kind::Wire: + return theParent.NodeKind == BRepGraph_NodeId::Kind::Face; + case BRepGraph_RefId::Kind::Face: + return theParent.NodeKind == BRepGraph_NodeId::Kind::Shell; + case BRepGraph_RefId::Kind::Shell: + return theParent.NodeKind == BRepGraph_NodeId::Kind::Solid; + case BRepGraph_RefId::Kind::Solid: + return theParent.NodeKind == BRepGraph_NodeId::Kind::CompSolid; + case BRepGraph_RefId::Kind::Child: + return theParent.NodeKind == BRepGraph_NodeId::Kind::Compound; + case BRepGraph_RefId::Kind::Occurrence: + return theParent.NodeKind == BRepGraph_NodeId::Kind::Product; + default: + return false; + } +} + +bool isRefInRange(const BRepGraphInc_Storage& theStorage, const BRepGraph_RefId theRef) +{ + switch (theRef.RefKind) + { + case BRepGraph_RefId::Kind::Vertex: + return BRepGraph_VertexRefId(theRef).IsValid(theStorage.NbVertexRefs()); + case BRepGraph_RefId::Kind::Wire: + return BRepGraph_WireRefId(theRef).IsValid(theStorage.NbWireRefs()); + case BRepGraph_RefId::Kind::Face: + return BRepGraph_FaceRefId(theRef).IsValid(theStorage.NbFaceRefs()); + case BRepGraph_RefId::Kind::Shell: + return BRepGraph_ShellRefId(theRef).IsValid(theStorage.NbShellRefs()); + case BRepGraph_RefId::Kind::Solid: + return BRepGraph_SolidRefId(theRef).IsValid(theStorage.NbSolidRefs()); + case BRepGraph_RefId::Kind::Child: + return BRepGraph_ChildRefId(theRef).IsValid(theStorage.NbChildRefs()); + case BRepGraph_RefId::Kind::Occurrence: + return BRepGraph_OccurrenceRefId(theRef).IsValid(theStorage.NbOccurrenceRefs()); + default: + return false; + } +} + +bool detachRefRelations(BRepGraphInc_Storage& theStorage, const BRepGraph_RefId theRef) +{ + switch (theRef.RefKind) + { + case BRepGraph_RefId::Kind::Wire: { + const BRepGraph_WireRefId aRefId(theRef); + const BRepGraphInc::WireRef& aRef = theStorage.WireRef(aRefId); + return theStorage.DetachWireFromFace(aRef.ParentFaceId, aRefId); + } + case BRepGraph_RefId::Kind::Face: { + const BRepGraph_FaceRefId aRefId(theRef); + const BRepGraphInc::FaceRef& aRef = theStorage.FaceRef(aRefId); + return theStorage.DetachFaceFromShell(aRef.ParentShellId, aRefId); + } + case BRepGraph_RefId::Kind::Shell: { + const BRepGraph_ShellRefId aRefId(theRef); + const BRepGraphInc::ShellRef& aRef = theStorage.ShellRef(aRefId); + return theStorage.DetachShellFromSolid(aRef.ParentSolidId, aRefId); + } + case BRepGraph_RefId::Kind::Solid: { + const BRepGraph_SolidRefId aRefId(theRef); + const BRepGraphInc::SolidRef& aRef = theStorage.SolidRef(aRefId); + return theStorage.DetachSolidFromCompSolid(aRef.ParentCompSolidId, aRefId); + } + case BRepGraph_RefId::Kind::Child: { + const BRepGraph_ChildRefId aRefId(theRef); + const BRepGraphInc::ChildRef& aRef = theStorage.ChildRef(aRefId); + return theStorage.DetachChildFromCompound(aRef.ParentCompoundId, aRefId); + } + case BRepGraph_RefId::Kind::Occurrence: { + const BRepGraph_OccurrenceRefId aRefId(theRef); + const BRepGraphInc::OccurrenceRef& aRef = theStorage.OccurrenceRef(aRefId); + return theStorage.DetachOccurrenceFromProduct(aRef.ParentProductId, aRefId); + } + default: + return true; + } +} + template -bool detachOrderedParentRef(BRepGraph& theGraph, - const RefIdT theRefId, - NCollection_DynamicArray& theParentRefIds, - const BRepGraph_NodeId theChildNode, - const bool theToPruneOrphanedChild) +bool detachOrderedParentRef(BRepGraph& theGraph, + BRepGraphInc_Storage& theStorage, + const RefIdT theRefId, + const NCollection_LinearVector& theParentRefIds, + const BRepGraph_NodeId theChildNode, + const bool theToPruneOrphanedChild) { if (!findOrderedRef(theParentRefIds, theRefId).IsValid()) { return false; } - if (!theGraph.Editor().Gen().RemoveRef(theRefId)) + detachRefRelations(theStorage, BRepGraph_RefId(theRefId)); + if (!isRefOwnedByAnyParent(theStorage, theRefId) && !theGraph.Editor().Gen().RemoveRef(theRefId)) { return false; } - eraseOrderedRef(theRefId, theParentRefIds); + if (theToPruneOrphanedChild && theChildNode.IsValid() + && !hasAnyActiveUsage(theGraph, theChildNode)) + { + theGraph.Editor().Gen().RemoveSubgraph(theChildNode); + } + return true; +} + +template +bool detachOrderedParentRef(BRepGraph& theGraph, + BRepGraphInc_Storage& theStorage, + const RefIdT theRefId, + const NCollection_DynamicArray& theParentRefIds, + const BRepGraph_NodeId theChildNode, + const bool theToPruneOrphanedChild) +{ + if (!findOrderedRef(theParentRefIds, theRefId).IsValid()) + { + return false; + } + + detachRefRelations(theStorage, BRepGraph_RefId(theRefId)); + if (!isRefOwnedByAnyParent(theStorage, theRefId) && !theGraph.Editor().Gen().RemoveRef(theRefId)) + { + return false; + } if (theToPruneOrphanedChild && theChildNode.IsValid() && !hasAnyActiveUsage(theGraph, theChildNode)) @@ -242,49 +642,317 @@ static bool isNodeIndexInRange(const BRepGraphInc_Storage& theStorage, //================================================================================================= -static const BRepGraphInc::BaseDef* topoEntity(const BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theNode) +static bool isActiveNode(const BRepGraphInc_Storage& theStorage, const BRepGraph_NodeId theNode) { if (!isNodeIndexInRange(theStorage, theNode)) { - return nullptr; + return false; } - switch (theNode.NodeKind) { case BRepGraph_NodeId::Kind::Vertex: - return &theStorage.Vertex(BRepGraph_VertexId(theNode)); + return !theStorage.IsRemoved(BRepGraph_VertexId(theNode)); case BRepGraph_NodeId::Kind::Edge: - return &theStorage.Edge(BRepGraph_EdgeId(theNode)); + return !theStorage.IsRemoved(BRepGraph_EdgeId(theNode)); case BRepGraph_NodeId::Kind::CoEdge: - return &theStorage.CoEdge(BRepGraph_CoEdgeId(theNode)); + return !theStorage.IsRemoved(BRepGraph_CoEdgeId(theNode)); case BRepGraph_NodeId::Kind::Wire: - return &theStorage.Wire(BRepGraph_WireId(theNode)); + return !theStorage.IsRemoved(BRepGraph_WireId(theNode)); case BRepGraph_NodeId::Kind::Face: - return &theStorage.Face(BRepGraph_FaceId(theNode)); + return !theStorage.IsRemoved(BRepGraph_FaceId(theNode)); case BRepGraph_NodeId::Kind::Shell: - return &theStorage.Shell(BRepGraph_ShellId(theNode)); + return !theStorage.IsRemoved(BRepGraph_ShellId(theNode)); case BRepGraph_NodeId::Kind::Solid: - return &theStorage.Solid(BRepGraph_SolidId(theNode)); + return !theStorage.IsRemoved(BRepGraph_SolidId(theNode)); case BRepGraph_NodeId::Kind::Compound: - return &theStorage.Compound(BRepGraph_CompoundId(theNode)); + return !theStorage.IsRemoved(BRepGraph_CompoundId(theNode)); case BRepGraph_NodeId::Kind::CompSolid: - return &theStorage.CompSolid(BRepGraph_CompSolidId(theNode)); + return !theStorage.IsRemoved(BRepGraph_CompSolidId(theNode)); case BRepGraph_NodeId::Kind::Product: - return &theStorage.Product(BRepGraph_ProductId(theNode)); + return !theStorage.IsRemoved(BRepGraph_ProductId(theNode)); case BRepGraph_NodeId::Kind::Occurrence: - return &theStorage.Occurrence(BRepGraph_OccurrenceId(theNode)); + return !theStorage.IsRemoved(BRepGraph_OccurrenceId(theNode)); } - - return nullptr; + return false; } //================================================================================================= -static bool isActiveNode(const BRepGraphInc_Storage& theStorage, const BRepGraph_NodeId theNode) +static bool coEdgeOrientedVertices(const BRepGraphInc_Storage& theStorage, + const BRepGraph_CoEdgeId theCoEdgeId, + BRepGraph_VertexId& theStartVertex, + BRepGraph_VertexId& theEndVertex) { - const BRepGraphInc::BaseDef* aDef = topoEntity(theStorage, theNode); - return aDef != nullptr && !aDef->IsRemoved; + if (!isActiveNode(theStorage, theCoEdgeId)) + { + return false; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(theCoEdgeId); + if (!isActiveNode(theStorage, aCoEdge.ChildEdgeId)) + { + return false; + } + + const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(aCoEdge.ChildEdgeId); + const BRepGraph_VertexRefId aStartRef = + (aCoEdge.Orientation == TopAbs_FORWARD) ? anEdge.StartVertexRefId : anEdge.EndVertexRefId; + const BRepGraph_VertexRefId anEndRef = + (aCoEdge.Orientation == TopAbs_FORWARD) ? anEdge.EndVertexRefId : anEdge.StartVertexRefId; + if (!aStartRef.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(aStartRef) + || !anEndRef.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(anEndRef)) + { + return false; + } + + theStartVertex = theStorage.VertexRef(aStartRef).ChildVertexId; + theEndVertex = theStorage.VertexRef(anEndRef).ChildVertexId; + return isActiveNode(theStorage, theStartVertex) && isActiveNode(theStorage, theEndVertex); +} + +//================================================================================================= + +static bool edgeOrientedVertices(const BRepGraphInc_Storage& theStorage, + const BRepGraph_EdgeId theEdgeId, + const TopAbs_Orientation theOrientation, + BRepGraph_VertexId& theStartVertex, + BRepGraph_VertexId& theEndVertex) +{ + if (!isActiveNode(theStorage, theEdgeId)) + { + return false; + } + + const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(theEdgeId); + const BRepGraph_VertexRefId aStartRef = + (theOrientation == TopAbs_FORWARD) ? anEdge.StartVertexRefId : anEdge.EndVertexRefId; + const BRepGraph_VertexRefId anEndRef = + (theOrientation == TopAbs_FORWARD) ? anEdge.EndVertexRefId : anEdge.StartVertexRefId; + if (!aStartRef.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(aStartRef) + || !anEndRef.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(anEndRef)) + { + return false; + } + + theStartVertex = theStorage.VertexRef(aStartRef).ChildVertexId; + theEndVertex = theStorage.VertexRef(anEndRef).ChildVertexId; + return isActiveNode(theStorage, theStartVertex) && isActiveNode(theStorage, theEndVertex); +} + +//================================================================================================= + +static bool coEdgeOrientedVerticesAfterReplacement(const BRepGraphInc_Storage& theStorage, + const BRepGraph_CoEdgeId theCoEdgeId, + const BRepGraph_EdgeId theOldEdge, + const BRepGraph_EdgeId theNewEdge, + const bool theReversed, + BRepGraph_VertexId& theStartVertex, + BRepGraph_VertexId& theEndVertex) +{ + if (!isActiveNode(theStorage, theCoEdgeId)) + { + return false; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(theCoEdgeId); + if (aCoEdge.ChildEdgeId != theOldEdge) + { + return coEdgeOrientedVertices(theStorage, theCoEdgeId, theStartVertex, theEndVertex); + } + + TopAbs_Orientation anOrientation = static_cast(aCoEdge.Orientation); + if (theReversed) + { + anOrientation = TopAbs::Reverse(anOrientation); + } + return edgeOrientedVertices(theStorage, theNewEdge, anOrientation, theStartVertex, theEndVertex); +} + +//================================================================================================= + +static BRepGraph::EditorView::WireOps::CoEdgeOrderStatus toEditorCoEdgeOrderStatus( + const BRepGraphInc_Storage::WireCoEdgeOrderStatus theStatus) +{ + using EditorStatus = BRepGraph::EditorView::WireOps::CoEdgeOrderStatus; + using StorageStatus = BRepGraphInc_Storage::WireCoEdgeOrderStatus; + switch (theStatus) + { + case StorageStatus::Connected: + return EditorStatus::Ready; + case StorageStatus::Reordered: + return EditorStatus::Reordered; + case StorageStatus::ToleranceOrdered: + case StorageStatus::Partial: + case StorageStatus::InvalidInput: + return EditorStatus::Disconnected; + } + return EditorStatus::Disconnected; +} + +//================================================================================================= + +static BRepGraph::EditorView::WireOps::CoEdgeOrderStatus preCheckCoEdgeOrder( + const BRepGraphInc_Storage& theStorage, + const NCollection_Array1& theInput, + const BRepGraph_WireId theExpectedWire, + const bool theRequireFree, + const bool theRequirePermutation, + NCollection_LinearVector& theOrdered) +{ + using CoEdgeOrderStatus = BRepGraph::EditorView::WireOps::CoEdgeOrderStatus; + + theOrdered.Clear(false); + if (theInput.Size() == 0) + { + return CoEdgeOrderStatus::Empty; + } + if (!BRepGraphInc_WireOrder::CoEdgeIdsAreUnique(theInput)) + { + return CoEdgeOrderStatus::DuplicateCoEdge; + } + + const NCollection_LinearVector* anOldCoEdges = nullptr; + if (theExpectedWire.IsValid()) + { + if (!theExpectedWire.IsValid(theStorage.NbWires()) || theStorage.IsRemoved(theExpectedWire)) + { + return CoEdgeOrderStatus::InvalidWire; + } + anOldCoEdges = &theStorage.WireRelations(theExpectedWire).CoEdgeIds; + if (theRequirePermutation && theInput.Size() != anOldCoEdges->Size()) + { + return CoEdgeOrderStatus::SizeMismatch; + } + } + + for (const BRepGraph_CoEdgeId& aCoEdgeId : theInput) + { + if (!isActiveNode(theStorage, aCoEdgeId)) + { + return CoEdgeOrderStatus::InvalidCoEdge; + } + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aCoEdgeId); + if (theRequireFree && aCoEdge.ParentWireId.IsValid()) + { + return CoEdgeOrderStatus::CoEdgeAlreadyBound; + } + if (theExpectedWire.IsValid() && aCoEdge.ParentWireId != theExpectedWire) + { + return CoEdgeOrderStatus::CoEdgeNotOwnedByWire; + } + BRepGraph_VertexId aStart; + BRepGraph_VertexId anEnd; + if (!coEdgeOrientedVertices(theStorage, aCoEdgeId, aStart, anEnd)) + { + return CoEdgeOrderStatus::InvalidCoEdge; + } + } + + if (theRequirePermutation && anOldCoEdges != nullptr) + { + NCollection_LinearVector aMatched; + for (size_t anIdx = 0; anIdx < anOldCoEdges->Size(); ++anIdx) + { + aMatched.Append(0); + } + + for (const BRepGraph_CoEdgeId& aNewCoEdgeId : theInput) + { + bool isFound = false; + for (size_t anOldIdx = 0; anOldIdx < anOldCoEdges->Size(); ++anOldIdx) + { + if (aMatched.Value(anOldIdx) == 0 && anOldCoEdges->Value(anOldIdx) == aNewCoEdgeId) + { + aMatched.ChangeValue(anOldIdx) = 1; + isFound = true; + break; + } + } + if (!isFound) + { + return CoEdgeOrderStatus::NotPermutation; + } + } + + bool isSameOrder = true; + for (size_t anIdx = 0; anIdx < anOldCoEdges->Size(); ++anIdx) + { + if (anOldCoEdges->Value(anIdx) != theInput.Value(static_cast(anIdx))) + { + isSameOrder = false; + break; + } + } + if (isSameOrder && theStorage.ValidateWireCoEdgeOrder(theExpectedWire)) + { + for (const BRepGraph_CoEdgeId& aCoEdgeId : theInput) + { + theOrdered.Append(aCoEdgeId); + } + return CoEdgeOrderStatus::AlreadyCurrent; + } + } + + return toEditorCoEdgeOrderStatus( + BRepGraphInc_WireOrder::BuildCoEdgeOrder(theStorage, BRepGraph_WireId(), theInput, theOrdered)); +} + +//================================================================================================= + +static bool rotateConnectedCoEdgesAfterRemoval( + const BRepGraphInc_Storage& theStorage, + const NCollection_LinearVector& theCurrentCoEdges, + const BRepGraph_CoEdgeId theRemovedCoEdge, + NCollection_LinearVector& theOrderedRemaining) +{ + NCollection_LinearVector aRemaining; + for (const BRepGraph_CoEdgeId& aCoEdgeId : theCurrentCoEdges) + { + if (aCoEdgeId != theRemovedCoEdge) + { + aRemaining.Append(aCoEdgeId); + } + } + + if (aRemaining.Size() < 2) + { + for (const BRepGraph_CoEdgeId& aCoEdgeId : aRemaining) + { + theOrderedRemaining.Append(aCoEdgeId); + } + return true; + } + + for (size_t aStartIdx = 0; aStartIdx < aRemaining.Size(); ++aStartIdx) + { + bool isConnected = true; + BRepGraph_VertexId aPrevEnd; + for (size_t anOffset = 0; anOffset < aRemaining.Size(); ++anOffset) + { + const BRepGraph_CoEdgeId aCoEdgeId = + aRemaining.Value((aStartIdx + anOffset) % aRemaining.Size()); + BRepGraph_VertexId aStart; + BRepGraph_VertexId anEnd; + if (!coEdgeOrientedVertices(theStorage, aCoEdgeId, aStart, anEnd) + || (anOffset > 0 && aStart != aPrevEnd)) + { + isConnected = false; + break; + } + aPrevEnd = anEnd; + } + if (!isConnected) + { + continue; + } + + for (size_t anOffset = 0; anOffset < aRemaining.Size(); ++anOffset) + { + theOrderedRemaining.Append(aRemaining.Value((aStartIdx + anOffset) % aRemaining.Size())); + } + return true; + } + return false; } //================================================================================================= @@ -298,43 +966,12 @@ static bool isActiveTopologyNode(const BRepGraphInc_Storage& theStorage, //================================================================================================= -[[maybe_unused]] static bool isRepIndexInRange(const BRepGraphInc_Storage& theStorage, - const BRepGraph_RepId theRepId) -{ - if (!theRepId.IsValid()) - { - return false; - } - - switch (theRepId.RepKind) - { - case BRepGraph_RepId::Kind::Surface: - return theRepId.IsValid(theStorage.NbSurfaces()); - case BRepGraph_RepId::Kind::Curve3D: - return theRepId.IsValid(theStorage.NbCurves3D()); - case BRepGraph_RepId::Kind::Curve2D: - return theRepId.IsValid(theStorage.NbCurves2D()); - case BRepGraph_RepId::Kind::Triangulation: - return theRepId.IsValid(theStorage.NbTriangulations()); - case BRepGraph_RepId::Kind::Polygon3D: - return theRepId.IsValid(theStorage.NbPolygons3D()); - case BRepGraph_RepId::Kind::Polygon2D: - return theRepId.IsValid(theStorage.NbPolygons2D()); - case BRepGraph_RepId::Kind::PolygonOnTri: - return theRepId.IsValid(theStorage.NbPolygonsOnTri()); - } - - return false; -} - -//================================================================================================= - static void rebindCoEdgesForEdgeReplacement(BRepGraphInc_Storage& theStorage, const BRepGraph_EdgeId theSourceEdgeId, const BRepGraph_EdgeId theReplacementEdgeId) { - const NCollection_DynamicArray& aCoEdgeIdxs = - theStorage.ReverseIndex().CoEdgesOfEdgeRef(theSourceEdgeId); + const NCollection_LinearVector& aCoEdgeIdxs = + theStorage.EdgeRelations(theSourceEdgeId).CoEdgeIds; const uint32_t aNbCoEdges = static_cast(aCoEdgeIdxs.Size()); NCollection_LocalArray aCoEdgeSnapshot(aNbCoEdges); for (uint32_t aCoEdgeIdx = 0; aCoEdgeIdx < aNbCoEdges; ++aCoEdgeIdx) @@ -351,60 +988,23 @@ static void rebindCoEdgesForEdgeReplacement(BRepGraphInc_Storage& theStorage, } BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.ChangeCoEdge(aCoEdgeId); - if (aCoEdge.IsRemoved || aCoEdge.EdgeDefId != theSourceEdgeId) + if (theStorage.IsRemoved(aCoEdgeId) || aCoEdge.ChildEdgeId != theSourceEdgeId) { continue; } - theStorage.ChangeReverseIndex().UnbindEdgeFromCoEdge(theSourceEdgeId, aCoEdgeId); - aCoEdge.EdgeDefId = theReplacementEdgeId; - theStorage.ChangeReverseIndex().BindEdgeToCoEdge(theReplacementEdgeId, aCoEdgeId); + aCoEdge.ChildEdgeId = theReplacementEdgeId; + theStorage.RebindCoEdgeEdge(aCoEdgeId, theSourceEdgeId, theReplacementEdgeId); } } //================================================================================================= -//! True if no active CoEdgeRef in theWire (other than theExcludingRef) resolves to -//! a non-removed CoEdge that references theEdge. Used to gate Edge->Wire unbind -//! when a single CoEdgeRef is removed: deduped reverse entry must drop only when -//! the last sibling pointing at the same edge is gone. -static bool isLastEdgeUsageInWire(const BRepGraphInc_Storage& theStorage, - const BRepGraph_WireId theWire, - const BRepGraph_EdgeId theEdge, - const BRepGraph_CoEdgeRefId theExcludingRef) -{ - if (!theWire.IsValid(theStorage.NbWires()) || !theEdge.IsValid()) - { - return true; - } - const BRepGraphInc::WireDef& aWireDef = theStorage.Wire(theWire); - for (const BRepGraph_CoEdgeRefId& aRefId : aWireDef.CoEdgeRefIds) - { - if (!aRefId.IsValid(theStorage.NbCoEdgeRefs()) || aRefId == theExcludingRef) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef = theStorage.CoEdgeRef(aRefId); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aRef.CoEdgeDefId); - if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId == theEdge) - { - return false; - } - } - return true; -} - -//================================================================================================= - static void unbindCoEdgesOfRemovedEdge(BRepGraphInc_Storage& theStorage, const BRepGraph_EdgeId theEdgeId) { - const NCollection_DynamicArray& aCoEdgeIdxs = - theStorage.ReverseIndex().CoEdgesOfEdgeRef(theEdgeId); + const NCollection_LinearVector& aCoEdgeIdxs = + theStorage.EdgeRelations(theEdgeId).CoEdgeIds; const uint32_t aNbCoEdges = static_cast(aCoEdgeIdxs.Size()); NCollection_LocalArray aCoEdgeSnapshot(aNbCoEdges); for (uint32_t aCoEdgeIdx = 0; aCoEdgeIdx < aNbCoEdges; ++aCoEdgeIdx) @@ -420,10 +1020,11 @@ static void unbindCoEdgesOfRemovedEdge(BRepGraphInc_Storage& theStorage, continue; } - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aCoEdgeId); - if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId == theEdgeId) + BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.ChangeCoEdge(aCoEdgeId); + if (!theStorage.IsRemoved(aCoEdgeId) && aCoEdge.ChildEdgeId == theEdgeId) { - theStorage.ChangeReverseIndex().UnbindEdgeFromCoEdge(theEdgeId, aCoEdgeId); + aCoEdge.ChildEdgeId = BRepGraph_EdgeId(); + theStorage.RebindCoEdgeEdge(aCoEdgeId, theEdgeId, BRepGraph_EdgeId()); } } } @@ -501,51 +1102,32 @@ int cachedActiveByKind(const BRepGraphInc_Storage& theStorage, const BRepGraph_N return 0; } -[[maybe_unused]] const NCollection_DynamicArray& wireCoEdgeRefIds( - const BRepGraphInc_Storage& theStorage, - const BRepGraph_WireId theWireId) -{ - return theStorage.Wire(theWireId).CoEdgeRefIds; -} - //! Initialize a sub-edge definition produced by Split. //! Copies shared properties from the original edge and assigns boundary vertex ref ids. //! Vertex ref entries must already exist in storage; only their RefId indices are passed. -void initSubEdgeEntity(BRepGraphInc::EdgeDef& theSub, - const BRepGraph_Curve3DRepId theCurve3DRepId, - const double theTolerance, - const bool theSameParameter, - const BRepGraph_VertexRefId theStartVertexRefId, - const BRepGraph_VertexRefId theEndVertexRefId, - const double theParamFirst, - const double theParamLast) +void initSubEdgeEntity(BRepGraphInc::EdgeDef& theSub, + const BRepGraph_EdgeCurve3DRepId theCurve3DRepId, + const double theTolerance, + const BRepGraph_VertexRefId theStartVertexRefId, + const BRepGraph_VertexRefId theEndVertexRefId) { theSub.Curve3DRepId = theCurve3DRepId; theSub.Tolerance = theTolerance; - theSub.SameParameter = theSameParameter; - theSub.SameRange = false; - theSub.IsDegenerate = false; theSub.StartVertexRefId = theStartVertexRefId; theSub.EndVertexRefId = theEndVertexRefId; - theSub.ParamFirst = theParamFirst; - theSub.ParamLast = theParamLast; } //! Initialize a sub-CoEdge definition produced by Split. -void initSubCoEdgeEntity(BRepGraphInc::CoEdgeDef& theCE, - const BRepGraph_EdgeId theEdgeId, - const BRepGraph_FaceId theFaceId, - const TopAbs_Orientation theOrientation, - const BRepGraph_Curve2DRepId theCurve2DRepId, - const double theParamFirst, - const double theParamLast) +void initSubCoEdgeEntity(BRepGraphInc::CoEdgeDef& theCE, + const BRepGraph_EdgeId theEdgeId, + const BRepGraph_FaceId theFaceId, + const TopAbs_Orientation theOrientation, + const BRepGraph_CoEdgeCurve2DRepId theCurve2DRepId) { - theCE.EdgeDefId = theEdgeId; - theCE.FaceDefId = theFaceId; + theCE.ChildEdgeId = theEdgeId; + theCE.FaceId = theFaceId; theCE.Orientation = theOrientation; theCE.Curve2DRepId = theCurve2DRepId; - theCE.ParamFirst = theParamFirst; - theCE.ParamLast = theParamLast; } } // namespace @@ -562,6 +1144,7 @@ BRepGraph_VertexId BRepGraph::EditorView::VertexOps::Add(const gp_Pnt& thePoint, aVtxDef.Point = thePoint; aVtxDef.Tolerance = theTolerance; myGraph->allocateUID(aVertexId); + myGraph->markModified(aVertexId); return aVertexId; } @@ -590,164 +1173,277 @@ BRepGraph_EdgeId BRepGraph::EditorView::EdgeOps::Add(const BRepGraph_VertexId BRepGraphInc::EdgeDef& anEdgeDef = aStorage.ChangeEdge(aEdgeId); if (theStartVtx.IsValid()) { - const BRepGraph_VertexRefId aStartVtxRefId(aStorage.NbVertexRefs()); - aStorage.AppendVertexRef(); - BRepGraphInc::VertexRef& aStartVtxRef = aStorage.ChangeVertexRef(aStartVtxRefId); - aStartVtxRef.ParentId = aEdgeId; - aStartVtxRef.VertexDefId = theStartVtx; - aStartVtxRef.Orientation = TopAbs_FORWARD; + const BRepGraph_VertexRefId aStartVtxRefId = aStorage.AppendVertexRef(); + BRepGraphInc::VertexRef& aStartVtxRef = aStorage.ChangeVertexRef(aStartVtxRefId); + aStartVtxRef.ChildVertexId = theStartVtx; + aStartVtxRef.ParentEdgeId = aEdgeId; + aStartVtxRef.Orientation = TopAbs_FORWARD; myGraph->allocateRefUID(aStartVtxRefId); anEdgeDef.StartVertexRefId = aStartVtxRefId; - aStorage.ChangeReverseIndex().BindVertexToEdge(theStartVtx, aEdgeId); + aStorage.AttachEdgeToVertex(aEdgeId, theStartVtx); } if (theEndVtx.IsValid()) { - const BRepGraph_VertexRefId anEndVtxRefId(aStorage.NbVertexRefs()); - aStorage.AppendVertexRef(); - BRepGraphInc::VertexRef& anEndVtxRef = aStorage.ChangeVertexRef(anEndVtxRefId); - anEndVtxRef.ParentId = aEdgeId; - anEndVtxRef.VertexDefId = theEndVtx; - anEndVtxRef.Orientation = TopAbs_REVERSED; + const BRepGraph_VertexRefId anEndVtxRefId = aStorage.AppendVertexRef(); + BRepGraphInc::VertexRef& anEndVtxRef = aStorage.ChangeVertexRef(anEndVtxRefId); + anEndVtxRef.ChildVertexId = theEndVtx; + anEndVtxRef.ParentEdgeId = aEdgeId; + anEndVtxRef.Orientation = TopAbs_REVERSED; myGraph->allocateRefUID(anEndVtxRefId); anEdgeDef.EndVertexRefId = anEndVtxRefId; - if (theEndVtx != theStartVtx) - { // BindVertexToEdge dedups, but skip needless work - aStorage.ChangeReverseIndex().BindVertexToEdge(theEndVtx, aEdgeId); - } + aStorage.AttachEdgeToVertex(aEdgeId, theEndVtx); } - anEdgeDef.ParamFirst = theFirst; - anEdgeDef.ParamLast = theLast; - anEdgeDef.Tolerance = theTolerance; - anEdgeDef.SameParameter = true; - anEdgeDef.SameRange = true; + anEdgeDef.Tolerance = theTolerance; myGraph->allocateUID(aEdgeId); if (!theCurve.IsNull()) { - const BRepGraph_Curve3DRepId aCurveRepId = aStorage.AppendCurve3DRep(); - aStorage.ChangeCurve3DRep(aCurveRepId).Curve = theCurve; - anEdgeDef.Curve3DRepId = aCurveRepId; + // Create owned use record + const BRepGraph_EdgeCurve3DRepId aRepId = aStorage.AppendEdgeCurve3DRep(); + BRepGraphInc::EdgeCurve3DRep& aUse = aStorage.ChangeEdgeCurve3DRep(aRepId); + aUse.ParentEdgeId = aEdgeId; + aUse.Curve = theCurve; + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + anEdgeDef.Curve3DRepId = aRepId; } + myGraph->markModified(aEdgeId); return aEdgeId; } //================================================================================================= -BRepGraph_WireId BRepGraph::EditorView::WireOps::Add( - const NCollection_DynamicArray>& theEdges) +BRepGraph::EditorView::WireOps::CoEdgeOrderStatus BRepGraph::EditorView::WireOps::CheckCoEdgeOrder( + const NCollection_Array1& theCoEdgeIds) const +{ + NCollection_LinearVector anOrdered; + return preCheckCoEdgeOrder(myGraph->myData->myIncStorage, + theCoEdgeIds, + BRepGraph_WireId(), + true, + false, + anOrdered); +} + +//================================================================================================= + +BRepGraph::EditorView::WireOps::CoEdgeOrderStatus BRepGraph::EditorView::WireOps::CheckCoEdgeOrder( + const BRepGraph_WireId theWire, + const NCollection_Array1& theCoEdgeIds) const +{ + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theWire.IsValid(aStorage.NbWires()) || aStorage.IsRemoved(theWire)) + { + return CoEdgeOrderStatus::InvalidWire; + } + + NCollection_LinearVector anOrdered; + return preCheckCoEdgeOrder(aStorage, theCoEdgeIds, theWire, false, true, anOrdered); +} + +//================================================================================================= + +BRepGraph::EditorView::WireOps::CoEdgeOrderStatus BRepGraph::EditorView::WireOps::CheckAppendCoEdge( + const BRepGraph_WireId theWire, + const BRepGraph_CoEdgeId theCoEdgeId) const { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - for (const std::pair& anEdgeEntry : theEdges) + if (!theWire.IsValid(aStorage.NbWires()) || aStorage.IsRemoved(theWire)) { - if (!isActiveNode(aStorage, anEdgeEntry.first)) + return CoEdgeOrderStatus::InvalidWire; + } + if (!isActiveNode(aStorage, theCoEdgeId)) + { + return CoEdgeOrderStatus::InvalidCoEdge; + } + + const BRepGraphInc::CoEdgeDef& aCandidate = aStorage.CoEdge(theCoEdgeId); + if (aCandidate.ParentWireId == theWire) + { + return CoEdgeOrderStatus::AlreadyContained; + } + if (aCandidate.ParentWireId.IsValid()) + { + return CoEdgeOrderStatus::CoEdgeAlreadyBound; + } + + BRepGraph_VertexId aCandidateStart; + BRepGraph_VertexId aCandidateEnd; + if (!coEdgeOrientedVertices(aStorage, theCoEdgeId, aCandidateStart, aCandidateEnd)) + { + return CoEdgeOrderStatus::InvalidCoEdge; + } + + NCollection_LinearVector aCandidateOrder; + const NCollection_LinearVector& aCurrent = + aStorage.WireRelations(theWire).CoEdgeIds; + for (const BRepGraph_CoEdgeId& aCurrentCoEdgeId : aCurrent) + { + if (!isActiveNode(aStorage, aCurrentCoEdgeId) + || aStorage.CoEdge(aCurrentCoEdgeId).ParentWireId != theWire) { - return BRepGraph_WireId(); + return CoEdgeOrderStatus::InvalidCoEdge; } + + BRepGraph_VertexId aStart; + BRepGraph_VertexId anEnd; + if (!coEdgeOrientedVertices(aStorage, aCurrentCoEdgeId, aStart, anEnd)) + { + return CoEdgeOrderStatus::InvalidCoEdge; + } + aCandidateOrder.Append(aCurrentCoEdgeId); + } + aCandidateOrder.Append(theCoEdgeId); + + bool isConnected = true; + bool hasPrev = false; + BRepGraph_VertexId aPrevEnd; + for (const BRepGraph_CoEdgeId& aCandidateCoEdgeId : aCandidateOrder) + { + BRepGraph_VertexId aStart; + BRepGraph_VertexId anEnd; + if (!coEdgeOrientedVertices(aStorage, aCandidateCoEdgeId, aStart, anEnd) + || (hasPrev && aStart != aPrevEnd)) + { + isConnected = false; + break; + } + aPrevEnd = anEnd; + hasPrev = true; + } + if (isConnected) + { + return CoEdgeOrderStatus::Ready; + } + + const NCollection_Array1 anInput = aCandidateOrder.ToArray1(); + NCollection_LinearVector anOrdered; + return toEditorCoEdgeOrderStatus( + BRepGraphInc_WireOrder::BuildCoEdgeOrder(aStorage, BRepGraph_WireId(), anInput, anOrdered)); +} + +//================================================================================================= + +BRepGraph::EditorView::WireOps::ReplaceEdgeStatus BRepGraph::EditorView::WireOps::CheckReplaceEdge( + const BRepGraph_WireId theWire, + const BRepGraph_EdgeId theOldEdge, + const BRepGraph_EdgeId theNewEdge, + const bool theReversed) const +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theWire.IsValid(aStorage.NbWires()) || aStorage.IsRemoved(theWire)) + { + return ReplaceEdgeStatus::InvalidWire; + } + if (!isActiveNode(aStorage, theOldEdge)) + { + return ReplaceEdgeStatus::InvalidOldEdge; + } + if (!isActiveNode(aStorage, theNewEdge)) + { + return ReplaceEdgeStatus::InvalidNewEdge; + } + if (theOldEdge == theNewEdge && !theReversed) + { + return ReplaceEdgeStatus::AlreadyCurrent; + } + + bool hasOldEdge = false; + bool hasPrev = false; + BRepGraph_VertexId aPrevEnd; + const NCollection_LinearVector& aCoEdgeIds = + aStorage.WireRelations(theWire).CoEdgeIds; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIds) + { + if (!isActiveNode(aStorage, aCoEdgeId) || aStorage.CoEdge(aCoEdgeId).ParentWireId != theWire) + { + return ReplaceEdgeStatus::InvalidOldEdge; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); + hasOldEdge = hasOldEdge || aCoEdge.ChildEdgeId == theOldEdge; + + BRepGraph_VertexId aStart; + BRepGraph_VertexId anEnd; + if (!coEdgeOrientedVerticesAfterReplacement(aStorage, + aCoEdgeId, + theOldEdge, + theNewEdge, + theReversed, + aStart, + anEnd) + || (hasPrev && aStart != aPrevEnd)) + { + return ReplaceEdgeStatus::Disconnected; + } + + aPrevEnd = anEnd; + hasPrev = true; + } + return hasOldEdge ? ReplaceEdgeStatus::Ready : ReplaceEdgeStatus::InvalidOldEdge; +} + +//================================================================================================= + +BRepGraph_WireId BRepGraph::EditorView::WireOps::Add( + const NCollection_Array1& theCoEdgeIds) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (theCoEdgeIds.IsEmpty()) + { + const BRepGraph_WireId aWireId(aStorage.NbWires()); + aStorage.AppendWire(); + myGraph->allocateUID(aWireId); + myGraph->markModified(aWireId); + return aWireId; + } + + NCollection_LinearVector aConnectedCoEdges; + const CoEdgeOrderStatus aStatus = + preCheckCoEdgeOrder(aStorage, theCoEdgeIds, BRepGraph_WireId(), true, false, aConnectedCoEdges); + if (aStatus != CoEdgeOrderStatus::Ready && aStatus != CoEdgeOrderStatus::Reordered) + { + return BRepGraph_WireId(); } const BRepGraph_WireId aWireId(aStorage.NbWires()); aStorage.AppendWire(); myGraph->allocateUID(aWireId); - for (const std::pair& anEdgeEntry : theEdges) + for (const BRepGraph_CoEdgeId& aCoEdgeId : aConnectedCoEdges) { - const BRepGraph_EdgeId anEdgeDefId = anEdgeEntry.first; - const TopAbs_Orientation anOri = anEdgeEntry.second; - - // Create CoEdge entity for this edge-wire binding. - const BRepGraph_CoEdgeId aCoEdgeId(aStorage.NbCoEdges()); - aStorage.AppendCoEdge(); BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(aCoEdgeId); - aCoEdge.EdgeDefId = anEdgeDefId; - aCoEdge.Orientation = anOri; - myGraph->allocateUID(aCoEdgeId); + aCoEdge.ParentWireId = aWireId; - // CoEdgeRef in ref-table. - const BRepGraph_CoEdgeRefId aCoEdgeRefId(aStorage.NbCoEdgeRefs()); - aStorage.AppendCoEdgeRef(); - BRepGraphInc::CoEdgeRef& aCoEdgeRef = aStorage.ChangeCoEdgeRef(aCoEdgeRefId); - aCoEdgeRef.ParentId = aWireId; - aCoEdgeRef.CoEdgeDefId = aCoEdgeId; - myGraph->allocateRefUID(aCoEdgeRefId); - aStorage.ChangeWire(aWireId).CoEdgeRefIds.Append(aCoEdgeRefId); - - aStorage.ChangeReverseIndex().BindEdgeToWire(aCoEdge.EdgeDefId, aWireId); - aStorage.ChangeReverseIndex().BindEdgeToCoEdge(aCoEdge.EdgeDefId, aCoEdgeId); - aStorage.ChangeReverseIndex().BindCoEdgeToWire(aCoEdgeId, aWireId); - } - - // Check closure. - if (!theEdges.IsEmpty()) - { - const BRepGraph_EdgeId aFirstEdgeNodeId = theEdges.First().first; - const TopAbs_Orientation aFirstOri = theEdges.First().second; - const BRepGraph_EdgeId aLastEdgeNodeId = theEdges.Last().first; - const TopAbs_Orientation aLastOri = theEdges.Last().second; - - const BRepGraphInc::EdgeDef& aFirstEdge = aStorage.Edge(aFirstEdgeNodeId); - const BRepGraphInc::EdgeDef& aLastEdge = aStorage.Edge(aLastEdgeNodeId); - const BRepGraph_VertexRefId aFirstRefId = - (aFirstOri == TopAbs_FORWARD) ? aFirstEdge.StartVertexRefId : aFirstEdge.EndVertexRefId; - const BRepGraph_VertexRefId aLastRefId = - (aLastOri == TopAbs_FORWARD) ? aLastEdge.EndVertexRefId : aLastEdge.StartVertexRefId; - const BRepGraph_NodeId aFirstVtx = - aFirstRefId.IsValid() ? BRepGraph_NodeId(aStorage.VertexRef(aFirstRefId).VertexDefId) - : BRepGraph_NodeId(); - const BRepGraph_NodeId aLastVtx = - aLastRefId.IsValid() ? BRepGraph_NodeId(aStorage.VertexRef(aLastRefId).VertexDefId) - : BRepGraph_NodeId(); - - const bool aIsClosed = aFirstVtx.IsValid() && aLastVtx.IsValid() && aFirstVtx == aLastVtx; - aStorage.ChangeWire(aWireId).IsClosed = aIsClosed; + aStorage.ChangeWireRelationsInternal(aWireId).CoEdgeIds.Append(aCoEdgeId); + BRepGraphInc::EdgeRelations& anEdgeRel = + aStorage.ChangeEdgeRelationsInternal(aCoEdge.ChildEdgeId); + appendUniqueRelationId(anEdgeRel.CoEdgeIds, aCoEdgeId); + myGraph->markModified(aCoEdgeId); + myGraph->markModified(aCoEdge.ChildEdgeId); } + myGraph->markModified(aWireId); return aWireId; } //================================================================================================= -BRepGraph_VertexRefId BRepGraph::EditorView::EdgeOps::AddInternalVertex( - const BRepGraph_EdgeId theEdgeEntity, - const BRepGraph_VertexId theVertexEntity, - const TopAbs_Orientation theOri) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theEdgeEntity) || !isActiveNode(aStorage, theVertexEntity)) - { - return BRepGraph_VertexRefId(); - } - - const BRepGraph_VertexRefId aVertexRefId(aStorage.NbVertexRefs()); - aStorage.AppendVertexRef(); - BRepGraphInc::VertexRef& aVertexRef = aStorage.ChangeVertexRef(aVertexRefId); - aVertexRef.ParentId = theEdgeEntity; - aVertexRef.VertexDefId = theVertexEntity; - aVertexRef.Orientation = theOri; - myGraph->allocateRefUID(aVertexRefId); - aStorage.ChangeEdge(theEdgeEntity).InternalVertexRefIds.Append(aVertexRefId); - - aStorage.ChangeReverseIndex().BindVertexToEdge(theVertexEntity, theEdgeEntity); - myGraph->markModified(theEdgeEntity); - return aVertexRefId; -} - -//================================================================================================= - BRepGraph_FaceId BRepGraph::EditorView::FaceOps::Add( - const occ::handle& theSurface, - const BRepGraph_WireId theOuterWire, - const NCollection_DynamicArray& theInnerWires, - const double theTolerance) + const occ::handle& theSurface, + const BRepGraph_WireId theOuterWire, + const NCollection_Array1& theInnerWires, + const double theTolerance) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (theOuterWire.IsValid() && !isActiveNode(aStorage, theOuterWire)) { return BRepGraph_FaceId(); } - for (const BRepGraph_WireId& aWireDefId : theInnerWires) + for (const BRepGraph_WireId& aChildWireId : theInnerWires) { - if (aWireDefId.IsValid() && !isActiveNode(aStorage, aWireDefId)) + if (aChildWireId.IsValid() && !isActiveNode(aStorage, aChildWireId)) { return BRepGraph_FaceId(); } @@ -759,69 +1455,64 @@ BRepGraph_FaceId BRepGraph::EditorView::FaceOps::Add( myGraph->allocateUID(aFaceId); if (!theSurface.IsNull()) { - const BRepGraph_SurfaceRepId aSurfRepId = aStorage.AppendSurfaceRep(); - aStorage.ChangeSurfaceRep(aSurfRepId).Surface = theSurface; - aFaceDef.SurfaceRepId = aSurfRepId; + // Create owned use record + const BRepGraph_FaceSurfaceRepId aRepId = aStorage.AppendFaceSurfaceRep(); + BRepGraphInc::FaceSurfaceRep& aUse = aStorage.ChangeFaceSurfaceRep(aRepId); + aUse.ParentFaceId = aFaceId; + aUse.Surface = theSurface; + aFaceDef.SurfaceRepId = aRepId; } // Link wire refs. if (theOuterWire.IsValid()) { - const BRepGraph_WireRefId anOuterWireRefId(aStorage.NbWireRefs()); - aStorage.AppendWireRef(); - BRepGraphInc::WireRef& anOuterWireRef = aStorage.ChangeWireRef(anOuterWireRefId); - anOuterWireRef.ParentId = aFaceId; - anOuterWireRef.WireDefId = theOuterWire; - anOuterWireRef.IsOuter = true; + const BRepGraph_WireRefId anOuterWireRefId = aStorage.AttachWireToFace(aFaceId, theOuterWire); myGraph->allocateRefUID(anOuterWireRefId); - aStorage.ChangeFace(aFaceId).WireRefIds.Append(anOuterWireRefId); - aStorage.ChangeReverseIndex().BindWireToFace(theOuterWire, aFaceId); } - for (const BRepGraph_WireId& aWireDefId : theInnerWires) + for (const BRepGraph_WireId& aChildWireId : theInnerWires) { - if (!aWireDefId.IsValid()) + if (!aChildWireId.IsValid()) { continue; } - const BRepGraph_WireRefId aWireRefId(aStorage.NbWireRefs()); - aStorage.AppendWireRef(); - BRepGraphInc::WireRef& aWireRef = aStorage.ChangeWireRef(aWireRefId); - aWireRef.ParentId = aFaceId; - aWireRef.WireDefId = aWireDefId; - aWireRef.IsOuter = false; + const BRepGraph_WireRefId aWireRefId = aStorage.AttachWireToFace(aFaceId, aChildWireId); myGraph->allocateRefUID(aWireRefId); - aStorage.ChangeFace(aFaceId).WireRefIds.Append(aWireRefId); - aStorage.ChangeReverseIndex().BindWireToFace(aWireDefId, aFaceId); } + myGraph->markModified(aFaceId); return aFaceId; } //================================================================================================= -BRepGraph_VertexRefId BRepGraph::EditorView::FaceOps::AddVertex( - const BRepGraph_FaceId theFaceEntity, - const BRepGraph_VertexId theVertexEntity, - const TopAbs_Orientation theOri) +BRepGraph_WireRefId BRepGraph::EditorView::FaceOps::Append( + const BRepGraph_FaceId theFaceEntity, + const BRepGraph_WireId theWireEntity, + const BRepGraphInc::ParityOrientation theOri) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theFaceEntity) || !isActiveNode(aStorage, theVertexEntity)) + if (!isActiveNode(aStorage, theFaceEntity) || !isActiveNode(aStorage, theWireEntity)) { - return BRepGraph_VertexRefId(); + return BRepGraph_WireRefId(); + } + myGraph->Editor().requireUnlocked(theFaceEntity, "BRepGraph::EditorView::Append(): locked face"); + myGraph->Editor().requireNoActiveGuard(theFaceEntity, + "BRepGraph::EditorView::Append(): guard active on face"); + + const BRepGraph_WireRefId aWireRefId = + aStorage.AttachWireToFace(theFaceEntity, theWireEntity, theOri); + myGraph->allocateRefUID(aWireRefId); + + myGraph->markRefModified(aWireRefId); + + if (!myGraph->myData->myIncStorage.DeferredMode()) + { + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "AddWire: post-mutation relation inconsistency"); } - const BRepGraph_VertexRefId aVertexRefId(aStorage.NbVertexRefs()); - aStorage.AppendVertexRef(); - BRepGraphInc::VertexRef& aVertexRef = aStorage.ChangeVertexRef(aVertexRefId); - aVertexRef.ParentId = theFaceEntity; - aVertexRef.VertexDefId = theVertexEntity; - aVertexRef.Orientation = theOri; - myGraph->allocateRefUID(aVertexRefId); - aStorage.ChangeFace(theFaceEntity).VertexRefIds.Append(aVertexRefId); - - myGraph->markModified(theFaceEntity); - return aVertexRefId; + return aWireRefId; } //================================================================================================= @@ -832,6 +1523,7 @@ BRepGraph_ShellId BRepGraph::EditorView::ShellOps::Add() const BRepGraph_ShellId aShellId(aStorage.NbShells()); aStorage.AppendShell(); myGraph->allocateUID(aShellId); + myGraph->markModified(aShellId); return aShellId; } @@ -844,131 +1536,65 @@ BRepGraph_SolidId BRepGraph::EditorView::SolidOps::Add() const BRepGraph_SolidId aSolidId(aStorage.NbSolids()); aStorage.AppendSolid(); myGraph->allocateUID(aSolidId); + myGraph->markModified(aSolidId); return aSolidId; } //================================================================================================= -BRepGraph_FaceRefId BRepGraph::EditorView::ShellOps::AddFace(const BRepGraph_ShellId theShellEntity, - const BRepGraph_FaceId theFaceEntity, - const TopAbs_Orientation theOri) +BRepGraph_FaceRefId BRepGraph::EditorView::ShellOps::Append( + const BRepGraph_ShellId theShellEntity, + const BRepGraph_FaceId theFaceEntity, + const BRepGraphInc::ParityOrientation theOri) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (!isActiveNode(aStorage, theShellEntity) || !isActiveNode(aStorage, theFaceEntity)) { return BRepGraph_FaceRefId(); } + myGraph->Editor().requireUnlocked(theShellEntity, + "BRepGraph::EditorView::Append(): locked shell"); + myGraph->Editor().requireNoActiveGuard(theShellEntity, + "BRepGraph::EditorView::Append(): guard active on shell"); - // Append FaceRef to the shell definition. - const BRepGraph_FaceRefId aFaceRefId(aStorage.NbFaceRefs()); - aStorage.AppendFaceRef(); - BRepGraphInc::FaceRef& aFREntry = aStorage.ChangeFaceRef(aFaceRefId); - aFREntry.ParentId = theShellEntity; - aFREntry.FaceDefId = theFaceEntity; - aFREntry.Orientation = theOri; + const BRepGraph_FaceRefId aFaceRefId = + aStorage.AttachFaceToShell(theShellEntity, theFaceEntity, theOri); myGraph->allocateRefUID(aFaceRefId); - aStorage.ChangeShell(theShellEntity).FaceRefIds.Append(aFaceRefId); - aStorage.ChangeReverseIndex().BindFaceToShell(theFaceEntity, theShellEntity); - myGraph->markModified(theShellEntity); + myGraph->markRefModified(aFaceRefId); return aFaceRefId; } //================================================================================================= -BRepGraph_ChildRefId BRepGraph::EditorView::ShellOps::AddChild( - const BRepGraph_ShellId theShellEntity, - const BRepGraph_NodeId theChildEntity, - const TopAbs_Orientation theOri) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theShellEntity) || !isActiveTopologyNode(aStorage, theChildEntity)) - { - return BRepGraph_ChildRefId(); - } - if (theChildEntity.NodeKind != BRepGraph_NodeId::Kind::Wire - && theChildEntity.NodeKind != BRepGraph_NodeId::Kind::Edge) - { - return BRepGraph_ChildRefId(); - } - - const BRepGraph_ChildRefId aChildRefId(aStorage.NbChildRefs()); - aStorage.AppendChildRef(); - BRepGraphInc::ChildRef& aChildRef = aStorage.ChangeChildRef(aChildRefId); - aChildRef.ParentId = theShellEntity; - aChildRef.ChildDefId = theChildEntity; - aChildRef.Orientation = theOri; - myGraph->allocateRefUID(aChildRefId); - aStorage.ChangeShell(theShellEntity).AuxChildRefIds.Append(aChildRefId); - - myGraph->markModified(theShellEntity); - return aChildRefId; -} - -//================================================================================================= - -BRepGraph_ShellRefId BRepGraph::EditorView::SolidOps::AddShell( - const BRepGraph_SolidId theSolidEntity, - const BRepGraph_ShellId theShellEntity, - const TopAbs_Orientation theOri) +BRepGraph_ShellRefId BRepGraph::EditorView::SolidOps::Append( + const BRepGraph_SolidId theSolidEntity, + const BRepGraph_ShellId theShellEntity, + const BRepGraphInc::ParityOrientation theOri) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (!isActiveNode(aStorage, theSolidEntity) || !isActiveNode(aStorage, theShellEntity)) { return BRepGraph_ShellRefId(); } + myGraph->Editor().requireUnlocked(theSolidEntity, + "BRepGraph::EditorView::Append(): locked solid"); + myGraph->Editor().requireNoActiveGuard(theSolidEntity, + "BRepGraph::EditorView::Append(): guard active on solid"); - // Append ShellRef to the solid definition. - const BRepGraph_ShellRefId aShellRefId(aStorage.NbShellRefs()); - aStorage.AppendShellRef(); - BRepGraphInc::ShellRef& aSREntry = aStorage.ChangeShellRef(aShellRefId); - aSREntry.ParentId = theSolidEntity; - aSREntry.ShellDefId = theShellEntity; - aSREntry.Orientation = theOri; + const BRepGraph_ShellRefId aShellRefId = + aStorage.AttachShellToSolid(theSolidEntity, theShellEntity, theOri); myGraph->allocateRefUID(aShellRefId); - aStorage.ChangeSolid(theSolidEntity).ShellRefIds.Append(aShellRefId); - aStorage.ChangeReverseIndex().BindShellToSolid(theShellEntity, theSolidEntity); - myGraph->markModified(theSolidEntity); + myGraph->markRefModified(aShellRefId); return aShellRefId; } //================================================================================================= -BRepGraph_ChildRefId BRepGraph::EditorView::SolidOps::AddChild( - const BRepGraph_SolidId theSolidEntity, - const BRepGraph_NodeId theChildEntity, - const TopAbs_Orientation theOri) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theSolidEntity) || !isActiveTopologyNode(aStorage, theChildEntity)) - { - return BRepGraph_ChildRefId(); - } - if (theChildEntity.NodeKind != BRepGraph_NodeId::Kind::Edge - && theChildEntity.NodeKind != BRepGraph_NodeId::Kind::Vertex) - { - return BRepGraph_ChildRefId(); - } - - const BRepGraph_ChildRefId aChildRefId(aStorage.NbChildRefs()); - aStorage.AppendChildRef(); - BRepGraphInc::ChildRef& aChildRef = aStorage.ChangeChildRef(aChildRefId); - aChildRef.ParentId = theSolidEntity; - aChildRef.ChildDefId = theChildEntity; - aChildRef.Orientation = theOri; - myGraph->allocateRefUID(aChildRefId); - aStorage.ChangeSolid(theSolidEntity).AuxChildRefIds.Append(aChildRefId); - - myGraph->markModified(theSolidEntity); - return aChildRefId; -} - -//================================================================================================= - BRepGraph_CompoundId BRepGraph::EditorView::CompoundOps::Add( - const NCollection_DynamicArray& theChildEntities) + const NCollection_Array1& theChildEntities) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; for (const BRepGraph_NodeId& aChild : theChildEntities) @@ -981,55 +1607,48 @@ BRepGraph_CompoundId BRepGraph::EditorView::CompoundOps::Add( const BRepGraph_CompoundId aCompoundId(aStorage.NbCompounds()); aStorage.AppendCompound(); - BRepGraphInc::CompoundDef& aCompDef = aStorage.ChangeCompound(aCompoundId); myGraph->allocateUID(aCompoundId); for (const BRepGraph_NodeId& aChild : theChildEntities) { - const BRepGraph_ChildRefId aChildRefId(aStorage.NbChildRefs()); - aStorage.AppendChildRef(); - BRepGraphInc::ChildRef& aCREntry = aStorage.ChangeChildRef(aChildRefId); - aCREntry.ParentId = aCompoundId; - aCREntry.ChildDefId = aChild; + const BRepGraph_ChildRefId aChildRefId = aStorage.AttachChildToCompound(aCompoundId, aChild); myGraph->allocateRefUID(aChildRefId); - aCompDef.ChildRefIds.Append(aChildRefId); - aStorage.ChangeReverseIndex().BindCompoundChild(aChild, aCompoundId); } + myGraph->markModified(aCompoundId); return aCompoundId; } //================================================================================================= -BRepGraph_ChildRefId BRepGraph::EditorView::CompoundOps::AddChild( - const BRepGraph_CompoundId theCompoundEntity, - const BRepGraph_NodeId theChildEntity, - const TopAbs_Orientation theOri) +BRepGraph_ChildRefId BRepGraph::EditorView::CompoundOps::Append( + const BRepGraph_CompoundId theCompoundEntity, + const BRepGraph_NodeId theChildEntity, + const BRepGraphInc::ParityOrientation theOri) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (!isActiveNode(aStorage, theCompoundEntity) || !isActiveTopologyNode(aStorage, theChildEntity)) { return BRepGraph_ChildRefId(); } + myGraph->Editor().requireUnlocked(theCompoundEntity, + "BRepGraph::EditorView::Append(): locked compound"); + myGraph->Editor().requireNoActiveGuard( + theCompoundEntity, + "BRepGraph::EditorView::Append(): guard active on compound"); - const BRepGraph_ChildRefId aChildRefId(aStorage.NbChildRefs()); - aStorage.AppendChildRef(); - BRepGraphInc::ChildRef& aChildRef = aStorage.ChangeChildRef(aChildRefId); - aChildRef.ParentId = theCompoundEntity; - aChildRef.ChildDefId = theChildEntity; - aChildRef.Orientation = theOri; + const BRepGraph_ChildRefId aChildRefId = + aStorage.AttachChildToCompound(theCompoundEntity, theChildEntity, TopLoc_Location(), theOri); myGraph->allocateRefUID(aChildRefId); - aStorage.ChangeCompound(theCompoundEntity).ChildRefIds.Append(aChildRefId); - aStorage.ChangeReverseIndex().BindCompoundChild(theChildEntity, theCompoundEntity); - myGraph->markModified(theCompoundEntity); + myGraph->markRefModified(aChildRefId); return aChildRefId; } //================================================================================================= BRepGraph_CompSolidId BRepGraph::EditorView::CompSolidOps::Add( - const NCollection_DynamicArray& theSolidEntities) + const NCollection_Array1& theSolidEntities) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; for (const BRepGraph_SolidId& aSolidId : theSolidEntities) @@ -1042,56 +1661,49 @@ BRepGraph_CompSolidId BRepGraph::EditorView::CompSolidOps::Add( const BRepGraph_CompSolidId aCompSolidId(aStorage.NbCompSolids()); aStorage.AppendCompSolid(); - BRepGraphInc::CompSolidDef& aCSolDef = aStorage.ChangeCompSolid(aCompSolidId); myGraph->allocateUID(aCompSolidId); for (const BRepGraph_SolidId& aSolidId : theSolidEntities) { - const BRepGraph_SolidRefId aSolidRefId(aStorage.NbSolidRefs()); - aStorage.AppendSolidRef(); - BRepGraphInc::SolidRef& aSREntry = aStorage.ChangeSolidRef(aSolidRefId); - aSREntry.ParentId = aCompSolidId; - aSREntry.SolidDefId = aSolidId; + const BRepGraph_SolidRefId aSolidRefId = + aStorage.AttachSolidToCompSolid(aCompSolidId, aSolidId); myGraph->allocateRefUID(aSolidRefId); - aCSolDef.SolidRefIds.Append(aSolidRefId); - aStorage.ChangeReverseIndex().BindSolidToCompSolid(aSolidId, aCompSolidId); } + myGraph->markModified(aCompSolidId); return aCompSolidId; } //================================================================================================= -BRepGraph_SolidRefId BRepGraph::EditorView::CompSolidOps::AddSolid( - const BRepGraph_CompSolidId theCompSolidEntity, - const BRepGraph_SolidId theSolidEntity, - const TopAbs_Orientation theOri) +BRepGraph_SolidRefId BRepGraph::EditorView::CompSolidOps::Append( + const BRepGraph_CompSolidId theCompSolidEntity, + const BRepGraph_SolidId theSolidEntity, + const BRepGraphInc::ParityOrientation theOri) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (!isActiveNode(aStorage, theCompSolidEntity) || !isActiveNode(aStorage, theSolidEntity)) { return BRepGraph_SolidRefId(); } + myGraph->Editor().requireUnlocked(theCompSolidEntity, + "BRepGraph::EditorView::Append(): locked compsolid"); + myGraph->Editor().requireNoActiveGuard( + theCompSolidEntity, + "BRepGraph::EditorView::Append(): guard active on compsolid"); - const BRepGraph_SolidRefId aSolidRefId(aStorage.NbSolidRefs()); - aStorage.AppendSolidRef(); - BRepGraphInc::SolidRef& aSREntry = aStorage.ChangeSolidRef(aSolidRefId); - aSREntry.ParentId = theCompSolidEntity; - aSREntry.SolidDefId = theSolidEntity; - aSREntry.Orientation = theOri; + const BRepGraph_SolidRefId aSolidRefId = + aStorage.AttachSolidToCompSolid(theCompSolidEntity, theSolidEntity, theOri); myGraph->allocateRefUID(aSolidRefId); - aStorage.ChangeCompSolid(theCompSolidEntity).SolidRefIds.Append(aSolidRefId); - aStorage.ChangeReverseIndex().BindSolidToCompSolid(theSolidEntity, theCompSolidEntity); - myGraph->markModified(theCompSolidEntity); + myGraph->markRefModified(aSolidRefId); return aSolidRefId; } //================================================================================================= -BRepGraph_ProductId BRepGraph::EditorView::ProductOps::LinkProductToTopology( - const BRepGraph_NodeId theShapeRoot, - const TopLoc_Location& thePlacement) +BRepGraph_ProductId BRepGraph::EditorView::ProductOps::Add(const BRepGraph_NodeId theShapeRoot, + const TopLoc_Location& thePlacement) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (!isActiveTopologyNode(aStorage, theShapeRoot)) @@ -1101,52 +1713,71 @@ BRepGraph_ProductId BRepGraph::EditorView::ProductOps::LinkProductToTopology( const BRepGraph_ProductId aProductId(aStorage.NbProducts()); aStorage.AppendProduct(); - BRepGraphInc::ProductDef& aProductDef = aStorage.ChangeProduct(aProductId); myGraph->allocateUID(aProductId); // Link the product to its shape root via an occurrence + occurrence ref. const BRepGraph_OccurrenceId anOccId(aStorage.NbOccurrences()); aStorage.AppendOccurrence(); BRepGraphInc::OccurrenceDef& anOccDef = aStorage.ChangeOccurrence(anOccId); - anOccDef.ChildDefId = theShapeRoot; - Standard_ASSERT_RETURN(isValidOccurrenceChildKind(anOccDef.ChildDefId.NodeKind), - "ProductOps::LinkProductToTopology: invalid occurrence child kind", + anOccDef.ChildNodeId = theShapeRoot; + Standard_ASSERT_RETURN(isValidOccurrenceChildKind(anOccDef.ChildNodeId.NodeKind), + "ProductOps::Add: invalid occurrence child kind", BRepGraph_ProductId()); myGraph->allocateUID(anOccId); - const BRepGraph_OccurrenceRefId anOccRefId(aStorage.NbOccurrenceRefs()); - aStorage.AppendOccurrenceRef(); - BRepGraphInc::OccurrenceRef& anOccRef = aStorage.ChangeOccurrenceRef(anOccRefId); - anOccRef.ParentId = aProductId; - anOccRef.OccurrenceDefId = anOccId; - anOccRef.LocalLocation = thePlacement; + const BRepGraph_OccurrenceRefId anOccRefId = + aStorage.AttachOccurrenceToProduct(aProductId, anOccId, thePlacement); myGraph->allocateRefUID(anOccRefId); - aProductDef.OccurrenceRefIds.Append(anOccRefId); - - // No reverse-index bind: the occurrence's child is a topology root, not a product; - // myProductToOccurrences only indexes occurrences whose ChildDefId is a Product. - myGraph->myData->myRootProductIds.Append(aProductId); + myGraph->markModified(anOccId); + myGraph->markRefModified(anOccRefId); return aProductId; } //================================================================================================= -BRepGraph_ProductId BRepGraph::EditorView::ProductOps::CreateEmptyProduct() +BRepGraph_ProductId BRepGraph::EditorView::ProductOps::Add() { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; const BRepGraph_ProductId aProductId(aStorage.NbProducts()); aStorage.AppendProduct(); myGraph->allocateUID(aProductId); - - myGraph->myData->myRootProductIds.Append(aProductId); + myGraph->markModified(aProductId); return aProductId; } //================================================================================================= -BRepGraph_OccurrenceId BRepGraph::EditorView::ProductOps::LinkProducts( +void BRepGraph::EditorView::ProductOps::AppendDocumentRoot(const BRepGraph_ProductId theProductId) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theProductId.IsValid(aStorage.NbProducts()) || aStorage.IsRemoved(theProductId)) + { + return; + } + myGraph->Editor().requireUnlocked(theProductId, + "BRepGraph::EditorView::AppendDocumentRoot(): locked product"); + myGraph->Editor().requireNoActiveGuard( + theProductId, + "BRepGraph::EditorView::AppendDocumentRoot(): guard active on product"); + + NCollection_LinearVector& aRoots = aStorage.ChangeRootProductIds(); + for (size_t anIdx = 0; anIdx < aRoots.Size(); ++anIdx) + { + if (aRoots.Value(anIdx) == theProductId) + { + return; + } + } + + aRoots.Append(theProductId); + myGraph->markModified(theProductId); +} + +//================================================================================================= + +BRepGraph_OccurrenceId BRepGraph::EditorView::ProductOps::Append( const BRepGraph_ProductId theParentProduct, const BRepGraph_ProductId theReferencedProduct, const TopLoc_Location& thePlacement, @@ -1176,31 +1807,43 @@ BRepGraph_OccurrenceId BRepGraph::EditorView::ProductOps::LinkProducts( return BRepGraph_OccurrenceId(); } if (theParentOccurrence.IsValid() - && aStorage.Occurrence(theParentOccurrence).ChildDefId != BRepGraph_NodeId(theParentProduct)) + && aStorage.Occurrence(theParentOccurrence).ChildNodeId != BRepGraph_NodeId(theParentProduct)) { return BRepGraph_OccurrenceId(); } + myGraph->Editor().requireUnlocked(theParentProduct, + "BRepGraph::EditorView::Append(): locked parent product"); + myGraph->Editor().requireNoActiveGuard( + theParentProduct, + "BRepGraph::EditorView::Append(): guard active on parent product"); + if (theParentOccurrence.IsValid()) + { + myGraph->Editor().requireUnlocked(theParentOccurrence, + "BRepGraph::EditorView::Append(): locked parent occurrence"); + myGraph->Editor().requireNoActiveGuard( + theParentOccurrence, + "BRepGraph::EditorView::Append(): guard active on parent occurrence"); + } const BRepGraph_OccurrenceId anOccId(aStorage.NbOccurrences()); aStorage.AppendOccurrence(); BRepGraphInc::OccurrenceDef& anOccDef = aStorage.ChangeOccurrence(anOccId); - anOccDef.ChildDefId = theReferencedProduct; - Standard_ASSERT_RETURN(isValidOccurrenceChildKind(anOccDef.ChildDefId.NodeKind), - "ProductOps::LinkProducts: invalid occurrence child kind", + anOccDef.ChildNodeId = theReferencedProduct; + Standard_ASSERT_RETURN(isValidOccurrenceChildKind(anOccDef.ChildNodeId.NodeKind), + "ProductOps::Append: invalid occurrence child kind", BRepGraph_OccurrenceId()); myGraph->allocateUID(anOccId); - const BRepGraph_OccurrenceRefId anOccRefId(aStorage.NbOccurrenceRefs()); - aStorage.AppendOccurrenceRef(); - BRepGraphInc::OccurrenceRef& anOccRef = aStorage.ChangeOccurrenceRef(anOccRefId); - anOccRef.ParentId = theParentProduct; - anOccRef.OccurrenceDefId = anOccId; - anOccRef.LocalLocation = thePlacement; + const BRepGraph_OccurrenceRefId anOccRefId = + aStorage.AttachOccurrenceToProduct(theParentProduct, anOccId, thePlacement); myGraph->allocateRefUID(anOccRefId); - aStorage.ChangeProduct(theParentProduct).OccurrenceRefIds.Append(anOccRefId); - removeFromRootProducts(myGraph->myData->myRootProductIds, theReferencedProduct); - aStorage.ChangeReverseIndex().BindProductOccurrence(anOccId, theReferencedProduct); + removeFromRootProducts(myGraph->myData->myIncStorage.ChangeRootProductIds(), + theReferencedProduct); + + myGraph->markModified(anOccId); + myGraph->markRefModified(anOccRefId); + myGraph->markModified(theReferencedProduct); if (theOutOccurrenceRefId != nullptr) { @@ -1212,60 +1855,18 @@ BRepGraph_OccurrenceId BRepGraph::EditorView::ProductOps::LinkProducts( //================================================================================================= void BRepGraph::EditorView::GenOps::RemoveNode(const BRepGraph_NodeId theNode) -{ - RemoveNode(theNode, BRepGraph_NodeId()); -} - -//================================================================================================= - -void BRepGraph::EditorView::GenOps::RemoveNode(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) { if (!theNode.IsValid()) { return; } + myGraph->Editor().requireUnlocked(theNode, "BRepGraph::EditorView::RemoveNode(): locked node"); + myGraph->Editor().requireNoActiveGuard( + theNode, + "BRepGraph::EditorView::RemoveNode(): guard active on node"); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - // When removing an Edge with a replacement, reparent all CoEdges from the - // removed edge to the replacement edge. This prevents orphaned CoEdges - // that would be excluded from queries via CoEdgesOfEdge(). - if (theNode.NodeKind == BRepGraph_NodeId::Kind::Edge && theReplacement.IsValid() - && theReplacement != theNode && theReplacement.NodeKind == BRepGraph_NodeId::Kind::Edge) - { - Standard_ASSERT_RETURN(isNodeIndexInRange(aStorage, theNode), - "RemoveNode: source edge index is out of range", - Standard_VOID_RETURN); - Standard_ASSERT_RETURN(isNodeIndexInRange(aStorage, theReplacement), - "RemoveNode: replacement edge index is out of range", - Standard_VOID_RETURN); - Standard_ASSERT_RETURN(!aStorage.Edge(BRepGraph_EdgeId(theReplacement)).IsRemoved, - "RemoveNode: replacement edge must be active", - Standard_VOID_RETURN); - - rebindCoEdgesForEdgeReplacement(aStorage, - BRepGraph_EdgeId::FromNodeId(theNode), - BRepGraph_EdgeId::FromNodeId(theReplacement)); - } - - switch (theNode.NodeKind) - { - case BRepGraph_NodeId::Kind::Vertex: - case BRepGraph_NodeId::Kind::Edge: - case BRepGraph_NodeId::Kind::CoEdge: - case BRepGraph_NodeId::Kind::Wire: - case BRepGraph_NodeId::Kind::Face: - case BRepGraph_NodeId::Kind::Shell: - case BRepGraph_NodeId::Kind::Solid: - case BRepGraph_NodeId::Kind::Compound: - case BRepGraph_NodeId::Kind::CompSolid: - case BRepGraph_NodeId::Kind::Product: - case BRepGraph_NodeId::Kind::Occurrence: - break; - default: - Standard_ASSERT_RETURN(false, "RemoveNode: unsupported node kind", Standard_VOID_RETURN); - } Standard_ASSERT_RETURN(isNodeIndexInRange(aStorage, theNode), "RemoveNode: node index is out of range", Standard_VOID_RETURN); @@ -1274,71 +1875,368 @@ void BRepGraph::EditorView::GenOps::RemoveNode(const BRepGraph_NodeId theNode, return; } - if (theNode.NodeKind == BRepGraph_NodeId::Kind::Edge) + // Propagate SubtreeGen upward to parents before unbinding relation entries, + // so generation-based caches on parent nodes detect the removal. + switch (theNode.NodeKind) { - const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::FromNodeId(theNode); - const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(anEdgeId); - BRepGraphInc_ReverseIndex& aRI = aStorage.ChangeReverseIndex(); - if (anEdge.StartVertexRefId.IsValid(aStorage.NbVertexRefs())) + case BRepGraph_NodeId::Kind::Vertex: + case BRepGraph_NodeId::Kind::CoEdge: + case BRepGraph_NodeId::Kind::Product: + case BRepGraph_NodeId::Kind::Occurrence: + break; + default: + myGraph->propagateSubtreeGen(theNode); + break; + } + + // Notify layers while relation parent context is still available. + myGraph->myData->myLayerRegistry.DispatchOnNodeRemoved(theNode); + + auto markOwnedRefRemoved = [&](const BRepGraph_RefId theRef) { + if (!isRefInRange(aStorage, theRef) || theRef.IsRemoved(*myGraph) + || !aStorage.MarkRemovedRef(theRef)) { - const BRepGraph_VertexId aStartV = aStorage.VertexRef(anEdge.StartVertexRefId).VertexDefId; - if (aStartV.IsValid()) - { - aRI.UnbindVertexFromEdge(aStartV, anEdgeId); - } + return; } - if (anEdge.EndVertexRefId.IsValid(aStorage.NbVertexRefs())) + myGraph->markRefModified(theRef); + const BRepGraph_RefUID aUID(theRef.RefKind, aStorage.BaseRef(theRef).UID); + if (aUID.IsValid()) { - const BRepGraph_VertexId anEndV = aStorage.VertexRef(anEdge.EndVertexRefId).VertexDefId; - if (anEndV.IsValid()) - { - aRI.UnbindVertexFromEdge(anEndV, anEdgeId); - } + std::unique_lock aLock(myGraph->myData->myIncStorage.myRefUIDToRefIdMutex); + myGraph->myData->myIncStorage.myRefUIDToRefId.UnBind(aUID); } - for (const BRepGraph_VertexRefId& anIntRefId : anEdge.InternalVertexRefIds) - { - if (!anIntRefId.IsValid(aStorage.NbVertexRefs())) + myGraph->myData->myLayerRegistry.DispatchOnRefRemoved(theRef); + }; + + switch (theNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Edge: { + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::FromNodeId(theNode); + const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(anEdgeId); + const BRepGraph_VertexId aStartVtx = + anEdge.StartVertexRefId.IsValid(aStorage.NbVertexRefs()) + ? aStorage.VertexRef(anEdge.StartVertexRefId).ChildVertexId + : BRepGraph_VertexId(); + const BRepGraph_VertexId anEndVtx = + anEdge.EndVertexRefId.IsValid(aStorage.NbVertexRefs()) + ? aStorage.VertexRef(anEdge.EndVertexRefId).ChildVertexId + : BRepGraph_VertexId(); + markOwnedRefRemoved(anEdge.StartVertexRefId); + markOwnedRefRemoved(anEdge.EndVertexRefId); + aStorage.RebindVertexEdge(aStartVtx, BRepGraph_VertexId(), anEdgeId, BRepGraph_VertexRefId()); + aStorage.RebindVertexEdge(anEndVtx, BRepGraph_VertexId(), anEdgeId, BRepGraph_VertexRefId()); + // Clear geometry RepId fields on the edge definition. + BRepGraphInc::EdgeDef& aMutEdge = aStorage.ChangeEdge(anEdgeId); + if (aMutEdge.Curve3DRepId.IsValid()) { - continue; + aStorage.MarkRemoved(aMutEdge.Curve3DRepId); } - const BRepGraph_VertexId anIntV = aStorage.VertexRef(anIntRefId).VertexDefId; - if (anIntV.IsValid()) + if (aMutEdge.Polygon3DRepId.IsValid()) { - aRI.UnbindVertexFromEdge(anIntV, anEdgeId); + aStorage.MarkRemoved(aMutEdge.Polygon3DRepId); } + break; } - // Keep reverse edge->coedge table coherent for pure removals too. - unbindCoEdgesOfRemovedEdge(aStorage, anEdgeId); + case BRepGraph_NodeId::Kind::CoEdge: { + const BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_CoEdgeId::FromNodeId(theNode); + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(aCoEdgeId); + aStorage.MarkRemoved(aCoEdge.Curve2DRepId); + aStorage.MarkRemoved(aCoEdge.Polygon2DRepId); + aStorage.MarkRemoved(aCoEdge.PolygonOnTriRepId); + if (aCoEdge.ParentWireId.IsValid(aStorage.NbWires())) + { + aStorage.DetachCoEdgeUse(aCoEdge.ParentWireId, aCoEdgeId); + } + break; + } + case BRepGraph_NodeId::Kind::Face: { + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::FromNodeId(theNode); + // Mark face as removed before detaching refs to prevent re-dirtying + // via markRefModified -> markModified during WireRef cleanup. + myGraph->myData->myIncStorage.MarkRemoved(theNode); + // Clear geometry RepId fields on the face definition. + BRepGraphInc::FaceDef& aMutFace = aStorage.ChangeFace(aFaceId); + if (aMutFace.SurfaceRepId.IsValid()) + { + aStorage.MarkRemoved(aMutFace.SurfaceRepId); + } + if (aMutFace.TriangulationRepId.IsValid()) + { + aStorage.MarkRemoved(aMutFace.TriangulationRepId); + } + while (!aStorage.FaceRelations(aFaceId).WireRefIds.IsEmpty()) + { + const BRepGraph_WireRefId aWireRefId = aStorage.FaceRelations(aFaceId).WireRefIds.First(); + markOwnedRefRemoved(aWireRefId); + aStorage.DetachWireFromFace(aFaceId, aWireRefId); + } + while (!aStorage.FaceRelations(aFaceId).ParentFaceRefIds.IsEmpty()) + { + const BRepGraph_FaceRefId aFaceRefId = + aStorage.FaceRelations(aFaceId).ParentFaceRefIds.First(); + const BRepGraph_ShellId aParentShellId = aStorage.FaceRef(aFaceRefId).ParentShellId; + markOwnedRefRemoved(aFaceRefId); + aStorage.DetachFaceFromShell(aParentShellId, aFaceRefId); + } + // FullCoEdgeIterator required: CoEdges are already marked removed by RemoveSubgraph + // before RemoveNode(Face) runs, so the default iterator would skip them. + for (BRepGraph_FullCoEdgeIterator aCoEdgeIt(*myGraph); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Current(); + if (aCoEdge.FaceId != aFaceId) + { + continue; + } + BRepGraphInc::CoEdgeDef& aMutCoEdge = aStorage.ChangeCoEdge(aCoEdgeId); + aMutCoEdge.FaceId = BRepGraph_FaceId(); + if (aMutCoEdge.Curve2DRepId.IsValid()) + { + aStorage.MarkRemoved(aMutCoEdge.Curve2DRepId); + } + if (aMutCoEdge.Polygon2DRepId.IsValid()) + { + aStorage.MarkRemoved(aMutCoEdge.Polygon2DRepId); + } + if (aMutCoEdge.PolygonOnTriRepId.IsValid()) + { + aStorage.MarkRemoved(aMutCoEdge.PolygonOnTriRepId); + } + } + break; + } + case BRepGraph_NodeId::Kind::Shell: + forEachId(aStorage.ShellRelations(BRepGraph_ShellId::FromNodeId(theNode)).FaceRefIds, + markOwnedRefRemoved); + break; + case BRepGraph_NodeId::Kind::Solid: + forEachId(aStorage.SolidRelations(BRepGraph_SolidId::FromNodeId(theNode)).ShellRefIds, + markOwnedRefRemoved); + break; + case BRepGraph_NodeId::Kind::CompSolid: + forEachId(aStorage.CompSolidRelations(BRepGraph_CompSolidId::FromNodeId(theNode)).SolidRefIds, + markOwnedRefRemoved); + break; + case BRepGraph_NodeId::Kind::Compound: + forEachId(aStorage.CompoundRelations(BRepGraph_CompoundId::FromNodeId(theNode)).ChildRefIds, + markOwnedRefRemoved); + break; + case BRepGraph_NodeId::Kind::Product: + forEachId( + aStorage.ProductRelations(BRepGraph_ProductId::FromNodeId(theNode)).OccurrenceRefIds, + markOwnedRefRemoved); + break; + default: + break; } // Mark removed on the entity (which is the sole definition store). BRepGraphInc::BaseDef* aDef = myGraph->changeTopoEntity(theNode); - if (aDef != nullptr && !aDef->IsRemoved) + if (aDef != nullptr && !theNode.IsRemoved(*myGraph)) { myGraph->myData->myIncStorage.MarkRemoved(theNode); // If this is a product, remove from root products. if (theNode.NodeKind == BRepGraph_NodeId::Kind::Product) { - removeFromRootProducts(myGraph->myData->myRootProductIds, + removeFromRootProducts(myGraph->myData->myIncStorage.ChangeRootProductIds(), BRepGraph_ProductId::FromNodeId(theNode)); } } // Increment OwnGen + SubtreeGen so generation-based cache freshness detects the removal. - BRepGraphInc::BaseDef* aRemovedDef = myGraph->changeTopoEntity(theNode); - if (aRemovedDef != nullptr) + if (aDef != nullptr) { - ++aRemovedDef->OwnGen; - ++aRemovedDef->SubtreeGen; + ++aDef->OwnGen; + ++aDef->SubtreeGen; } { - std::unique_lock aWriteLock(myGraph->myData->myCurrentShapesMutex); - myGraph->myData->myCurrentShapes.UnBind(theNode); + const BRepGraph_UID aUID = + (aDef != nullptr) ? BRepGraph_UID(theNode.NodeKind, aDef->UID) : BRepGraph_UID(); + if (aUID.IsValid()) + { + std::unique_lock aLock(myGraph->myData->myIncStorage.myUIDToNodeIdMutex); + myGraph->myData->myIncStorage.myUIDToNodeId.UnBind(aUID); + } } + myGraph->myData->myIncStorage.UnbindCurrentShape(theNode); +} + +//================================================================================================= + +void BRepGraph::EditorView::GenOps::ReplaceNode(const BRepGraph_NodeId theNode, + const BRepGraph_NodeId theReplacement) +{ + if (!theNode.IsValid() || theReplacement == theNode) + { + return; + } + myGraph->Editor().requireUnlocked(theNode, "BRepGraph::EditorView::ReplaceNode(): locked node"); + myGraph->Editor().requireNoActiveGuard( + theNode, + "BRepGraph::EditorView::ReplaceNode(): guard active on node"); + + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theReplacement.IsValid() || !isNodeIndexInRange(aStorage, theReplacement) + || !isActiveNode(aStorage, theReplacement)) + { + RemoveNode(theNode); + return; + } + + if (theNode.NodeKind == BRepGraph_NodeId::Kind::Edge + && theReplacement.NodeKind == BRepGraph_NodeId::Kind::Edge) + { + Standard_ASSERT_RETURN(isNodeIndexInRange(aStorage, theNode), + "ReplaceNode: source edge index is out of range", + Standard_VOID_RETURN); + Standard_ASSERT_RETURN(!aStorage.IsRemoved(BRepGraph_EdgeId(theReplacement)), + "ReplaceNode: replacement edge must be active", + Standard_VOID_RETURN); + + rebindCoEdgesForEdgeReplacement(aStorage, + BRepGraph_EdgeId::FromNodeId(theNode), + BRepGraph_EdgeId::FromNodeId(theReplacement)); + } + + Standard_ASSERT_RETURN(isNodeIndexInRange(aStorage, theNode), + "ReplaceNode: node index is out of range", + Standard_VOID_RETURN); + if (!isActiveNode(aStorage, theNode)) + { + return; + } + + // Propagate SubtreeGen upward to parents before unbinding relation entries, + // so generation-based caches on parent nodes detect the removal. + switch (theNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + case BRepGraph_NodeId::Kind::CoEdge: + case BRepGraph_NodeId::Kind::Product: + case BRepGraph_NodeId::Kind::Occurrence: + break; + default: + myGraph->propagateSubtreeGen(theNode); + break; + } + + if (theNode.NodeKind == BRepGraph_NodeId::Kind::Edge) + { + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::FromNodeId(theNode); + const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(anEdgeId); + unbindCoEdgesOfRemovedEdge(aStorage, anEdgeId); + + auto markOwnedRefRemoved = [&](const BRepGraph_RefId theRef) { + if (!isRefInRange(aStorage, theRef) || theRef.IsRemoved(*myGraph) + || !aStorage.MarkRemovedRef(theRef)) + { + return; + } + myGraph->markRefModified(theRef); + const BRepGraph_RefUID aUID(theRef.RefKind, aStorage.BaseRef(theRef).UID); + if (aUID.IsValid()) + { + std::unique_lock aLock( + myGraph->myData->myIncStorage.myRefUIDToRefIdMutex); + myGraph->myData->myIncStorage.myRefUIDToRefId.UnBind(aUID); + } + myGraph->myData->myLayerRegistry.DispatchOnRefRemoved(theRef); + }; + markOwnedRefRemoved(anEdge.StartVertexRefId); + markOwnedRefRemoved(anEdge.EndVertexRefId); + } + else if (theNode.NodeKind == BRepGraph_NodeId::Kind::CoEdge) + { + const BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_CoEdgeId::FromNodeId(theNode); + if (aCoEdgeId.IsValid(aStorage.NbCoEdges()) && !aStorage.IsRemoved(aCoEdgeId)) + { + const BRepGraph_WireId aParentWireId = aStorage.CoEdge(aCoEdgeId).ParentWireId; + if (aParentWireId.IsValid(aStorage.NbWires())) + { + aStorage.DetachCoEdgeUse(aParentWireId, aCoEdgeId); + } + } + } + else if (theNode.NodeKind == BRepGraph_NodeId::Kind::Wire) + { + const BRepGraph_WireId aWireId = BRepGraph_WireId::FromNodeId(theNode); + if (aWireId.IsValid(aStorage.NbWires())) + { + const NCollection_LinearVector& aCoEdges = + aStorage.WireRelations(aWireId).CoEdgeIds; + const size_t aNbCoEdges = aCoEdges.Size(); + NCollection_LocalArray aCoEdgeSnapshot( + static_cast(aNbCoEdges)); + NCollection_LocalArray anEdgeSnapshot( + static_cast(aNbCoEdges)); + for (size_t aCoEdgeIdx = 0; aCoEdgeIdx < aNbCoEdges; ++aCoEdgeIdx) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdges.Value(aCoEdgeIdx); + aCoEdgeSnapshot[static_cast(aCoEdgeIdx)] = aCoEdgeId; + anEdgeSnapshot[static_cast(aCoEdgeIdx)] = + aCoEdgeId.IsValid(aStorage.NbCoEdges()) ? aStorage.CoEdge(aCoEdgeId).ChildEdgeId + : BRepGraph_EdgeId(); + } + for (size_t aCoEdgeIdx = 0; aCoEdgeIdx < aNbCoEdges; ++aCoEdgeIdx) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeSnapshot[static_cast(aCoEdgeIdx)]; + if (aCoEdgeId.IsValid(aStorage.NbCoEdges()) && !aStorage.IsRemoved(aCoEdgeId)) + { + aStorage.DetachCoEdgeUse(aWireId, aCoEdgeId); + aStorage.SetRemoved(aCoEdgeId, true); + myGraph->markModified(BRepGraph_NodeId(aCoEdgeId)); + } + } + for (BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_CoEdgeId::Start(); + aCoEdgeId.IsValid(aStorage.NbCoEdges()); + ++aCoEdgeId) + { + if (!aStorage.IsRemoved(aCoEdgeId) && aStorage.CoEdge(aCoEdgeId).ParentWireId == aWireId) + { + aStorage.DetachCoEdgeUse(aWireId, aCoEdgeId); + aStorage.SetRemoved(aCoEdgeId, true); + myGraph->markModified(BRepGraph_NodeId(aCoEdgeId)); + } + } + } + } + + // Mark removed on the entity (which is the sole definition store). + BRepGraphInc::BaseDef* aDef = myGraph->changeTopoEntity(theNode); + if (aDef != nullptr && !theNode.IsRemoved(*myGraph)) + { + myGraph->myData->myIncStorage.MarkRemoved(theNode); + // If this is a product, remove from root products. + if (theNode.NodeKind == BRepGraph_NodeId::Kind::Product) + { + removeFromRootProducts(myGraph->myData->myIncStorage.ChangeRootProductIds(), + BRepGraph_ProductId::FromNodeId(theNode)); + } + } + + // Increment OwnGen + SubtreeGen so generation-based cache freshness detects the removal. + if (aDef != nullptr) + { + ++aDef->OwnGen; + ++aDef->SubtreeGen; + } + + { + const BRepGraph_UID aUID = + (aDef != nullptr) ? BRepGraph_UID(theNode.NodeKind, aDef->UID) : BRepGraph_UID(); + if (aUID.IsValid()) + { + std::unique_lock aLock(myGraph->myData->myIncStorage.myUIDToNodeIdMutex); + myGraph->myData->myIncStorage.myUIDToNodeId.UnBind(aUID); + } + } + + myGraph->myData->myIncStorage.UnbindCurrentShape(theNode); + // Notify registered layers. - myGraph->myLayerRegistry.DispatchOnNodeRemoved(theNode, theReplacement); + myGraph->myData->myLayerRegistry.DispatchOnNodeReplaced(theNode, theReplacement); } //================================================================================================= @@ -1354,11 +2252,11 @@ struct RemoveSubgraphScope : Data(theData), IsOutermost(false) { - ++Data.myRemoveSubgraphDepth; - IsOutermost = (Data.myRemoveSubgraphDepth == 1); + Data.myIncStorage.IncrementRemoveSubgraphDepth(); + IsOutermost = (Data.myIncStorage.RemoveSubgraphDepth() == 1); } - ~RemoveSubgraphScope() { --Data.myRemoveSubgraphDepth; } + ~RemoveSubgraphScope() { Data.myIncStorage.DecrementRemoveSubgraphDepth(); } RemoveSubgraphScope(const RemoveSubgraphScope&) = delete; RemoveSubgraphScope& operator=(const RemoveSubgraphScope&) = delete; @@ -1367,8 +2265,14 @@ struct RemoveSubgraphScope void BRepGraph::EditorView::GenOps::RemoveSubgraph(const BRepGraph_NodeId theNode) { - // Cascade-aware reverse-index maintenance: per-kind unbinds inside the recursion - // are skipped; we rebuild once at the outermost call exit. Inner mutations stay O(1). + myGraph->Editor().requireUnlocked(theNode, + "BRepGraph::EditorView::RemoveSubgraph(): locked node"); + myGraph->Editor().requireNoActiveGuard( + theNode, + "BRepGraph::EditorView::RemoveSubgraph(): guard active on node"); + + // Cascade-aware relation maintenance: each removal updates only affected + // relation entries; the outermost call validates the final invariant. RemoveSubgraphScope aScope(*myGraph->myData); const bool isOutermost = aScope.IsOutermost; @@ -1380,7 +2284,7 @@ void BRepGraph::EditorView::GenOps::RemoveSubgraph(const BRepGraph_NodeId theNod // - Product: occurrence children cascade. // - Occurrence: child occurrence cascade + parent product ref detachment. // All other kinds use the generic ChildExplorer/ParentExplorer cascade. - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; switch (theNode.NodeKind) { @@ -1389,15 +2293,15 @@ void BRepGraph::EditorView::GenOps::RemoveSubgraph(const BRepGraph_NodeId theNod { // Snapshot occurrence indices before iterating, because RemoveSubgraph(Occurrence) // modifies the parent's OccurrenceRefIds via swap-remove. - NCollection_DynamicArray anOccIndices; + NCollection_LinearVector anOccIndices; for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*myGraph, BRepGraph_ProductId::FromNodeId(theNode)); anOccIt.More(); anOccIt.Next()) { - anOccIndices.Append(aStorage.OccurrenceRef(anOccIt.CurrentId()).OccurrenceDefId.Index); + anOccIndices.Append(aStorage.OccurrenceRef(anOccIt.CurrentId()).ChildOccurrenceId.Index); } - for (const int anOccIdx : anOccIndices) + for (const uint32_t anOccIdx : anOccIndices) { RemoveSubgraph(BRepGraph_OccurrenceId(anOccIdx)); } @@ -1411,67 +2315,65 @@ void BRepGraph::EditorView::GenOps::RemoveSubgraph(const BRepGraph_NodeId theNod myGraph->myData->myIncStorage.Occurrence(BRepGraph_OccurrenceId(theNode)); // If the child is a topology node and has no other active usage, cascade into it. - if (anOcc.ChildDefId.IsValid() - && BRepGraph_NodeId::IsTopologyKind(anOcc.ChildDefId.NodeKind)) + if (anOcc.ChildNodeId.IsValid() + && BRepGraph_NodeId::IsTopologyKind(anOcc.ChildNodeId.NodeKind)) { - if (!hasAnyActiveUsage(*myGraph, anOcc.ChildDefId)) + if (!hasAnyActiveUsage(*myGraph, anOcc.ChildNodeId)) { - RemoveSubgraph(anOcc.ChildDefId); + RemoveSubgraph(anOcc.ChildNodeId); } } - // Detach from parent product's OccurrenceRefIds. - // Find the parent product by scanning OccurrenceRefs. - for (BRepGraph_OccurrenceRefId aOccRefId = myGraph->Refs().Occurrences().StartId(); - aOccRefId < myGraph->Refs().Occurrences().EndId(); - ++aOccRefId) + // Detach all parent product memberships for this occurrence. + NCollection_LinearVector aParentProducts; + for (BRepGraph_ProductId aProductId = BRepGraph_ProductId::Start(); + aProductId.IsValid(aStorage.NbProducts()); + ++aProductId) { - const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aOccRefId); - if (aRef.IsRemoved || aRef.OccurrenceDefId != theNode) + if (!productHasOccurrenceDef(aStorage, aProductId, BRepGraph_OccurrenceId(theNode))) { continue; } - - const BRepGraph_ProductId aParentProduct = BRepGraph_ProductId::FromNodeId(aRef.ParentId); - if (!aParentProduct.IsValid(aStorage.NbProducts())) + aParentProducts.Append(aProductId); + } + for (const BRepGraph_ProductId& aProductId : aParentProducts) + { + if (!aProductId.IsValid(aStorage.NbProducts()) || aStorage.IsRemoved(aProductId)) { - break; + continue; } - - NCollection_DynamicArray& aRefIds = - myGraph->myData->myIncStorage.ChangeProduct(aParentProduct).OccurrenceRefIds; - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aRefIds); - anIt.More(); - anIt.Next(), ++anIdx) + const NCollection_LinearVector& aRefIds = + myGraph->myData->myIncStorage.ProductRelations(aProductId).OccurrenceRefIds; + for (const BRepGraph_OccurrenceRefId& aOccRefId : aRefIds) { - if (anIt.Value() == aOccRefId) + if (!aOccRefId.IsValid(aStorage.NbOccurrenceRefs())) + { + continue; + } + const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aOccRefId); + if (!aStorage.IsRemoved(aOccRefId) && aRef.ChildOccurrenceId == theNode) { myGraph->myData->myIncStorage.MarkRemovedRef(aOccRefId); - if (anIdx < static_cast(aRefIds.Size()) - 1u) + myGraph->myData->myIncStorage.DetachOccurrenceFromProduct(aProductId, aOccRefId); + myGraph->markModified(aProductId); + if (anOcc.ChildNodeId.IsValid()) { - aRefIds.ChangeValue(static_cast(anIdx)) = - aRefIds.Value(aRefIds.Size() - 1u); } - aRefIds.EraseLast(); - myGraph->markModified(aParentProduct); break; } } - break; } // After detaching, if the child product lost its last active parent occurrence, // re-add it to root products. - if (anOcc.ChildDefId.IsValid() - && anOcc.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) + if (anOcc.ChildNodeId.IsValid() + && anOcc.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) { const BRepGraph_ProductId aChildProduct = - BRepGraph_ProductId::FromNodeId(anOcc.ChildDefId); - if (aChildProduct.IsValid(aStorage.NbProducts()) - && !aStorage.Product(aChildProduct).IsRemoved - && !hasAnyActiveUsage(*myGraph, anOcc.ChildDefId)) + BRepGraph_ProductId::FromNodeId(anOcc.ChildNodeId); + if (aChildProduct.IsValid(aStorage.NbProducts()) && !aStorage.IsRemoved(aChildProduct) + && !hasAnyActiveUsage(*myGraph, anOcc.ChildNodeId)) { - myGraph->myData->myRootProductIds.Append(aChildProduct); + myGraph->myData->myIncStorage.ChangeRootProductIds().Append(aChildProduct); } } } @@ -1480,7 +2382,7 @@ void BRepGraph::EditorView::GenOps::RemoveSubgraph(const BRepGraph_NodeId theNod default: { // Generic topology cascade via ChildExplorer(DirectChildren) + ParentExplorer(DirectParents). // Covers Compound, CompSolid, Solid, Shell, Face, Wire, CoEdge, Edge, Vertex. - NCollection_DynamicArray aChildNodes; + NCollection_LinearVector aChildNodes; for (BRepGraph_ChildExplorer anExp(*myGraph, theNode, BRepGraph_ChildExplorer::TraversalMode::DirectChildren); @@ -1506,9 +2408,8 @@ void BRepGraph::EditorView::GenOps::RemoveSubgraph(const BRepGraph_NodeId theNod if (isOutermost) { - myGraph->myData->myIncStorage.BuildReverseIndex(); - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveSubgraph: reverse-index invariant violated"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveSubgraph: relation invariant violated"); } } @@ -1520,138 +2421,40 @@ bool BRepGraph::EditorView::GenOps::RemoveRef(const BRepGraph_RefId theRef) { return false; } - + myGraph->Editor().requireUnlocked(theRef, "BRepGraph::EditorView::RemoveRef(): locked reference"); + myGraph->Editor().requireNoActiveGuard( + theRef, + "BRepGraph::EditorView::RemoveRef(): guard active on reference"); // Snapshot ref state before MarkRemoved flips IsRemoved. - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraphInc::BaseRef& aRef = aStorage.BaseRef(theRef); - if (aRef.IsRemoved) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (theRef.IsRemoved(*myGraph)) { return false; } - const BRepGraph_NodeId aParent = aRef.ParentId; - BRepGraph_NodeId aChildNode; - BRepGraph_NodeId aOccChildDef; - BRepGraph_EdgeId aCoEdgeUnderlyingEdge; - switch (theRef.RefKind) - { - case BRepGraph_RefId::Kind::Shell: - aChildNode = aStorage.ShellRef(BRepGraph_ShellRefId(theRef)).ShellDefId; - break; - case BRepGraph_RefId::Kind::Face: - aChildNode = aStorage.FaceRef(BRepGraph_FaceRefId(theRef)).FaceDefId; - break; - case BRepGraph_RefId::Kind::Wire: - aChildNode = aStorage.WireRef(BRepGraph_WireRefId(theRef)).WireDefId; - break; - case BRepGraph_RefId::Kind::CoEdge: { - aChildNode = aStorage.CoEdgeRef(BRepGraph_CoEdgeRefId(theRef)).CoEdgeDefId; - if (aChildNode.IsValid(aStorage.NbCoEdges())) - { - aCoEdgeUnderlyingEdge = aStorage.CoEdge(BRepGraph_CoEdgeId(aChildNode)).EdgeDefId; - } - break; - } - case BRepGraph_RefId::Kind::Vertex: - aChildNode = aStorage.VertexRef(BRepGraph_VertexRefId(theRef)).VertexDefId; - break; - case BRepGraph_RefId::Kind::Solid: - aChildNode = aStorage.SolidRef(BRepGraph_SolidRefId(theRef)).SolidDefId; - break; - case BRepGraph_RefId::Kind::Child: - aChildNode = aStorage.ChildRef(BRepGraph_ChildRefId(theRef)).ChildDefId; - break; - case BRepGraph_RefId::Kind::Occurrence: { - aChildNode = aStorage.OccurrenceRef(BRepGraph_OccurrenceRefId(theRef)).OccurrenceDefId; - if (aChildNode.IsValid()) - { - aOccChildDef = aStorage.Occurrence(BRepGraph_OccurrenceId(aChildNode)).ChildDefId; - } - break; - } - default: - break; - } - + detachRefRelations(aStorage, theRef); if (!aStorage.MarkRemovedRef(theRef)) { return false; } - - BRepGraphInc_ReverseIndex& aRI = aStorage.ChangeReverseIndex(); - switch (theRef.RefKind) + myGraph->markRefModified(theRef); { - case BRepGraph_RefId::Kind::Face: - if (aParent.NodeKind == BRepGraph_NodeId::Kind::Shell && aChildNode.IsValid()) - { - aRI.UnbindFaceFromShell(BRepGraph_FaceId(aChildNode), BRepGraph_ShellId(aParent)); - } - break; - case BRepGraph_RefId::Kind::Wire: - if (aParent.NodeKind == BRepGraph_NodeId::Kind::Face && aChildNode.IsValid()) - { - aRI.UnbindWireFromFace(BRepGraph_WireId(aChildNode), BRepGraph_FaceId(aParent)); - } - break; - case BRepGraph_RefId::Kind::CoEdge: - if (aParent.NodeKind == BRepGraph_NodeId::Kind::Wire && aChildNode.IsValid()) - { - const BRepGraph_WireId aWireId(aParent); - aRI.UnbindCoEdgeFromWire(BRepGraph_CoEdgeId(aChildNode), aWireId); - if (aCoEdgeUnderlyingEdge.IsValid() - && isLastEdgeUsageInWire(aStorage, - aWireId, - aCoEdgeUnderlyingEdge, - BRepGraph_CoEdgeRefId(theRef))) - { - aRI.UnbindEdgeFromWire(aCoEdgeUnderlyingEdge, aWireId); - } - } - break; - case BRepGraph_RefId::Kind::Vertex: - if (aParent.NodeKind == BRepGraph_NodeId::Kind::Edge && aChildNode.IsValid()) - { - aRI.UnbindVertexFromEdge(BRepGraph_VertexId(aChildNode), BRepGraph_EdgeId(aParent)); - } - break; - case BRepGraph_RefId::Kind::Shell: - if (aParent.NodeKind == BRepGraph_NodeId::Kind::Solid && aChildNode.IsValid()) - { - aRI.UnbindShellFromSolid(BRepGraph_ShellId(aChildNode), BRepGraph_SolidId(aParent)); - } - break; - case BRepGraph_RefId::Kind::Solid: - if (aParent.NodeKind == BRepGraph_NodeId::Kind::CompSolid && aChildNode.IsValid()) - { - aRI.UnbindSolidFromCompSolid(BRepGraph_SolidId(aChildNode), BRepGraph_CompSolidId(aParent)); - } - break; - case BRepGraph_RefId::Kind::Child: - if (aParent.NodeKind == BRepGraph_NodeId::Kind::Compound && aChildNode.IsValid()) - { - aRI.UnbindCompoundChild(aChildNode, BRepGraph_CompoundId(aParent)); - } - break; - case BRepGraph_RefId::Kind::Occurrence: - if (aChildNode.IsValid() && aOccChildDef.IsValid() - && aOccChildDef.NodeKind == BRepGraph_NodeId::Kind::Product) - { - aRI.UnbindProductOccurrence(BRepGraph_OccurrenceId(aChildNode), - BRepGraph_ProductId::FromNodeId(aOccChildDef)); - } - break; - default: - break; + const BRepGraph_RefUID aUID(theRef.RefKind, aStorage.BaseRef(theRef).UID); + if (aUID.IsValid()) + { + std::unique_lock aLock(myGraph->myData->myIncStorage.myRefUIDToRefIdMutex); + myGraph->myData->myIncStorage.myRefUIDToRefId.UnBind(aUID); + } } - myGraph->myLayerRegistry.DispatchOnRefRemoved(theRef); - myGraph->markRefModified(theRef); - Standard_ASSERT_VOID(aStorage.ValidateReverseIndex(), - "BRepGraph::RemoveRef: reverse-index invariant violated"); + myGraph->myData->myLayerRegistry.DispatchOnRefRemoved(theRef); + if (aStorage.RemoveSubgraphDepth() == 0 && !aStorage.DeferredMode()) + { + Standard_ASSERT_VOID(aStorage.ValidateRelations(), + "BRepGraph::RemoveRef: relation invariant violated"); + } return true; } -//================================================================================================= - bool BRepGraph::EditorView::GenOps::RemoveRef(const BRepGraph_NodeId theParent, const BRepGraph_RefId theRef, const bool theToPruneOrphanedChild) @@ -1660,51 +2463,554 @@ bool BRepGraph::EditorView::GenOps::RemoveRef(const BRepGraph_NodeId theParent, { return false; } + myGraph->Editor().requireUnlocked(theRef, "BRepGraph::EditorView::RemoveRef(): locked reference"); + myGraph->Editor().requireNoActiveGuard( + theRef, + "BRepGraph::EditorView::RemoveRef(): guard active on reference"); + Standard_ASSERT_RETURN(isExpectedParentKindForRef(theParent, theRef.RefKind), + "RemoveRef: reference kind is incompatible with parent kind", + false); - const BRepGraphInc::BaseRef& aRef = myGraph->myData->myIncStorage.BaseRef(theRef); - Standard_ASSERT_RETURN(aRef.ParentId == theParent, "RemoveRef: reference parent mismatch", false); - - const BRepGraph_NodeId aChildNode = refChildNode(*myGraph, theRef); - if (!RemoveRef(theRef)) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!isActiveNode(aStorage, theParent) || !isRefInRange(aStorage, theRef) + || theRef.IsRemoved(*myGraph)) { return false; } - if (theToPruneOrphanedChild && aChildNode.IsValid() && !hasAnyActiveUsage(*myGraph, aChildNode)) + const BRepGraph_NodeId aChildNode = refChildNode(*myGraph, theRef); + bool isRemoved = false; + switch (theRef.RefKind) { - RemoveSubgraph(aChildNode); + case BRepGraph_RefId::Kind::Vertex: { + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::FromNodeId(theParent); + const BRepGraph_VertexRefId aVertexRefId(theRef); + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(anEdgeId); + if (anEdge.StartVertexRefId != aVertexRefId && anEdge.EndVertexRefId != aVertexRefId) + { + return false; + } + if (anEdge.StartVertexRefId == aVertexRefId) + { + anEdge.StartVertexRefId = BRepGraph_VertexRefId(); + } + if (anEdge.EndVertexRefId == aVertexRefId) + { + anEdge.EndVertexRefId = BRepGraph_VertexRefId(); + } + if (!isRefOwnedByAnyParent(myGraph->myData->myIncStorage, aVertexRefId) + && !RemoveRef(aVertexRefId)) + { + return false; + } + if (theToPruneOrphanedChild && aChildNode.IsValid() + && !hasAnyActiveUsage(*myGraph, aChildNode)) + { + RemoveSubgraph(aChildNode); + } + isRemoved = true; + break; + } + case BRepGraph_RefId::Kind::Shell: + isRemoved = detachOrderedParentRef( + *myGraph, + myGraph->myData->myIncStorage, + BRepGraph_ShellRefId(theRef), + myGraph->myData->myIncStorage.SolidRelations(BRepGraph_SolidId(theParent)).ShellRefIds, + aChildNode, + theToPruneOrphanedChild); + break; + case BRepGraph_RefId::Kind::Face: + isRemoved = detachOrderedParentRef( + *myGraph, + myGraph->myData->myIncStorage, + BRepGraph_FaceRefId(theRef), + myGraph->myData->myIncStorage.ShellRelations(BRepGraph_ShellId(theParent)).FaceRefIds, + aChildNode, + theToPruneOrphanedChild); + break; + case BRepGraph_RefId::Kind::Wire: + isRemoved = detachOrderedParentRef( + *myGraph, + myGraph->myData->myIncStorage, + BRepGraph_WireRefId(theRef), + myGraph->myData->myIncStorage.FaceRelations(BRepGraph_FaceId(theParent)).WireRefIds, + aChildNode, + theToPruneOrphanedChild); + break; + case BRepGraph_RefId::Kind::Solid: + isRemoved = detachOrderedParentRef( + *myGraph, + myGraph->myData->myIncStorage, + BRepGraph_SolidRefId(theRef), + myGraph->myData->myIncStorage.CompSolidRelations(BRepGraph_CompSolidId(theParent)) + .SolidRefIds, + aChildNode, + theToPruneOrphanedChild); + break; + case BRepGraph_RefId::Kind::Child: + isRemoved = detachOrderedParentRef( + *myGraph, + myGraph->myData->myIncStorage, + BRepGraph_ChildRefId(theRef), + myGraph->myData->myIncStorage.CompoundRelations(BRepGraph_CompoundId(theParent)) + .ChildRefIds, + aChildNode, + theToPruneOrphanedChild); + break; + case BRepGraph_RefId::Kind::Occurrence: + isRemoved = detachOrderedParentRef( + *myGraph, + myGraph->myData->myIncStorage, + BRepGraph_OccurrenceRefId(theRef), + myGraph->myData->myIncStorage.ProductRelations(BRepGraph_ProductId(theParent)) + .OccurrenceRefIds, + aChildNode, + theToPruneOrphanedChild); + break; + default: + isRemoved = RemoveRef(theRef); + break; } + if (!isRemoved) + { + return false; + } + + myGraph->markModified(theParent); return true; } //================================================================================================= -void BRepGraph::EditorView::GenOps::RemoveRep(const BRepGraph_RepId theRep) +void BRepGraph::EditorView::GenOps::CleanupRemovedReferences() { - if (!theRep.IsValid()) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Build a set of all removed NodeIds (BRepGraph_FullXxxIterator includes removed). + NCollection_FlatMap aRemovedNodes; + for (BRepGraph_FullVertexIterator anIt(*myGraph); anIt.More(); anIt.Next()) { - return; + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullEdgeIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullCoEdgeIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullWireIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullFaceIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullShellIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullSolidIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullCompoundIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullCompSolidIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullProductIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } + } + for (BRepGraph_FullOccurrenceIterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + if (aStorage.IsRemoved(anIt.CurrentId())) + { + aRemovedNodes.Add(anIt.CurrentId()); + } } - if (myGraph->myData->myIncStorage.MarkRemovedRep(theRep)) - { - myGraph->markRepModified(theRep); - } -} + // Helper: erase one value from a DynamicArray using shift-down + EraseLast. + auto markRemovedRefEntry = [&](const BRepGraph_RefId theRef) { + if (!isRefInRange(aStorage, theRef) || theRef.IsRemoved(*myGraph) + || !aStorage.MarkRemovedRef(theRef)) + { + return; + } + myGraph->markRefModified(theRef); + const BRepGraph_RefUID aUID(theRef.RefKind, aStorage.BaseRef(theRef).UID); + if (aUID.IsValid()) + { + std::unique_lock aLock(myGraph->myData->myIncStorage.myRefUIDToRefIdMutex); + myGraph->myData->myIncStorage.myRefUIDToRefId.UnBind(aUID); + } + myGraph->myData->myLayerRegistry.DispatchOnRefRemoved(theRef); + }; -//================================================================================================= - -BRepGraph_Curve2DRepId BRepGraph::EditorView::CoEdgeOps::CreateCurve2DRep( - const occ::handle& theCurve2d) -{ - if (theCurve2d.IsNull()) + for (NCollection_FlatMap::Iterator anIt(aRemovedNodes); anIt.More(); + anIt.Next()) { - return BRepGraph_Curve2DRepId(); + const BRepGraph_NodeId aRemovedNode = anIt.Value(); + switch (aRemovedNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Edge: { + const BRepGraphInc::EdgeDef& anEdge = + aStorage.Edge(BRepGraph_EdgeId::FromNodeId(aRemovedNode)); + markRemovedRefEntry(anEdge.StartVertexRefId); + markRemovedRefEntry(anEdge.EndVertexRefId); + break; + } + case BRepGraph_NodeId::Kind::Face: + forEachId(aStorage.FaceRelations(BRepGraph_FaceId::FromNodeId(aRemovedNode)).WireRefIds, + markRemovedRefEntry); + break; + case BRepGraph_NodeId::Kind::Shell: + forEachId(aStorage.ShellRelations(BRepGraph_ShellId::FromNodeId(aRemovedNode)).FaceRefIds, + markRemovedRefEntry); + break; + case BRepGraph_NodeId::Kind::Solid: + forEachId(aStorage.SolidRelations(BRepGraph_SolidId::FromNodeId(aRemovedNode)).ShellRefIds, + markRemovedRefEntry); + break; + case BRepGraph_NodeId::Kind::CompSolid: + forEachId( + aStorage.CompSolidRelations(BRepGraph_CompSolidId::FromNodeId(aRemovedNode)).SolidRefIds, + markRemovedRefEntry); + break; + case BRepGraph_NodeId::Kind::Compound: + forEachId( + aStorage.CompoundRelations(BRepGraph_CompoundId::FromNodeId(aRemovedNode)).ChildRefIds, + markRemovedRefEntry); + break; + case BRepGraph_NodeId::Kind::Product: + forEachId( + aStorage.ProductRelations(BRepGraph_ProductId::FromNodeId(aRemovedNode)).OccurrenceRefIds, + markRemovedRefEntry); + break; + default: + break; + } } - const BRepGraph_Curve2DRepId aRepId = myGraph->myData->myIncStorage.AppendCurve2DRep(); - myGraph->myData->myIncStorage.ChangeCurve2DRep(aRepId).Curve = theCurve2d; - return aRepId; + // Phase 2: CoEdgeDefs - clear ChildEdgeId / FaceId. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_CoEdgeId aCEId = anIt.CurrentId(); + const BRepGraphInc::CoEdgeDef& aCE = anIt.Current(); + if (aCE.ChildEdgeId.IsValid(aStorage.NbEdges()) && aRemovedNodes.Contains(aCE.ChildEdgeId)) + { + const BRepGraph_EdgeId anOldEdge = aCE.ChildEdgeId; + aStorage.ChangeCoEdge(aCEId).ChildEdgeId = BRepGraph_EdgeId(); + aStorage.RebindCoEdgeEdge(aCEId, anOldEdge, BRepGraph_EdgeId()); + } + if (aCE.FaceId.IsValid(aStorage.NbFaces()) && aRemovedNodes.Contains(aCE.FaceId)) + { + BRepGraphInc::CoEdgeDef& aMutCE = aStorage.ChangeCoEdge(aCEId); + aMutCE.FaceId = BRepGraph_FaceId(); + clearCoEdgeFaceScopedRepresentations(aStorage, aMutCE); + } + } + + // Phase 3: EdgeDefs - clear vertex refs. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_EdgeId anEdgeId = anIt.CurrentId(); + const BRepGraphInc::EdgeDef& anEdge = anIt.Current(); + if (anEdge.StartVertexRefId.IsValid(aStorage.NbVertexRefs())) + { + const BRepGraphInc::VertexRef& aVRef = aStorage.VertexRef(anEdge.StartVertexRefId); + if (aStorage.IsRemoved(anEdge.StartVertexRefId) + || (aVRef.ChildVertexId.IsValid(aStorage.NbVertices()) + && aRemovedNodes.Contains(aVRef.ChildVertexId))) + { + const BRepGraph_VertexId aVertexId = aVRef.ChildVertexId; + if (aVertexId.IsValid()) + { + } + markRemovedRefEntry(anEdge.StartVertexRefId); + aStorage.ChangeEdge(anEdgeId).StartVertexRefId = BRepGraph_VertexRefId(); + aStorage.RebindVertexEdge(aVertexId, + BRepGraph_VertexId(), + anEdgeId, + BRepGraph_VertexRefId()); + } + } + if (anEdge.EndVertexRefId.IsValid(aStorage.NbVertexRefs())) + { + const BRepGraphInc::VertexRef& aVRef = aStorage.VertexRef(anEdge.EndVertexRefId); + if (aStorage.IsRemoved(anEdge.EndVertexRefId) + || (aVRef.ChildVertexId.IsValid(aStorage.NbVertices()) + && aRemovedNodes.Contains(aVRef.ChildVertexId))) + { + const BRepGraph_VertexId aVertexId = aVRef.ChildVertexId; + if (aVertexId.IsValid()) + { + } + markRemovedRefEntry(anEdge.EndVertexRefId); + aStorage.ChangeEdge(anEdgeId).EndVertexRefId = BRepGraph_VertexRefId(); + aStorage.RebindVertexEdge(aVertexId, + BRepGraph_VertexId(), + anEdgeId, + BRepGraph_VertexRefId()); + } + } + } + + // Phase 4a: Mark orphaned CoEdges as removed. A CoEdge whose ChildEdgeId was + // cleared in Phase 2 has no topological identity and must be retired before + // wire refs are pruned (so Phase 4b's IsRemoved check picks it up). + // Note: do not check FaceId here - free-wire coedges have FaceId + // intentionally invalid and must not be retired. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_CoEdgeId aCEId = anIt.CurrentId(); + const BRepGraphInc::CoEdgeDef& aCE = anIt.Current(); + if (aCE.ParentWireId.IsValid(aStorage.NbWires()) && aRemovedNodes.Contains(aCE.ParentWireId)) + { + aStorage.DetachCoEdgeUse(aCE.ParentWireId, aCEId); + aStorage.MarkRemoved(BRepGraph_NodeId(aCEId)); + } + else if (!aCE.ChildEdgeId.IsValid()) + { + aStorage.MarkRemoved(BRepGraph_NodeId(aCEId)); + } + } + + // Phase 4b: WireDefs - clean CoEdges that are removed/dangling. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_WireId aWireId = anIt.CurrentId(); + for (size_t aCoEdgeIdx = 0; aCoEdgeIdx < aStorage.WireRelations(aWireId).CoEdgeIds.Size();) + { + const BRepGraph_CoEdgeId aCEId = aStorage.WireRelations(aWireId).CoEdgeIds.Value(aCoEdgeIdx); + if (!aCEId.IsValid(aStorage.NbCoEdges())) + { + ++aCoEdgeIdx; + continue; + } + const BRepGraphInc::CoEdgeDef& aCE = aStorage.CoEdge(aCEId); + if (aStorage.IsRemoved(aCEId) || !aCE.ChildEdgeId.IsValid() + || (aCE.ChildEdgeId.IsValid(aStorage.NbEdges()) + && aRemovedNodes.Contains(aCE.ChildEdgeId))) + { + aStorage.DetachCoEdgeUse(aWireId, aCEId); + if (aCE.ChildEdgeId.IsValid() + && !wireHasActiveEdgeThroughAnotherCoEdge(aStorage, aWireId, aCE.ChildEdgeId, aCEId)) + { + } + continue; + } + ++aCoEdgeIdx; + } + } + + // Phase 5: FaceDefs - clean WireRefs. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_FaceId aFaceId = anIt.CurrentId(); + // WireRefs + for (size_t aWireRefIdx = 0; aWireRefIdx < aStorage.FaceRelations(aFaceId).WireRefIds.Size();) + { + const BRepGraph_WireRefId aWRId = + aStorage.FaceRelations(aFaceId).WireRefIds.Value(aWireRefIdx); + const BRepGraphInc::WireRef& aWR = aStorage.WireRef(aWRId); + if (aStorage.IsRemoved(aWRId)) + { + ++aWireRefIdx; + continue; + } + if (aWR.ChildWireId.IsValid(aStorage.NbWires()) && aRemovedNodes.Contains(aWR.ChildWireId)) + { + aStorage.MarkRemovedRef(BRepGraph_RefId(aWRId)); + aStorage.DetachWireFromFace(aFaceId, aWRId); + continue; + } + ++aWireRefIdx; + } + } + + // Phase 6: ShellDefs - clean FaceRefs. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_ShellId aShellId = anIt.CurrentId(); + // FaceRefs + for (size_t aFaceRefIdx = 0; aFaceRefIdx < aStorage.ShellRelations(aShellId).FaceRefIds.Size();) + { + const BRepGraph_FaceRefId aFRId = + aStorage.ShellRelations(aShellId).FaceRefIds.Value(aFaceRefIdx); + const BRepGraphInc::FaceRef& aFR = aStorage.FaceRef(aFRId); + if (aStorage.IsRemoved(aFRId)) + { + ++aFaceRefIdx; + continue; + } + if (aFR.ChildFaceId.IsValid(aStorage.NbFaces()) && aRemovedNodes.Contains(aFR.ChildFaceId)) + { + aStorage.MarkRemovedRef(BRepGraph_RefId(aFRId)); + aStorage.DetachFaceFromShell(aShellId, aFRId); + continue; + } + ++aFaceRefIdx; + } + } + + // Phase 7: SolidDefs - clean ShellRefs. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_SolidId aSolidId = anIt.CurrentId(); + // ShellRefs + for (size_t aShellRefIdx = 0; + aShellRefIdx < aStorage.SolidRelations(aSolidId).ShellRefIds.Size();) + { + const BRepGraph_ShellRefId aSRId = + aStorage.SolidRelations(aSolidId).ShellRefIds.Value(aShellRefIdx); + const BRepGraphInc::ShellRef& aSR = aStorage.ShellRef(aSRId); + if (aStorage.IsRemoved(aSRId)) + { + ++aShellRefIdx; + continue; + } + if (aSR.ChildShellId.IsValid(aStorage.NbShells()) && aRemovedNodes.Contains(aSR.ChildShellId)) + { + aStorage.MarkRemovedRef(BRepGraph_RefId(aSRId)); + aStorage.DetachShellFromSolid(aSolidId, aSRId); + continue; + } + ++aShellRefIdx; + } + } + + // Phase 8: CompoundDefs - clean ChildRefs. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_CompoundId aCompId = anIt.CurrentId(); + for (size_t aChildRefIdx = 0; + aChildRefIdx < aStorage.CompoundRelations(aCompId).ChildRefIds.Size();) + { + const BRepGraph_ChildRefId aCRId = + aStorage.CompoundRelations(aCompId).ChildRefIds.Value(aChildRefIdx); + const BRepGraphInc::ChildRef& aCR = aStorage.ChildRef(aCRId); + if (aStorage.IsRemoved(aCRId)) + { + ++aChildRefIdx; + continue; + } + if (aCR.ChildNodeId.IsValid() && aRemovedNodes.Contains(aCR.ChildNodeId)) + { + aStorage.MarkRemovedRef(BRepGraph_RefId(aCRId)); + aStorage.DetachChildFromCompound(aCompId, aCRId); + continue; + } + ++aChildRefIdx; + } + } + + // Phase 9: CompSolidDefs - clean SolidRefs. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_CompSolidId aCSId = anIt.CurrentId(); + for (size_t aSolidRefIdx = 0; + aSolidRefIdx < aStorage.CompSolidRelations(aCSId).SolidRefIds.Size();) + { + const BRepGraph_SolidRefId aSRId = + aStorage.CompSolidRelations(aCSId).SolidRefIds.Value(aSolidRefIdx); + const BRepGraphInc::SolidRef& aSR = aStorage.SolidRef(aSRId); + if (aStorage.IsRemoved(aSRId)) + { + ++aSolidRefIdx; + continue; + } + if (aSR.ChildSolidId.IsValid(aStorage.NbSolids()) && aRemovedNodes.Contains(aSR.ChildSolidId)) + { + aStorage.MarkRemovedRef(BRepGraph_RefId(aSRId)); + aStorage.DetachSolidFromCompSolid(aCSId, aSRId); + continue; + } + ++aSolidRefIdx; + } + } + + // Phase 10: OccurrenceDefs - clear ChildNodeId and retire orphan Occurrences. + // An Occurrence with no live child has no semantic meaning, so mark it + // removed and add it to aRemovedNodes for downstream cleanup phases. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_OccurrenceId anOccId = anIt.CurrentId(); + const BRepGraphInc::OccurrenceDef& anOcc = anIt.Current(); + if (anOcc.ChildNodeId.IsValid() && aRemovedNodes.Contains(anOcc.ChildNodeId)) + { + aStorage.ChangeOccurrence(anOccId).ChildNodeId = BRepGraph_NodeId(); + aStorage.MarkRemoved(BRepGraph_NodeId(anOccId)); + aRemovedNodes.Add(BRepGraph_NodeId(anOccId)); + } + } + + // Phase 11: ProductDefs - clean OccurrenceRefs. + for (BRepGraph_Iterator anIt(*myGraph); anIt.More(); anIt.Next()) + { + const BRepGraph_ProductId aProdId = anIt.CurrentId(); + for (size_t anOccRefIdx = 0; + anOccRefIdx < aStorage.ProductRelations(aProdId).OccurrenceRefIds.Size();) + { + const BRepGraph_OccurrenceRefId aORId = + aStorage.ProductRelations(aProdId).OccurrenceRefIds.Value(anOccRefIdx); + const BRepGraphInc::OccurrenceRef& aOR = aStorage.OccurrenceRef(aORId); + if (aStorage.IsRemoved(aORId)) + { + ++anOccRefIdx; + continue; + } + if (aOR.ChildOccurrenceId.IsValid(aStorage.NbOccurrences()) + && aRemovedNodes.Contains(aOR.ChildOccurrenceId)) + { + aStorage.MarkRemovedRef(BRepGraph_RefId(aORId)); + aStorage.DetachOccurrenceFromProduct(aProdId, aORId); + continue; + } + ++anOccRefIdx; + } + } + + aStorage.RecountActiveCounts(); + Standard_ASSERT_VOID(aStorage.ValidateRelations(), + "CleanupRemovedReferences: relation invariant violated"); } //================================================================================================= @@ -1712,73 +3018,137 @@ BRepGraph_Curve2DRepId BRepGraph::EditorView::CoEdgeOps::CreateCurve2DRep( void BRepGraph::EditorView::CoEdgeOps::SetPCurve(const BRepGraph_CoEdgeId theCoEdge, const occ::handle& theCurve2d) { - BRepGraph_MutGuard aCoEdge = myGraph->Editor().CoEdges().Mut(theCoEdge); - aCoEdge.Internal().Curve2DRepId = - theCurve2d.IsNull() ? BRepGraph_Curve2DRepId() : CreateCurve2DRep(theCurve2d); + if (theCurve2d.IsNull()) + { + ClearPCurve(theCoEdge); + return; + } + + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (theCoEdge.IsValid(aStorage.NbCoEdges()) && !aStorage.IsRemoved(theCoEdge)) + { + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(theCoEdge); + if (aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + && !aStorage.IsRemoved(aCoEdge.Curve2DRepId)) + { + const BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + SetPCurve(theCoEdge, theCurve2d, aUse.ParamFirst, aUse.ParamLast); + return; + } + } + + SetPCurve(theCoEdge, theCurve2d, theCurve2d->FirstParameter(), theCurve2d->LastParameter()); } //================================================================================================= -void BRepGraph::EditorView::CoEdgeOps::AddPCurve(const BRepGraph_EdgeId theEdgeEntity, - const BRepGraph_FaceId theFaceEntity, - const occ::handle& theCurve2d, - const double theFirst, - const double theLast, - const TopAbs_Orientation theEdgeOrientation) +BRepGraph_CoEdgeId BRepGraph::EditorView::CoEdgeOps::Add( + const BRepGraph_EdgeId theEdge, + const BRepGraphInc::ParityOrientation theOrientation) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!isActiveNode(aStorage, theEdge)) + { + return BRepGraph_CoEdgeId(); + } + + const BRepGraph_CoEdgeId aCoEdgeId(aStorage.NbCoEdges()); + aStorage.AppendCoEdge(); + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(aCoEdgeId); + aCoEdge.ChildEdgeId = theEdge; + aCoEdge.Orientation = theOrientation; + BRepGraphInc::EdgeRelations& anEdgeRel = aStorage.ChangeEdgeRelationsInternal(theEdge); + appendUniqueRelationId(anEdgeRel.CoEdgeIds, aCoEdgeId); + myGraph->allocateUID(aCoEdgeId); + myGraph->markModified(aCoEdgeId); + myGraph->markModified(theEdge); + return aCoEdgeId; +} + +//================================================================================================= + +BRepGraph_CoEdgeId BRepGraph::EditorView::CoEdgeOps::Add( + const BRepGraph_EdgeId theEdgeEntity, + const BRepGraph_FaceId theFaceEntity, + const occ::handle& theCurve2d, + const double theFirst, + const double theLast, + const BRepGraphInc::ParityOrientation theEdgeOrientation) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (!isActiveNode(aStorage, theEdgeEntity) || !isActiveNode(aStorage, theFaceEntity) || theCurve2d.IsNull()) { - return; + return BRepGraph_CoEdgeId(); } // Create CoEdge entity for the new PCurve binding. const BRepGraph_CoEdgeId aCoEdgeId(aStorage.NbCoEdges()); aStorage.AppendCoEdge(); BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(aCoEdgeId); - aCoEdge.EdgeDefId = theEdgeEntity; - aCoEdge.FaceDefId = theFaceEntity; + aCoEdge.ChildEdgeId = theEdgeEntity; + aCoEdge.FaceId = theFaceEntity; aCoEdge.Orientation = theEdgeOrientation; if (!theCurve2d.IsNull()) { - const BRepGraph_Curve2DRepId aCurve2DRepId = aStorage.AppendCurve2DRep(); - aStorage.ChangeCurve2DRep(aCurve2DRepId).Curve = theCurve2d; - aCoEdge.Curve2DRepId = aCurve2DRepId; + // Create owned use record + const BRepGraph_CoEdgeCurve2DRepId aRepId = aStorage.AppendCoEdgeCurve2DRep(); + BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.ChangeCoEdgeCurve2DRep(aRepId); + aUse.ParentCoEdgeId = aCoEdgeId; + aUse.Curve = theCurve2d; + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + aCoEdge.Curve2DRepId = aRepId; } - aCoEdge.ParamFirst = theFirst; - aCoEdge.ParamLast = theLast; - // Update reverse indices. - aStorage.ChangeReverseIndex().BindEdgeToCoEdge(theEdgeEntity, aCoEdgeId); - aStorage.ChangeReverseIndex().BindEdgeToFace(theEdgeEntity, theFaceEntity); + BRepGraphInc::EdgeRelations& anEdgeRel = aStorage.ChangeEdgeRelationsInternal(theEdgeEntity); + appendUniqueRelationId(anEdgeRel.CoEdgeIds, aCoEdgeId); + myGraph->allocateUID(aCoEdgeId); + myGraph->markModified(aCoEdgeId); myGraph->markModified(theEdgeEntity); + myGraph->markModified(theFaceEntity); + return aCoEdgeId; } //================================================================================================= void BRepGraph::EditorView::BeginDeferredInvalidation() { - myGraph->myData->myDeferredMode.store(true, std::memory_order_relaxed); + myGraph->myData->myIncStorage.SetDeferredMode(true); } //================================================================================================= void BRepGraph::EditorView::EndDeferredInvalidation() noexcept { - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { return; } - myGraph->myData->myDeferredMode.store(false, std::memory_order_relaxed); + myGraph->myData->myIncStorage.SetDeferredMode(false); - NCollection_DynamicArray& aDeferredList = myGraph->myData->myDeferredModified; + NCollection_LinearVector& aDeferredList = + myGraph->myData->myIncStorage.ChangeDeferredModified(); if (aDeferredList.IsEmpty()) { + NCollection_LinearVector& aDeferredRefList = + myGraph->myData->myIncStorage.ChangeDeferredRefModified(); + if (!aDeferredRefList.IsEmpty() + && myGraph->myData->myLayerRegistry.HasRefModificationSubscribers()) + { + int aRefKindsMask = 0; + for (const BRepGraph_RefId& aRef : aDeferredRefList) + { + aRefKindsMask |= BRepGraph_Layer::RefKindBit(aRef.RefKind); + } + myGraph->myData->myLayerRegistry.DispatchRefsModified(aDeferredRefList.ToArray1(), + aRefKindsMask); + } + myGraph->myData->myIncStorage.ClearDeferredQueues(); return; } @@ -1787,8 +3157,7 @@ void BRepGraph::EditorView::EndDeferredInvalidation() noexcept // Propagate SubtreeGen upward from each directly-modified node. // Dense per-kind visited flags for O(1) lookup without hashing overhead. - const BRepGraphInc_ReverseIndex& aRevIdx = myGraph->myData->myIncStorage.ReverseIndex(); - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; // Dense visited arrays indexed by entity index per kind. // NCollection_Array1 with bool values: O(1) checked/set by index. @@ -1839,8 +3208,8 @@ void BRepGraph::EditorView::EndDeferredInvalidation() noexcept // BFS-style upward propagation: process nodes front-to-back, appending // newly discovered parents at the end. The loop index advances through // the growing vector, so each node is visited exactly once. - NCollection_DynamicArray aAllModified; - aAllModified.SetIncrement(aDeferredList.Size() * 2); + NCollection_LinearVector aAllModified; + aAllModified.Reserve(aDeferredList.Size() * 2); int aModifiedKindsMask = 0; // Seed with directly modified nodes. @@ -1860,7 +3229,22 @@ void BRepGraph::EditorView::EndDeferredInvalidation() noexcept { const BRepGraph_NodeId aNodeId = aAllModified.Value(i); - // Collect parent NodeIds via reverse index and increment SubtreeGen. + const auto appendParent = [&](const BRepGraph_NodeId theParentId) { + if (!markVisited(theParentId)) + { + return; + } + BRepGraphInc::BaseDef* aParent = myGraph->changeTopoEntity(theParentId); + if (aParent == nullptr || theParentId.IsRemoved(*myGraph)) + { + return; + } + ++aParent->SubtreeGen; + aAllModified.Append(theParentId); + aModifiedKindsMask |= BRepGraph_Layer::KindBit(theParentId.NodeKind); + }; + + // Collect parent NodeIds via relation and increment SubtreeGen. // NOT OwnGen - parent's own data didn't change. // The visited set ensures each parent is incremented exactly once per flush, // even in diamond topologies. This matches immediate-mode LastPropWave semantics. @@ -1870,174 +3254,245 @@ void BRepGraph::EditorView::EndDeferredInvalidation() noexcept // Vertex modifications don't propagate in deferred mode. break; case BRepGraph_NodeId::Kind::Edge: { - const NCollection_DynamicArray* aWires = - aRevIdx.WiresOfEdge(BRepGraph_EdgeId(aNodeId)); - if (aWires != nullptr) + for (BRepGraph_WiresOfEdge aWireIt = + myGraph->Topo().Edges().WiresOf(BRepGraph_EdgeId(aNodeId)); + aWireIt.More(); + aWireIt.Next()) { - for (const BRepGraph_WireId& aWireId : *aWires) + appendParent(aWireIt.CurrentId()); + } + break; + } + case BRepGraph_NodeId::Kind::CoEdge: { + const BRepGraph_CoEdgeId aCoEdgeId(aNodeId); + if (aCoEdgeId.IsValid(aStorage.NbCoEdges()) && !aStorage.IsRemoved(aCoEdgeId)) + { + const BRepGraph_WireId aWireId = aStorage.CoEdge(aCoEdgeId).ParentWireId; + if (aWireId.IsValid(aStorage.NbWires())) { - const BRepGraph_NodeId aParentId = aWireId; - if (!markVisited(aParentId)) - { - continue; - } - BRepGraphInc::BaseDef* aParent = myGraph->changeTopoEntity(aParentId); - if (aParent == nullptr || aParent->IsRemoved) - { - continue; - } - ++aParent->SubtreeGen; - aAllModified.Append(aParentId); - aModifiedKindsMask |= BRepGraph_Layer::KindBit(aParentId.NodeKind); + appendParent(aWireId); } } break; } - case BRepGraph_NodeId::Kind::CoEdge: - break; case BRepGraph_NodeId::Kind::Wire: { - const NCollection_DynamicArray* aFaces = - aRevIdx.FacesOfWire(BRepGraph_WireId(aNodeId)); - if (aFaces != nullptr) + for (const BRepGraph_WireRefId& aRefId : + aStorage.WireRelations(BRepGraph_WireId(aNodeId)).ParentWireRefIds) { - for (const BRepGraph_FaceId& aFaceId : *aFaces) + if (aRefId.IsValid(aStorage.NbWireRefs()) && !aStorage.IsRemoved(aRefId)) { - const BRepGraph_NodeId aParentId = aFaceId; - if (!markVisited(aParentId)) - { - continue; - } - BRepGraphInc::BaseDef* aParent = myGraph->changeTopoEntity(aParentId); - if (aParent == nullptr || aParent->IsRemoved) - { - continue; - } - ++aParent->SubtreeGen; - aAllModified.Append(aParentId); - aModifiedKindsMask |= BRepGraph_Layer::KindBit(aParentId.NodeKind); + appendParent(aStorage.WireRef(aRefId).ParentFaceId); } } break; } case BRepGraph_NodeId::Kind::Face: { - const NCollection_DynamicArray* aShells = - aRevIdx.ShellsOfFace(BRepGraph_FaceId(aNodeId)); - if (aShells != nullptr) + for (const BRepGraph_FaceRefId& aRefId : + aStorage.FaceRelations(BRepGraph_FaceId(aNodeId)).ParentFaceRefIds) { - for (const BRepGraph_ShellId& aShellId : *aShells) + if (aRefId.IsValid(aStorage.NbFaceRefs()) && !aStorage.IsRemoved(aRefId)) { - const BRepGraph_NodeId aParentId = aShellId; - if (!markVisited(aParentId)) - { - continue; - } - BRepGraphInc::BaseDef* aParent = myGraph->changeTopoEntity(aParentId); - if (aParent == nullptr || aParent->IsRemoved) - { - continue; - } - ++aParent->SubtreeGen; - aAllModified.Append(aParentId); - aModifiedKindsMask |= BRepGraph_Layer::KindBit(aParentId.NodeKind); + appendParent(aStorage.FaceRef(aRefId).ParentShellId); } } break; } case BRepGraph_NodeId::Kind::Shell: { - const NCollection_DynamicArray* aSolids = - aRevIdx.SolidsOfShell(BRepGraph_ShellId(aNodeId)); - if (aSolids != nullptr) + for (const BRepGraph_ShellRefId& aRefId : + aStorage.ShellRelations(BRepGraph_ShellId(aNodeId)).ParentShellRefIds) { - for (const BRepGraph_SolidId& aSolidId : *aSolids) + if (aRefId.IsValid(aStorage.NbShellRefs()) && !aStorage.IsRemoved(aRefId)) { - const BRepGraph_NodeId aParentId = aSolidId; - if (!markVisited(aParentId)) - { - continue; - } - BRepGraphInc::BaseDef* aParent = myGraph->changeTopoEntity(aParentId); - if (aParent == nullptr || aParent->IsRemoved) - { - continue; - } - ++aParent->SubtreeGen; - aAllModified.Append(aParentId); - aModifiedKindsMask |= BRepGraph_Layer::KindBit(aParentId.NodeKind); + appendParent(aStorage.ShellRef(aRefId).ParentSolidId); } } break; } case BRepGraph_NodeId::Kind::Occurrence: { - // Occurrence modifications propagate to the parent product. - // Find the parent product via OccurrenceRef.ParentId. - for (BRepGraph_OccurrenceRefId aOccRefId = myGraph->Refs().Occurrences().StartId(); - aOccRefId < myGraph->Refs().Occurrences().EndId(); - ++aOccRefId) + const BRepGraph_OccurrenceId anOccurrenceId(aNodeId); + for (BRepGraph_ProductId aProductId = BRepGraph_ProductId::Start(); + aProductId.IsValid(aStorage.NbProducts()); + ++aProductId) { - const BRepGraphInc::OccurrenceRef& aRef = - myGraph->myData->myIncStorage.OccurrenceRef(aOccRefId); - if (!aRef.IsRemoved && aRef.OccurrenceDefId == aNodeId) + if (aStorage.IsRemoved(aProductId)) { - const BRepGraph_NodeId aParentId = aRef.ParentId; - if (markVisited(aParentId)) + continue; + } + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.ProductRelations(aProductId).OccurrenceRefIds) + { + if (aRefId.IsValid(aStorage.NbOccurrenceRefs()) && !aStorage.IsRemoved(aRefId) + && aStorage.OccurrenceRef(aRefId).ChildOccurrenceId == anOccurrenceId) { - BRepGraphInc::BaseDef* aParent = myGraph->changeTopoEntity(aParentId); - if (aParent != nullptr && !aParent->IsRemoved) - { - ++aParent->SubtreeGen; - aAllModified.Append(aParentId); - aModifiedKindsMask |= BRepGraph_Layer::KindBit(aParentId.NodeKind); - } + appendParent(aProductId); + break; } - break; } } break; } - default: + default: { + // Solid/Compound/CompSolid/Product: propagate to parent occurrences + // that reference this node as ChildNodeId - matching immediate-mode + // propagateSubtreeGen logic. + if (BRepGraph_NodeId::IsTopologyKind(aNodeId.NodeKind) + || aNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) + { + for (const BRepGraph_OccurrenceRefId& aRefId : aStorage.OccurrenceRefsOfNode(aNodeId)) + { + if (!aRefId.IsValid(aStorage.NbOccurrenceRefs()) || aStorage.IsRemoved(aRefId)) + { + continue; + } + const BRepGraph_OccurrenceId anOccurrenceId = + aStorage.OccurrenceRef(aRefId).ChildOccurrenceId; + if (!anOccurrenceId.IsValid(aStorage.NbOccurrences()) + || aStorage.IsRemoved(anOccurrenceId)) + { + continue; + } + appendParent(anOccurrenceId); + } + } break; + } } } // Dispatch batch modification event to subscribing layers. - if (myGraph->myLayerRegistry.HasModificationSubscribers() && !aAllModified.IsEmpty()) + if (myGraph->myData->myLayerRegistry.HasModificationSubscribers() && !aAllModified.IsEmpty()) { - myGraph->myLayerRegistry.DispatchNodesModified(aAllModified, aModifiedKindsMask); + myGraph->myData->myLayerRegistry.DispatchNodesModified(aAllModified.ToArray1(), + aModifiedKindsMask); } - // Clear deferred list for next scope. - aDeferredList.Clear(); - // Dispatch deferred reference modification events to subscribing layers. - NCollection_DynamicArray& aDeferredRefList = - myGraph->myData->myDeferredRefModified; - if (!aDeferredRefList.IsEmpty() && myGraph->myLayerRegistry.HasRefModificationSubscribers()) + NCollection_LinearVector& aDeferredRefList = + myGraph->myData->myIncStorage.ChangeDeferredRefModified(); + if (!aDeferredRefList.IsEmpty() + && myGraph->myData->myLayerRegistry.HasRefModificationSubscribers()) { int aRefKindsMask = 0; for (const BRepGraph_RefId& aRef : aDeferredRefList) { aRefKindsMask |= BRepGraph_Layer::RefKindBit(aRef.RefKind); } - myGraph->myLayerRegistry.DispatchRefsModified(aDeferredRefList, aRefKindsMask); + myGraph->myData->myLayerRegistry.DispatchRefsModified(aDeferredRefList.ToArray1(), + aRefKindsMask); } - aDeferredRefList.Clear(); + myGraph->myData->myIncStorage.ClearDeferredQueues(); } //================================================================================================= bool BRepGraph::EditorView::IsDeferredMode() const { - return myGraph->myData->myDeferredMode.load(std::memory_order_relaxed); + return myGraph->myData->myIncStorage.DeferredMode(); +} + +//================================================================================================= + +bool BRepGraph::EditorView::isOwned(const BRepGraph_ItemId theItem) const +{ + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + switch (theItem.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: { + const BRepGraph_NodeId aNode = theItem.NodeId(); + if (!isNodeIndexInRange(aStorage, aNode)) + { + return false; + } + switch (aNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + return aStorage.IsOwned(BRepGraph_VertexId(aNode)); + case BRepGraph_NodeId::Kind::Edge: + return aStorage.IsOwned(BRepGraph_EdgeId(aNode)); + case BRepGraph_NodeId::Kind::CoEdge: + return aStorage.IsOwned(BRepGraph_CoEdgeId(aNode)); + case BRepGraph_NodeId::Kind::Wire: + return aStorage.IsOwned(BRepGraph_WireId(aNode)); + case BRepGraph_NodeId::Kind::Face: + return aStorage.IsOwned(BRepGraph_FaceId(aNode)); + case BRepGraph_NodeId::Kind::Shell: + return aStorage.IsOwned(BRepGraph_ShellId(aNode)); + case BRepGraph_NodeId::Kind::Solid: + return aStorage.IsOwned(BRepGraph_SolidId(aNode)); + case BRepGraph_NodeId::Kind::Compound: + return aStorage.IsOwned(BRepGraph_CompoundId(aNode)); + case BRepGraph_NodeId::Kind::CompSolid: + return aStorage.IsOwned(BRepGraph_CompSolidId(aNode)); + case BRepGraph_NodeId::Kind::Product: + return aStorage.IsOwned(BRepGraph_ProductId(aNode)); + case BRepGraph_NodeId::Kind::Occurrence: + return aStorage.IsOwned(BRepGraph_OccurrenceId(aNode)); + } + return false; + } + case BRepGraph_ItemId::Domain::Reference: { + const BRepGraph_RefId aRef = theItem.RefId(); + if (!isRefInRange(aStorage, aRef)) + { + return false; + } + switch (aRef.RefKind) + { + case BRepGraph_RefId::Kind::Vertex: + return aStorage.IsOwned(BRepGraph_VertexRefId(aRef)); + case BRepGraph_RefId::Kind::Wire: + return aStorage.IsOwned(BRepGraph_WireRefId(aRef)); + case BRepGraph_RefId::Kind::Face: + return aStorage.IsOwned(BRepGraph_FaceRefId(aRef)); + case BRepGraph_RefId::Kind::Shell: + return aStorage.IsOwned(BRepGraph_ShellRefId(aRef)); + case BRepGraph_RefId::Kind::Solid: + return aStorage.IsOwned(BRepGraph_SolidRefId(aRef)); + case BRepGraph_RefId::Kind::Child: + return aStorage.IsOwned(BRepGraph_ChildRefId(aRef)); + case BRepGraph_RefId::Kind::Occurrence: + return aStorage.IsOwned(BRepGraph_OccurrenceRefId(aRef)); + } + return false; + } + case BRepGraph_ItemId::Domain::None: + return false; + } + return false; +} + +//================================================================================================= + +void BRepGraph::EditorView::requireUnlocked(const BRepGraph_ItemId theItem, + const char* theOperation) const +{ + if (isOwned(theItem)) + { + throw Standard_ProgramError(theOperation); + } +} + +//================================================================================================= + +void BRepGraph::EditorView::requireNoActiveGuard(const BRepGraph_ItemId theItem, + const char* theOperation) const +{ + if (myGraph->myData->myIncStorage.IsGuarded(theItem)) + { + throw Standard_ProgramError(theOperation); + } } //================================================================================================= void BRepGraph::EditorView::GenOps::applyModificationImpl( const BRepGraph_NodeId theTarget, - NCollection_DynamicArray&& theReplacements, + NCollection_LinearVector&& theReplacements, const TCollection_AsciiString& theOpLabel) { - myGraph->myData->myHistoryLog.Record(theOpLabel, theTarget, theReplacements); + myGraph->LayerRegistry().Ensure()->Record(theOpLabel, + theTarget, + theReplacements.ToArray1()); myGraph->invalidateSubgraphImpl(theTarget); } @@ -2051,49 +3506,99 @@ void BRepGraph::EditorView::EdgeOps::Split(const BRepGraph_EdgeId theEdgeEntit { theSubA = BRepGraph_EdgeId(); theSubB = BRepGraph_EdgeId(); + myGraph->Editor().requireUnlocked(theEdgeEntity, "BRepGraph::EditorView::Split(): owned edge"); + myGraph->Editor().requireNoActiveGuard(BRepGraph_ItemId(theEdgeEntity), + "BRepGraph::EditorView::Split(): guard active on edge"); Standard_ASSERT_RETURN(theEdgeEntity.IsValid(myGraph->myData->myIncStorage.NbEdges()), "Split: edge index is out of range", Standard_VOID_RETURN); Standard_ASSERT_RETURN(theSplitVertex.IsValid(myGraph->myData->myIncStorage.NbVertices()), "Split: split-vertex index is out of range", Standard_VOID_RETURN); + Standard_ASSERT_RETURN(!myGraph->myData->myIncStorage.IsRemoved(theSplitVertex), + "Split: split-vertex is removed", + Standard_VOID_RETURN); // Copy all data from the original EdgeDef before appending to vectors (which may reallocate). const BRepGraphInc::EdgeDef& anOrig = myGraph->myData->myIncStorage.Edge(theEdgeEntity); - Standard_ASSERT_RETURN(!anOrig.IsRemoved, "Split: source edge is removed", Standard_VOID_RETURN); - Standard_ASSERT_RETURN(!anOrig.IsDegenerate, - "Split: degenerate edge cannot be split", + Standard_ASSERT_RETURN(!myGraph->myData->myIncStorage.IsRemoved(theEdgeEntity), + "Split: source edge is removed", Standard_VOID_RETURN); - Standard_ASSERT_RETURN(anOrig.ParamFirst < theSplitParam && theSplitParam < anOrig.ParamLast, + + // Read param range from use record if available. + const BRepGraphInc_Storage& aConstStorage2 = myGraph->myData->myIncStorage; + double aOrigParamFirst = 0.0, aOrigParamLast = 0.0; + if (anOrig.Curve3DRepId.IsValid(aConstStorage2.NbEdgeCurves3D()) + && !aConstStorage2.IsRemoved(anOrig.Curve3DRepId)) + { + const BRepGraphInc::EdgeCurve3DRep& aUse = aConstStorage2.EdgeCurve3DRep(anOrig.Curve3DRepId); + aOrigParamFirst = aUse.ParamFirst; + aOrigParamLast = aUse.ParamLast; + } + Standard_ASSERT_RETURN(aOrigParamFirst < theSplitParam && theSplitParam < aOrigParamLast, "Split: split parameter must be inside open edge range", Standard_VOID_RETURN); - const BRepGraphInc_Storage& aConstStorage = myGraph->myData->myIncStorage; - const BRepGraph_Curve3DRepId aOrigCurve3DRepId = anOrig.Curve3DRepId; - const double aOrigTolerance = anOrig.Tolerance; - const bool aOrigSameParameter = anOrig.SameParameter; - const double aOrigParamFirst = anOrig.ParamFirst; - const double aOrigParamLast = anOrig.ParamLast; - const BRepGraph_VertexRefId aOrigStartVertexRefId = anOrig.StartVertexRefId; - const BRepGraph_VertexRefId aOrigEndVertexRefId = anOrig.EndVertexRefId; - const bool aOrigSameRange = anOrig.SameRange; + const BRepGraphInc_Storage& aConstStorage = aConstStorage2; + const BRepGraph_EdgeCurve3DRepId aOrigCurve3DRepId = anOrig.Curve3DRepId; + const BRepGraph_EdgePolygon3DRepId aOrigPolygon3DRepId = anOrig.Polygon3DRepId; + const double aOrigTolerance = anOrig.Tolerance; + const BRepGraph_VertexRefId aOrigStartVertexRefId = anOrig.StartVertexRefId; + const BRepGraph_VertexRefId aOrigEndVertexRefId = anOrig.EndVertexRefId; // Resolve original vertex def ids through storage ref entries. - const BRepGraph_VertexId aOrigStartVertexDefId = - aOrigStartVertexRefId.IsValid() ? aConstStorage.VertexRef(aOrigStartVertexRefId).VertexDefId + const BRepGraph_VertexId aOrigStartChildVertexId = + aOrigStartVertexRefId.IsValid() ? aConstStorage.VertexRef(aOrigStartVertexRefId).ChildVertexId : BRepGraph_VertexId(); - const BRepGraph_VertexId aOrigEndVertexDefId = - aOrigEndVertexRefId.IsValid() ? aConstStorage.VertexRef(aOrigEndVertexRefId).VertexDefId + const BRepGraph_VertexId aOrigEndChildVertexId = + aOrigEndVertexRefId.IsValid() ? aConstStorage.VertexRef(aOrigEndVertexRefId).ChildVertexId : BRepGraph_VertexId(); - // Copy wire indices: ReverseIdx may be rebuilt below. - const NCollection_DynamicArray* aOrigWiresPtr = - myGraph->myData->myIncStorage.ReverseIndex().WiresOfEdge(theEdgeEntity); - const NCollection_DynamicArray aOrigWires = - aOrigWiresPtr != nullptr ? *aOrigWiresPtr : NCollection_DynamicArray(); + // Copy wire indices: relation table may be rebuilt below. + NCollection_LinearVector aOrigWires; + for (BRepGraph_WiresOfEdge aWireIt = myGraph->Topo().Edges().WiresOf(theEdgeEntity); + aWireIt.More(); + aWireIt.Next()) + { + aOrigWires.Append(aWireIt.CurrentId()); + } BRepGraphInc_Storage& aMutStorage = myGraph->myData->myIncStorage; + const auto aCopyEdgeCurveUse = [&](const BRepGraph_EdgeId theParentEdge, + const double theFirst, + const double theLast) -> BRepGraph_EdgeCurve3DRepId { + if (!aOrigCurve3DRepId.IsValid(aMutStorage.NbEdgeCurves3D()) + || aMutStorage.IsRemoved(aOrigCurve3DRepId)) + { + return BRepGraph_EdgeCurve3DRepId(); + } + const BRepGraphInc::EdgeCurve3DRep& anOrigUse = aMutStorage.EdgeCurve3DRep(aOrigCurve3DRepId); + const BRepGraph_EdgeCurve3DRepId aRepId = aMutStorage.AppendEdgeCurve3DRep(); + BRepGraphInc::EdgeCurve3DRep& aUse = aMutStorage.ChangeEdgeCurve3DRep(aRepId); + aUse.ParentEdgeId = theParentEdge; + aUse.Curve = anOrigUse.Curve; + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + return aRepId; + }; + + const auto aCopyEdgePolygon3DRep = + [&](const BRepGraph_EdgeId theParentEdge) -> BRepGraph_EdgePolygon3DRepId { + if (!aOrigPolygon3DRepId.IsValid(aMutStorage.NbEdgePolygons3D()) + || aMutStorage.IsRemoved(aOrigPolygon3DRepId)) + { + return BRepGraph_EdgePolygon3DRepId(); + } + const BRepGraphInc::EdgePolygon3DRep& anOrigUse = + aMutStorage.EdgePolygon3DRep(aOrigPolygon3DRepId); + const BRepGraph_EdgePolygon3DRepId aRepId = aMutStorage.AppendEdgePolygon3DRep(); + BRepGraphInc::EdgePolygon3DRep& aUse = aMutStorage.ChangeEdgePolygon3DRep(aRepId); + aUse.ParentEdgeId = theParentEdge; + aUse.Polygon = anOrigUse.Polygon; + return aRepId; + }; + // Allocate SubA slot. const BRepGraph_EdgeId aSubAId(aMutStorage.NbEdges()); aMutStorage.AppendEdge(); @@ -2105,7 +3610,7 @@ void BRepGraph::EditorView::EdgeOps::Split(const BRepGraph_EdgeId theEdgeEntit theSubB = aSubBId; // Build vertex ref entries for the split vertex (no Location since split vertex is new). - const BRepGraph_VertexId aSplitVertexDefId = theSplitVertex; + const BRepGraph_VertexId aSplitChildVertexId = theSplitVertex; // Create start vertex ref entry for SubA (copy from original edge's start vertex ref). BRepGraph_VertexRefId aSubAStartRefId; @@ -2114,30 +3619,28 @@ void BRepGraph::EditorView::EdgeOps::Split(const BRepGraph_EdgeId theEdgeEntit // Copy fields before append (which may reallocate and invalidate references). const BRepGraphInc::VertexRef& aOrigStartRef = myGraph->myData->myIncStorage.VertexRef(aOrigStartVertexRefId); - const BRepGraph_VertexId aOrigStartVertexId = aOrigStartRef.VertexDefId; + const BRepGraph_VertexId aOrigStartVertexId = aOrigStartRef.ChildVertexId; const TopAbs_Orientation aOrigStartOri = aOrigStartRef.Orientation; - const TopLoc_Location aOrigStartLoc = aOrigStartRef.LocalLocation; const BRepGraph_VertexRefId aSubAStartRefId2(aMutStorage.NbVertexRefs()); aMutStorage.AppendVertexRef(); BRepGraphInc::VertexRef& aSubAStartRef = aMutStorage.ChangeVertexRef(aSubAStartRefId2); - aSubAStartRef.ParentId = BRepGraph_NodeId(aSubAId); - aSubAStartRef.VertexDefId = aOrigStartVertexId; + aSubAStartRef.ChildVertexId = aOrigStartVertexId; + aSubAStartRef.ParentEdgeId = aSubAId; aSubAStartRef.Orientation = aOrigStartOri; - aSubAStartRef.LocalLocation = aOrigStartLoc; myGraph->allocateRefUID(aSubAStartRefId2); aSubAStartRefId = aSubAStartRefId2; } // Create end vertex ref entry for SubA (split vertex, REVERSED). BRepGraph_VertexRefId aSubAEndRefId; - if (aSplitVertexDefId.IsValid()) + if (aSplitChildVertexId.IsValid()) { const BRepGraph_VertexRefId aSubAEndRefId2(aMutStorage.NbVertexRefs()); aMutStorage.AppendVertexRef(); BRepGraphInc::VertexRef& aSubAEndRef = aMutStorage.ChangeVertexRef(aSubAEndRefId2); - aSubAEndRef.ParentId = BRepGraph_NodeId(aSubAId); - aSubAEndRef.VertexDefId = aSplitVertexDefId; + aSubAEndRef.ChildVertexId = aSplitChildVertexId; + aSubAEndRef.ParentEdgeId = aSubAId; aSubAEndRef.Orientation = TopAbs_REVERSED; myGraph->allocateRefUID(aSubAEndRefId2); aSubAEndRefId = aSubAEndRefId2; @@ -2145,13 +3648,13 @@ void BRepGraph::EditorView::EdgeOps::Split(const BRepGraph_EdgeId theEdgeEntit // Create start vertex ref entry for SubB (split vertex, FORWARD). BRepGraph_VertexRefId aSubBStartRefId; - if (aSplitVertexDefId.IsValid()) + if (aSplitChildVertexId.IsValid()) { const BRepGraph_VertexRefId aSubBStartRefId2(aMutStorage.NbVertexRefs()); aMutStorage.AppendVertexRef(); BRepGraphInc::VertexRef& aSubBStartRef = aMutStorage.ChangeVertexRef(aSubBStartRefId2); - aSubBStartRef.ParentId = BRepGraph_NodeId(aSubBId); - aSubBStartRef.VertexDefId = aSplitVertexDefId; + aSubBStartRef.ChildVertexId = aSplitChildVertexId; + aSubBStartRef.ParentEdgeId = aSubBId; aSubBStartRef.Orientation = TopAbs_FORWARD; myGraph->allocateRefUID(aSubBStartRefId2); aSubBStartRefId = aSubBStartRefId2; @@ -2164,115 +3667,146 @@ void BRepGraph::EditorView::EdgeOps::Split(const BRepGraph_EdgeId theEdgeEntit // Copy fields before append (which may reallocate and invalidate references). const BRepGraphInc::VertexRef& aOrigEndRef = myGraph->myData->myIncStorage.VertexRef(aOrigEndVertexRefId); - const BRepGraph_VertexId aOrigEndVertexId = aOrigEndRef.VertexDefId; + const BRepGraph_VertexId aOrigEndVertexId = aOrigEndRef.ChildVertexId; const TopAbs_Orientation aOrigEndOri = aOrigEndRef.Orientation; - const TopLoc_Location aOrigEndLoc = aOrigEndRef.LocalLocation; const BRepGraph_VertexRefId aSubBEndRefId2(aMutStorage.NbVertexRefs()); aMutStorage.AppendVertexRef(); BRepGraphInc::VertexRef& aSubBEndRef = aMutStorage.ChangeVertexRef(aSubBEndRefId2); - aSubBEndRef.ParentId = BRepGraph_NodeId(aSubBId); - aSubBEndRef.VertexDefId = aOrigEndVertexId; + aSubBEndRef.ChildVertexId = aOrigEndVertexId; + aSubBEndRef.ParentEdgeId = aSubBId; aSubBEndRef.Orientation = aOrigEndOri; - aSubBEndRef.LocalLocation = aOrigEndLoc; myGraph->allocateRefUID(aSubBEndRefId2); aSubBEndRefId = aSubBEndRefId2; } // Set SubA: StartVertex -> SplitVertex, [ParamFirst, theSplitParam]. + const BRepGraph_EdgeCurve3DRepId aSubACurve3DRepId = + aCopyEdgeCurveUse(aSubAId, aOrigParamFirst, theSplitParam); + const BRepGraph_EdgePolygon3DRepId aSubAPolygon3DRepId = aCopyEdgePolygon3DRep(aSubAId); { BRepGraphInc::EdgeDef& aSubA = myGraph->myData->myIncStorage.ChangeEdge(aSubAId); - initSubEdgeEntity(aSubA, - aOrigCurve3DRepId, - aOrigTolerance, - aOrigSameParameter, - aSubAStartRefId, - aSubAEndRefId, - aOrigParamFirst, - theSplitParam); + initSubEdgeEntity(aSubA, aSubACurve3DRepId, aOrigTolerance, aSubAStartRefId, aSubAEndRefId); + aSubA.Polygon3DRepId = aSubAPolygon3DRepId; } + const auto anAttachEdgeVertices = [&](const BRepGraph_EdgeId theEdgeId, + const BRepGraph_VertexRefId theStartRefId, + const BRepGraph_VertexRefId theEndRefId) { + if (theStartRefId.IsValid(aMutStorage.NbVertexRefs())) + { + aMutStorage.AttachEdgeToVertex(theEdgeId, aMutStorage.VertexRef(theStartRefId).ChildVertexId); + } + if (theEndRefId.IsValid(aMutStorage.NbVertexRefs())) + { + aMutStorage.AttachEdgeToVertex(theEdgeId, aMutStorage.VertexRef(theEndRefId).ChildVertexId); + } + }; + anAttachEdgeVertices(aSubAId, aSubAStartRefId, aSubAEndRefId); // Set SubB: SplitVertex -> EndVertex, [theSplitParam, ParamLast]. + const BRepGraph_EdgeCurve3DRepId aSubBCurve3DRepId = + aCopyEdgeCurveUse(aSubBId, theSplitParam, aOrigParamLast); + const BRepGraph_EdgePolygon3DRepId aSubBPolygon3DRepId = aCopyEdgePolygon3DRep(aSubBId); { BRepGraphInc::EdgeDef& aSubB = myGraph->myData->myIncStorage.ChangeEdge(aSubBId); - initSubEdgeEntity(aSubB, - aOrigCurve3DRepId, - aOrigTolerance, - aOrigSameParameter, - aSubBStartRefId, - aSubBEndRefId, - theSplitParam, - aOrigParamLast); + initSubEdgeEntity(aSubB, aSubBCurve3DRepId, aOrigTolerance, aSubBStartRefId, aSubBEndRefId); + aSubB.Polygon3DRepId = aSubBPolygon3DRepId; } + anAttachEdgeVertices(aSubBId, aSubBStartRefId, aSubBEndRefId); myGraph->allocateUID(theSubA); myGraph->allocateUID(theSubB); // Rebuild CoEdge incidence in a single pass: snapshot all CoEdges of the // original edge, allocate two fully-initialised CoEdges per original, rebuild - // wire CoEdgeRefIds, retire each original via reverse-index unbind. The seam + // wire CoEdgeIds, retire each original via relation unbind. The seam // relation re-emerges automatically from the resulting (Edge, Face, Orientation) // tuples (see BRepGraph_Tool::CoEdge::SeamPair). - NCollection_DynamicArray aOrigFaces; { - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc_ReverseIndex& aRevIdx = aStorage.ChangeReverseIndex(); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; // Step 1: snapshot old CoEdge ids before any mutation. - NCollection_DynamicArray anOrigCoEdgeIds; - if (const NCollection_DynamicArray* aSrc = - aStorage.ReverseIndex().CoEdgesOfEdge(theEdgeEntity)) + NCollection_LinearVector anOrigCoEdgeIds; + if (theEdgeEntity.IsValid(aStorage.NbEdges())) { - for (const BRepGraph_CoEdgeId& anId : *aSrc) + for (const BRepGraph_CoEdgeId& anId : aStorage.EdgeRelations(theEdgeEntity).CoEdgeIds) { anOrigCoEdgeIds.Append(anId); } } const uint32_t aNbOrig = static_cast(anOrigCoEdgeIds.Size()); - // Step 2: allocate SubA-CE + SubB-CE per original with full payload. - NCollection_DataMap aIdxOf; - NCollection_DynamicArray aNewACoEdgeIds; - NCollection_DynamicArray aNewBCoEdgeIds; - const double aParamRange = aOrigParamLast - aOrigParamFirst; + // Step 2: allocate SubA-CE + SubB-CE per original with full representation. + NCollection_FlatDataMap aIdxOf; + NCollection_LinearVector aNewACoEdgeIds(aNbOrig, BRepGraph_CoEdgeId()); + NCollection_LinearVector aNewBCoEdgeIds(aNbOrig, BRepGraph_CoEdgeId()); + const double aSplitRatio = + (theSplitParam - aOrigParamFirst) / (aOrigParamLast - aOrigParamFirst); + const auto aCopyCoEdgeCurveUse = + [&](const BRepGraph_CoEdgeCurve2DRepId theOrigRepId, + const BRepGraph_CoEdgeId theParentCoEdge, + const bool theIsFirstPart) -> BRepGraph_CoEdgeCurve2DRepId { + if (!theOrigRepId.IsValid(aStorage.NbCoEdgeCurves2D()) || aStorage.IsRemoved(theOrigRepId)) + { + return BRepGraph_CoEdgeCurve2DRepId(); + } + const BRepGraphInc::CoEdgeCurve2DRep& anOrigUse = aStorage.CoEdgeCurve2DRep(theOrigRepId); + const double aParamSplit = + anOrigUse.ParamFirst + (anOrigUse.ParamLast - anOrigUse.ParamFirst) * aSplitRatio; + const BRepGraph_CoEdgeCurve2DRepId aRepId = aStorage.AppendCoEdgeCurve2DRep(); + BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.ChangeCoEdgeCurve2DRep(aRepId); + aUse.ParentCoEdgeId = theParentCoEdge; + aUse.Curve = anOrigUse.Curve; + aUse.ParamFirst = theIsFirstPart ? anOrigUse.ParamFirst : aParamSplit; + aUse.ParamLast = theIsFirstPart ? aParamSplit : anOrigUse.ParamLast; + return aRepId; + }; + const auto aCopyCoEdgePolygon2DRep = + [&](const BRepGraph_CoEdgePolygon2DRepId theOrigRepId, + const BRepGraph_CoEdgeId theParentCoEdge) -> BRepGraph_CoEdgePolygon2DRepId { + if (!theOrigRepId.IsValid(aStorage.NbCoEdgePolygons2D()) || aStorage.IsRemoved(theOrigRepId)) + { + return BRepGraph_CoEdgePolygon2DRepId(); + } + const BRepGraphInc::CoEdgePolygon2DRep& anOrigUse = aStorage.CoEdgePolygon2DRep(theOrigRepId); + const BRepGraph_CoEdgePolygon2DRepId aRepId = aStorage.AppendCoEdgePolygon2DRep(); + BRepGraphInc::CoEdgePolygon2DRep& aUse = aStorage.ChangeCoEdgePolygon2DRep(aRepId); + aUse.ParentCoEdgeId = theParentCoEdge; + aUse.Polygon = anOrigUse.Polygon; + return aRepId; + }; + const auto aCopyCoEdgePolygonOnTriRep = + [&](const BRepGraph_CoEdgePolygonOnTriRepId theOrigRepId, + const BRepGraph_CoEdgeId theParentCoEdge) -> BRepGraph_CoEdgePolygonOnTriRepId { + if (!theOrigRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) + || aStorage.IsRemoved(theOrigRepId)) + { + return BRepGraph_CoEdgePolygonOnTriRepId(); + } + const BRepGraphInc::CoEdgePolygonOnTriRep& anOrigUse = + aStorage.CoEdgePolygonOnTriRep(theOrigRepId); + const BRepGraph_CoEdgePolygonOnTriRepId aRepId = aStorage.AppendCoEdgePolygonOnTriRep(); + BRepGraphInc::CoEdgePolygonOnTriRep& aUse = aStorage.ChangeCoEdgePolygonOnTriRep(aRepId); + aUse.ParentCoEdgeId = theParentCoEdge; + aUse.Polygon = anOrigUse.Polygon; + return aRepId; + }; + for (uint32_t i = 0; i < aNbOrig; ++i) { const BRepGraph_CoEdgeId aOldId = anOrigCoEdgeIds.Value(static_cast(i)); const BRepGraphInc::CoEdgeDef aOld = aStorage.CoEdge(aOldId); // by-value snapshot - double aPCSplit; - if (aOrigSameRange) - { - aPCSplit = theSplitParam; - } - else - { - const double aPCRange = aOld.ParamLast - aOld.ParamFirst; - if (aParamRange > 0.0) - { - aPCSplit = aOld.ParamFirst + ((theSplitParam - aOrigParamFirst) / aParamRange) * aPCRange; - } - else - { - aPCSplit = 0.5 * (aOld.ParamFirst + aOld.ParamLast); - } - } - const BRepGraph_CoEdgeId aNewA = aStorage.AppendCoEdge(); { BRepGraphInc::CoEdgeDef& aNewACE = aStorage.ChangeCoEdge(aNewA); initSubCoEdgeEntity(aNewACE, theSubA, - aOld.FaceDefId, + aOld.FaceId, aOld.Orientation, - aOld.Curve2DRepId, - aOld.ParamFirst, - aPCSplit); - // UV at the split-point end is left default (0,0); callers needing - // the exact point should evaluate the Curve2DRep at aPCSplit. - aNewACE.UV1 = aOld.UV1; - aNewACE.Polygon2DRepId = aOld.Polygon2DRepId; - aNewACE.PolygonOnTriRepId = aOld.PolygonOnTriRepId; + aCopyCoEdgeCurveUse(aOld.Curve2DRepId, aNewA, true)); + aNewACE.Polygon2DRepId = aCopyCoEdgePolygon2DRep(aOld.Polygon2DRepId, aNewA); + aNewACE.PolygonOnTriRepId = aCopyCoEdgePolygonOnTriRep(aOld.PolygonOnTriRepId, aNewA); } myGraph->allocateUID(aNewA); @@ -2281,119 +3815,72 @@ void BRepGraph::EditorView::EdgeOps::Split(const BRepGraph_EdgeId theEdgeEntit BRepGraphInc::CoEdgeDef& aNewBCE = aStorage.ChangeCoEdge(aNewB); initSubCoEdgeEntity(aNewBCE, theSubB, - aOld.FaceDefId, + aOld.FaceId, aOld.Orientation, - aOld.Curve2DRepId, - aPCSplit, - aOld.ParamLast); - // UV at the split-point start is left default (0,0) - see aNewACE. - aNewBCE.UV2 = aOld.UV2; - aNewBCE.Polygon2DRepId = aOld.Polygon2DRepId; - aNewBCE.PolygonOnTriRepId = aOld.PolygonOnTriRepId; + aCopyCoEdgeCurveUse(aOld.Curve2DRepId, aNewB, false)); + aNewBCE.Polygon2DRepId = aCopyCoEdgePolygon2DRep(aOld.Polygon2DRepId, aNewB); + aNewBCE.PolygonOnTriRepId = aCopyCoEdgePolygonOnTriRep(aOld.PolygonOnTriRepId, aNewB); } myGraph->allocateUID(aNewB); - aNewACoEdgeIds.Append(aNewA); - aNewBCoEdgeIds.Append(aNewB); + aNewACoEdgeIds.ChangeValue(static_cast(i)) = aNewA; + aNewBCoEdgeIds.ChangeValue(static_cast(i)) = aNewB; aIdxOf.Bind(aOldId, i); - if (aOld.FaceDefId.IsValid()) - { - aOrigFaces.Append(aOld.FaceDefId); - } } - // Step 3: rebuild wire CoEdgeRefIds (snapshot-before-mutate). For each - // wire containing the original edge, rebind its existing CoEdgeRef to - // the new SubA-CE in place and insert a fresh CoEdgeRef for SubB-CE - // immediately after. Track insertion offset so that subsequent inserts - // land at the correct position in the growing list. + // Step 3: rebuild wire CoEdgeIds (snapshot-before-mutate). For each + // wire containing the original edge, replace the existing coedge entry + // with the new SubA-CE in place and insert a fresh SubB-CE immediately + // after. Track insertion offset so that subsequent inserts land at the + // correct position in the growing list. for (const BRepGraph_WireId& aWireId : aOrigWires) { - NCollection_DynamicArray aSnapshot; + NCollection_LinearVector aSnapshot; { - const BRepGraphInc::WireDef& aWireDef = aStorage.Wire(aWireId); - for (NCollection_DynamicArray::Iterator aRefIt( - aWireDef.CoEdgeRefIds); - aRefIt.More(); - aRefIt.Next()) + const BRepGraphInc::WireRelations& aWireRel = aStorage.WireRelations(aWireId); + for (const BRepGraph_CoEdgeId& aCoEdgeId : aWireRel.CoEdgeIds) { - aSnapshot.Append(aRefIt.Value()); + aSnapshot.Append(aCoEdgeId); } } - size_t anInsertOffset = 0; for (size_t i = 0; i < aSnapshot.Size(); ++i) { - const BRepGraph_CoEdgeRefId aRefId = aSnapshot.Value(i); - const BRepGraphInc::CoEdgeRef& aRefEnt = aStorage.CoEdgeRef(aRefId); - // Skip refs the caller has already retired: their slot stays in the - // wire's list but is filtered out of any live walk. Split must not - // revive them. - if (aRefEnt.IsRemoved) + const BRepGraph_CoEdgeId aOldCEId = aSnapshot.Value(i); + if (aStorage.IsRemoved(aOldCEId)) { continue; } - const BRepGraph_CoEdgeId aOldCEId = aRefEnt.CoEdgeDefId; - int aJ = -1; - if (!aIdxOf.Find(aOldCEId, aJ)) + const uint32_t* aJPtr = aIdxOf.Seek(aOldCEId); + if (aJPtr == nullptr) { continue; } - const BRepGraph_CoEdgeId aNewA = aNewACoEdgeIds.Value(aJ); - const BRepGraph_CoEdgeId aNewB = aNewBCoEdgeIds.Value(aJ); - const TopLoc_Location aLoc = aStorage.CoEdgeRef(aRefId).LocalLocation; - - // Rebind existing ref in-place to SubA-CE (preserves order slot). - aStorage.ChangeCoEdgeRef(aRefId).CoEdgeDefId = aNewA; - - // Allocate fresh ref for SubB-CE and insert into wire after aRefId. - const BRepGraph_CoEdgeRefId aNewRefId = aStorage.AppendCoEdgeRef(); - { - BRepGraphInc::CoEdgeRef& aNewRef = aStorage.ChangeCoEdgeRef(aNewRefId); - aNewRef.ParentId = aWireId; - aNewRef.CoEdgeDefId = aNewB; - aNewRef.LocalLocation = aLoc; - } - myGraph->allocateRefUID(aNewRefId); - - const size_t aPos = i + anInsertOffset; - BRepGraphInc::WireDef& aWireEnt = aStorage.ChangeWire(aWireId); - auto& aCoEdgeRefIds = aWireEnt.CoEdgeRefIds; - if (aPos < aCoEdgeRefIds.Size()) - { - aCoEdgeRefIds.InsertAfter(aPos, aNewRefId); - } - else - { - aCoEdgeRefIds.Append(aNewRefId); - } - ++anInsertOffset; - - aRevIdx.UnbindCoEdgeFromWire(aOldCEId, aWireId); - aRevIdx.BindCoEdgeToWire(aNewA, aWireId); - aRevIdx.BindCoEdgeToWire(aNewB, aWireId); + const uint32_t aJ = *aJPtr; + const BRepGraph_CoEdgeId aNewA = aNewACoEdgeIds.Value(static_cast(aJ)); + const BRepGraph_CoEdgeId aNewB = aNewBCoEdgeIds.Value(static_cast(aJ)); + aStorage.ReplaceCoEdgeUseWithPair(aWireId, aOldCEId, aNewA, aNewB); } + aStorage.CanonicalizeWireCoEdgeOrder(aWireId); } - // Step 4: retire the original CoEdges and rebuild the Edge->CoEdge - // reverse index so CoEdgesOfEdge(theSubA/B) resolves to the new pair. + // Step 4: retire the original CoEdges and rebuild relations so + // EdgeRelations(theSubA/B).CoEdgeIds resolves to the new pair. for (uint32_t i = 0; i < aNbOrig; ++i) { - const BRepGraph_CoEdgeId aOldId = anOrigCoEdgeIds.Value(static_cast(i)); - const BRepGraph_CoEdgeId aNewA = aNewACoEdgeIds.Value(static_cast(i)); - const BRepGraph_CoEdgeId aNewB = aNewBCoEdgeIds.Value(static_cast(i)); + const BRepGraph_CoEdgeId aOldId = anOrigCoEdgeIds.Value(static_cast(i)); + const BRepGraphInc::CoEdgeDef& aOld = aStorage.CoEdge(aOldId); + aStorage.MarkRemoved(aOld.Curve2DRepId); + aStorage.MarkRemoved(aOld.Polygon2DRepId); + aStorage.MarkRemoved(aOld.PolygonOnTriRepId); aStorage.MarkRemoved(BRepGraph_NodeId(aOldId)); - aRevIdx.UnbindEdgeFromCoEdge(theEdgeEntity, aOldId); - aRevIdx.BindEdgeToCoEdge(theSubA, aNewA); - aRevIdx.BindEdgeToCoEdge(theSubB, aNewB); } - // Step 5: retire the original edge's vertex refs. Their ParentId - // points at the about-to-be-removed edge; leaving them live would - // trip the "Orphan VertexRef: ParentId is not a live Edge" Audit - // rule. Internal vertex refs are dropped rather than reparented to - // SubA/SubB based on parameter - reparenting is a follow-up when a - // caller surfaces that needs it. + // Step 5: retire the original edge's boundary vertex refs before the + // original edge is removed; otherwise active refs would have no live edge + // slot owner. Internal vertex refs are dropped rather than reattached to + // SubA/SubB based on parameter - reattachment is a follow-up if a caller + // surfaces that need. if (aOrigStartVertexRefId.IsValid()) { aStorage.MarkRemovedRef(aOrigStartVertexRefId); @@ -2402,129 +3889,43 @@ void BRepGraph::EditorView::EdgeOps::Split(const BRepGraph_EdgeId theEdgeEntit { aStorage.MarkRemovedRef(aOrigEndVertexRefId); } - // Defensive retirement of internal vertex refs. No existing test - // populates InternalVertexRefIds before a Split, so the loop is a - // no-op under the current suite; it guards against orphan refs the - // moment a caller does populate them. - { - const BRepGraphInc::EdgeDef& aOrigEdgeRef = aStorage.Edge(theEdgeEntity); - for (NCollection_DynamicArray::Iterator anIntRefIt( - aOrigEdgeRef.InternalVertexRefIds); - anIntRefIt.More(); - anIntRefIt.Next()) - { - const BRepGraph_VertexRefId anIntRef = anIntRefIt.Value(); - if (anIntRef.IsValid()) - { - aStorage.MarkRemovedRef(anIntRef); - } - } - } - - const occ::handle aRegularityLayer = - myGraph->LayerRegistry().FindLayer(); - if (!aRegularityLayer.IsNull()) - { - aRegularityLayer->CopyRegularities(theEdgeEntity, theSubA); - aRegularityLayer->CopyRegularities(theEdgeEntity, theSubB); - aRegularityLayer->RemoveRegularities(theEdgeEntity); - } // Mark original edge as removed. + aStorage.MarkRemoved(aOrigCurve3DRepId); + aStorage.MarkRemoved(aOrigPolygon3DRepId); aStorage.MarkRemoved(theEdgeEntity); } - // Update edge-to-wire reverse index incrementally. - BRepGraphInc_ReverseIndex& aRevIdx = myGraph->myData->myIncStorage.ChangeReverseIndex(); for (const BRepGraph_WireId& aWireId : aOrigWires) { - aRevIdx.UnbindEdgeFromWire(BRepGraph_EdgeId(theEdgeEntity), aWireId); - aRevIdx.BindEdgeToWire(aSubAId, aWireId); - aRevIdx.BindEdgeToWire(aSubBId, aWireId); myGraph->markModified(aWireId); } - // Incremental vertex-to-edge updates: register sub-edge vertices. - { - const BRepGraphInc_Storage& aStorageRef = myGraph->myData->myIncStorage; - const BRepGraphInc::EdgeDef& aSubAEnt = aStorageRef.Edge(aSubAId); - const BRepGraphInc::EdgeDef& aSubBEnt = aStorageRef.Edge(aSubBId); - BRepGraphInc_ReverseIndex& aRevIdxMut = myGraph->myData->myIncStorage.ChangeReverseIndex(); - - // Resolve vertex def ids from the sub-edge ref entries. - if (aSubAEnt.StartVertexRefId.IsValid()) - { - const BRepGraph_VertexId aVtxId = - aStorageRef.VertexRef(aSubAEnt.StartVertexRefId).VertexDefId; - if (aVtxId.IsValid()) - { - aRevIdxMut.BindVertexToEdge(aVtxId, aSubAId); - } - } - if (aSubAEnt.EndVertexRefId.IsValid()) - { - const BRepGraph_VertexId aVtxId = aStorageRef.VertexRef(aSubAEnt.EndVertexRefId).VertexDefId; - if (aVtxId.IsValid()) - { - aRevIdxMut.BindVertexToEdge(aVtxId, aSubAId); - } - } - if (aSubBEnt.StartVertexRefId.IsValid()) - { - const BRepGraph_VertexId aVtxId = - aStorageRef.VertexRef(aSubBEnt.StartVertexRefId).VertexDefId; - if (aVtxId.IsValid()) - { - aRevIdxMut.BindVertexToEdge(aVtxId, aSubBId); - } - } - if (aSubBEnt.EndVertexRefId.IsValid()) - { - const BRepGraph_VertexId aVtxId = aStorageRef.VertexRef(aSubBEnt.EndVertexRefId).VertexDefId; - if (aVtxId.IsValid()) - { - aRevIdxMut.BindVertexToEdge(aVtxId, aSubBId); - } - } - - // Remove old edge from vertex-to-edge index. - if (aOrigStartVertexDefId.IsValid()) - { - aRevIdxMut.UnbindVertexFromEdge(aOrigStartVertexDefId, BRepGraph_EdgeId(theEdgeEntity)); - } - if (aOrigEndVertexDefId.IsValid()) - { - aRevIdxMut.UnbindVertexFromEdge(aOrigEndVertexDefId, BRepGraph_EdgeId(theEdgeEntity)); - } - - // Edge-to-face: unbind the original edge and bind both sub-edges for - // each face the original edge touched. aOrigFaces was captured during - // the CoEdge rebuild above (may contain duplicates for seam edges - the - // Bind/Unbind helpers are dedup-safe). - for (const BRepGraph_FaceId& aFaceId : aOrigFaces) - { - aRevIdxMut.UnbindEdgeFromFace(BRepGraph_EdgeId(theEdgeEntity), aFaceId); - aRevIdxMut.BindEdgeToFace(aSubAId, aFaceId); - aRevIdxMut.BindEdgeToFace(aSubBId, aFaceId); - } - } + myGraph->myData->myIncStorage.RebindVertexEdge(aOrigStartChildVertexId, + BRepGraph_VertexId(), + theEdgeEntity, + BRepGraph_VertexRefId()); + myGraph->myData->myIncStorage.RebindVertexEdge(aOrigEndChildVertexId, + BRepGraph_VertexId(), + theEdgeEntity, + BRepGraph_VertexRefId()); myGraph->markModified(theEdgeEntity); myGraph->markModified(theSubA); myGraph->markModified(theSubB); - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "Split: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "Split: post-mutation relation inconsistency"); } //================================================================================================= -void BRepGraph::EditorView::WireOps::ReplaceEdge(const BRepGraph_WireId theWireDefId, +void BRepGraph::EditorView::WireOps::ReplaceEdge(const BRepGraph_WireId theChildWireId, const BRepGraph_EdgeId theOldEdgeEntity, const BRepGraph_EdgeId theNewEdgeEntity, const bool theReversed) { - Standard_ASSERT_RETURN(theWireDefId.IsValid(myGraph->myData->myIncStorage.NbWires()), + Standard_ASSERT_RETURN(theChildWireId.IsValid(myGraph->myData->myIncStorage.NbWires()), "ReplaceEdge: wire index is out of range", Standard_VOID_RETURN); Standard_ASSERT_RETURN(theOldEdgeEntity.IsValid(myGraph->myData->myIncStorage.NbEdges()), @@ -2533,26 +3934,46 @@ void BRepGraph::EditorView::WireOps::ReplaceEdge(const BRepGraph_WireId theWireD Standard_ASSERT_RETURN(theNewEdgeEntity.IsValid(myGraph->myData->myIncStorage.NbEdges()), "ReplaceEdge: new edge index is out of range", Standard_VOID_RETURN); - Standard_ASSERT_RETURN(!myGraph->myData->myIncStorage.Edge(theNewEdgeEntity).IsRemoved, + Standard_ASSERT_RETURN(!myGraph->myData->myIncStorage.IsRemoved(theNewEdgeEntity), "ReplaceEdge: replacement edge must be active", Standard_VOID_RETURN); - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - - // Update incidence by scanning wire-owned coedge ref entries. - for (BRepGraph_RefsCoEdgeOfWire aRefIt(*myGraph, theWireDefId); aRefIt.More(); aRefIt.Next()) + if (!isActiveNode(myGraph->myData->myIncStorage, theChildWireId) + || !isActiveNode(myGraph->myData->myIncStorage, theOldEdgeEntity)) { - const BRepGraphInc::CoEdgeRef& aRef = aStorage.CoEdgeRef(aRefIt.CurrentId()); - const BRepGraph_CoEdgeId aCoEdgeDefId = aRef.CoEdgeDefId; - if (!aCoEdgeDefId.IsValid(aStorage.NbCoEdges())) + return; + } + + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const ReplaceEdgeStatus aStatus = + CheckReplaceEdge(theChildWireId, theOldEdgeEntity, theNewEdgeEntity, theReversed); + if (aStatus == ReplaceEdgeStatus::AlreadyCurrent) + { + return; + } + if (aStatus != ReplaceEdgeStatus::Ready) + { + return; + } + + // Update incidence by scanning wire-owned coedge entries. + for (BRepGraph_CoEdgesOfWire aRefIt(*myGraph, theChildWireId); aRefIt.More(); aRefIt.Next()) + { + const BRepGraph_CoEdgeId aCoChildEdgeId = aRefIt.CurrentId(); + if (!aCoChildEdgeId.IsValid(aStorage.NbCoEdges())) { continue; } - BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(aCoEdgeDefId); - if (aCoEdge.EdgeDefId == theOldEdgeEntity) + if (aStorage.IsRemoved(aCoChildEdgeId)) { - aCoEdge.EdgeDefId = theNewEdgeEntity; + continue; + } + + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(aCoChildEdgeId); + if (aCoEdge.ChildEdgeId == theOldEdgeEntity) + { + aCoEdge.ChildEdgeId = theNewEdgeEntity; if (theReversed) { aCoEdge.Orientation = TopAbs::Reverse(aCoEdge.Orientation); @@ -2561,54 +3982,159 @@ void BRepGraph::EditorView::WireOps::ReplaceEdge(const BRepGraph_WireId theWireD // When the canonical edge has opposite vertex ordering (isReversed=true), // the PCurve was defined for the old edge's direction and must be reversed // so it maps parameter-space coordinates relative to the canonical edge. - if (aCoEdge.Curve2DRepId.IsValid()) + if (aCoEdge.Curve2DRepId.IsValid() + && aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D())) { - BRepGraphInc::Curve2DRep& aCurveRep = aStorage.ChangeCurve2DRep(aCoEdge.Curve2DRepId); + BRepGraphInc::CoEdgeCurve2DRep& aCurveRep = + aStorage.ChangeCoEdgeCurve2DRep(aCoEdge.Curve2DRepId); if (!aCurveRep.Curve.IsNull()) { - const double anOldParamFirst = aCoEdge.ParamFirst; - const double anOldParamLast = aCoEdge.ParamLast; - aCoEdge.ParamFirst = aCurveRep.Curve->ReversedParameter(anOldParamLast); - aCoEdge.ParamLast = aCurveRep.Curve->ReversedParameter(anOldParamFirst); + const double anOldParamFirst = aCurveRep.ParamFirst; + const double anOldParamLast = aCurveRep.ParamLast; + aCurveRep.ParamFirst = aCurveRep.Curve->ReversedParameter(anOldParamLast); + aCurveRep.ParamLast = aCurveRep.Curve->ReversedParameter(anOldParamFirst); aCurveRep.Curve = aCurveRep.Curve->Reversed(); - std::swap(aCoEdge.UV1, aCoEdge.UV2); } } } - // Update reverse indices incrementally. - BRepGraphInc_ReverseIndex& aRevIdx = myGraph->myData->myIncStorage.ChangeReverseIndex(); - aRevIdx.ReplaceEdgeInWireMap(theOldEdgeEntity, theNewEdgeEntity, theWireDefId); - aRevIdx.UnbindEdgeFromCoEdge(theOldEdgeEntity, aCoEdgeDefId); - aRevIdx.BindEdgeToCoEdge(theNewEdgeEntity, aCoEdgeDefId); - - // Update edge-to-face: bind new edge, unbind old edge for all faces of this wire. - const NCollection_DynamicArray* aFaces = aRevIdx.FacesOfWire(theWireDefId); - if (aFaces != nullptr) - { - for (const BRepGraph_FaceId& aFaceId : *aFaces) - { - aRevIdx.BindEdgeToFace(theNewEdgeEntity, aFaceId); - aRevIdx.UnbindEdgeFromFace(theOldEdgeEntity, aFaceId); - } - } + myGraph->myData->myIncStorage.RebindCoEdgeEdge(aCoChildEdgeId, + theOldEdgeEntity, + theNewEdgeEntity); } } - myGraph->markModified(theWireDefId); + myGraph->markModified(theChildWireId); - // Validate reverse index only when not in deferred mode. + // Validate relation only when not in deferred mode. // In deferred mode (batch sewing, parallel mutations), intermediate states // may have temporarily stale entries; validation runs at CommitMutation(). - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "ReplaceEdge: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "ReplaceEdge: post-mutation relation inconsistency"); } } //================================================================================================= +void BRepGraph::EditorView::EdgeOps::Reverse(const BRepGraph_EdgeId theEdge) +{ + Standard_ASSERT_RETURN(theEdge.IsValid(myGraph->myData->myIncStorage.NbEdges()), + "Reverse: edge index is out of range", + Standard_VOID_RETURN); + if (!isActiveNode(myGraph->myData->myIncStorage, theEdge)) + { + return; + } + + BRepGraphInc::EdgeDef& aEdgeDef = myGraph->myData->myIncStorage.ChangeEdge(theEdge); + std::swap(aEdgeDef.StartVertexRefId, aEdgeDef.EndVertexRefId); + myGraph->markModified(theEdge); + + if (!myGraph->myData->myIncStorage.DeferredMode()) + { + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "Reverse: post-mutation relation inconsistency"); + } +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::Reverse(const BRepGraph_WireId theWire) +{ + Standard_ASSERT_RETURN(theWire.IsValid(myGraph->myData->myIncStorage.NbWires()), + "Reverse: wire index is out of range", + Standard_VOID_RETURN); + if (!isActiveNode(myGraph->myData->myIncStorage, theWire)) + { + return; + } + + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + aStorage.ReverseWireCoEdges(theWire); + + const NCollection_LinearVector& aRefs = + aStorage.WireRelations(theWire).CoEdgeIds; + const size_t aNb = aRefs.Size(); + for (size_t i = 0; i < aNb; ++i) + { + const BRepGraph_CoEdgeId aCoEdgeId = aRefs.Value(i); + if (!aCoEdgeId.IsValid(aStorage.NbCoEdges())) + { + continue; + } + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(aCoEdgeId); + if (aStorage.IsRemoved(aCoEdgeId)) + { + continue; + } + aCoEdge.Orientation = TopAbs::Reverse(aCoEdge.Orientation); + myGraph->markModified(aCoEdgeId); + } + + myGraph->markModified(theWire); + if (!myGraph->myData->myIncStorage.DeferredMode()) + { + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "Reverse: post-mutation relation inconsistency"); + } +} + +//================================================================================================= + +bool BRepGraph::EditorView::WireOps::SetCoEdgeOrder( + const BRepGraph_WireId theWire, + const NCollection_Array1& theCoEdgeIds) +{ + Standard_ASSERT_RETURN(theWire.IsValid(myGraph->myData->myIncStorage.NbWires()), + "SetCoEdgeOrder: wire index is out of range", + false); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!isActiveNode(aStorage, theWire)) + { + return false; + } + + const NCollection_LinearVector& anOldCoEdges = + aStorage.WireRelations(theWire).CoEdgeIds; + + NCollection_LinearVector aConnectedCoEdges; + const CoEdgeOrderStatus aStatus = + preCheckCoEdgeOrder(aStorage, theCoEdgeIds, theWire, false, true, aConnectedCoEdges); + if (aStatus == CoEdgeOrderStatus::AlreadyCurrent) + { + return true; + } + if (aStatus != CoEdgeOrderStatus::Ready && aStatus != CoEdgeOrderStatus::Reordered) + { + return false; + } + + NCollection_LinearVector anOldCoEdgeSnapshot; + for (const BRepGraph_CoEdgeId& aCoEdgeId : anOldCoEdges) + { + anOldCoEdgeSnapshot.Append(aCoEdgeId); + } + + aStorage.SetWireCoEdges(theWire, aConnectedCoEdges.ToArray1()); + if (!aStorage.ValidateWireCoEdgeOrder(theWire)) + { + aStorage.SetWireCoEdges(theWire, anOldCoEdgeSnapshot.ToArray1()); + return false; + } + myGraph->markModified(theWire); + if (!aStorage.DeferredMode()) + { + Standard_ASSERT_RETURN(aStorage.ValidateRelations(), + "SetCoEdgeOrder: post-mutation relation inconsistency", + false); + } + return true; +} + +//================================================================================================= + bool BRepGraph::EditorView::CompoundOps::RemoveChild(const BRepGraph_CompoundId theCompoundDefId, const BRepGraph_ChildRefId theChildRefId) { @@ -2625,28 +4151,32 @@ bool BRepGraph::EditorView::CompoundOps::RemoveChild(const BRepGraph_CompoundId return false; } - const BRepGraphInc::ChildRef& aRef = aStorage.ChildRef(theChildRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theCompoundDefId) - || !aRef.ChildDefId.IsValid() || !isActiveNode(aStorage, aRef.ChildDefId)) + const BRepGraphInc::ChildRef& aRef = aStorage.ChildRef(theChildRefId); + const NCollection_LinearVector& aCompoundRefIdsRO = + aStorage.CompoundRelations(theCompoundDefId).ChildRefIds; + if (aStorage.IsRemoved(theChildRefId) || !containsOrderedRef(aCompoundRefIdsRO, theChildRefId) + || !aRef.ChildNodeId.IsValid() || !isActiveNode(aStorage, aRef.ChildNodeId)) { return false; } - NCollection_DynamicArray& aCompoundRefIds = - aStorage.ChangeCompound(theCompoundDefId).ChildRefIds; - const BRepGraph_NodeId aChildDefId = aRef.ChildDefId; - if (!detachOrderedParentRef(*myGraph, theChildRefId, aCompoundRefIds, aChildDefId, true)) + const BRepGraph_NodeId aChildNodeId = aRef.ChildNodeId; + if (!detachOrderedParentRef(*myGraph, + myGraph->myData->myIncStorage, + theChildRefId, + aCompoundRefIdsRO, + aChildNodeId, + true)) { return false; } - aStorage.ChangeReverseIndex().UnbindCompoundChild(aChildDefId, theCompoundDefId); myGraph->markModified(theCompoundDefId); - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveChild: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveChild: post-mutation relation inconsistency"); } return true; @@ -2673,53 +4203,56 @@ bool BRepGraph::EditorView::ProductOps::RemoveOccurrence( } const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(theOccurrenceRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theProductDefId) - || !aRef.OccurrenceDefId.IsValid() || !isActiveNode(aStorage, aRef.OccurrenceDefId)) + const NCollection_LinearVector& aProductRefIdsRO = + aStorage.ProductRelations(theProductDefId).OccurrenceRefIds; + if (aStorage.IsRemoved(theOccurrenceRefId) + || !containsOrderedRef(aProductRefIdsRO, theOccurrenceRefId) + || !aRef.ChildOccurrenceId.IsValid() || !isActiveNode(aStorage, aRef.ChildOccurrenceId)) { return false; } - NCollection_DynamicArray& aProductRefIds = - aStorage.ChangeProduct(theProductDefId).OccurrenceRefIds; - const BRepGraph_OccurrenceId anOccDefId = aRef.OccurrenceDefId; - // Capture child def id before detach (myProductToOccurrences is keyed by ChildDefId). - const BRepGraph_NodeId aChildDefIdSnap = aStorage.Occurrence(anOccDefId).ChildDefId; + const BRepGraph_OccurrenceId anOccDefId = aRef.ChildOccurrenceId; + // Capture child def id before detach (myProductToOccurrences is keyed by ChildNodeId). + const BRepGraph_NodeId aChildNodeIdSnap = aStorage.Occurrence(anOccDefId).ChildNodeId; if (!detachOrderedParentRef(*myGraph, + myGraph->myData->myIncStorage, theOccurrenceRefId, - aProductRefIds, + aProductRefIdsRO, BRepGraph_NodeId(anOccDefId), true)) { return false; } - if (aChildDefIdSnap.IsValid() && aChildDefIdSnap.NodeKind == BRepGraph_NodeId::Kind::Product) + if (!productHasOccurrenceDef(aStorage, theProductDefId, anOccDefId)) + { + } + + if (!isOccurrenceDefOwnedByAnyProduct(aStorage, anOccDefId) && aChildNodeIdSnap.IsValid()) { - aStorage.ChangeReverseIndex().UnbindProductOccurrence( - anOccDefId, - BRepGraph_ProductId::FromNodeId(aChildDefIdSnap)); } // Check if the removed occurrence's child product was its last parent; // if so, re-add the child product to root products. const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(anOccDefId); - if (anOccDef.ChildDefId.IsValid() - && anOccDef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) + if (anOccDef.ChildNodeId.IsValid() + && anOccDef.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) { - const BRepGraph_ProductId aChildProduct = BRepGraph_ProductId::FromNodeId(anOccDef.ChildDefId); - if (aChildProduct.IsValid(aStorage.NbProducts()) && !aStorage.Product(aChildProduct).IsRemoved - && !hasAnyActiveUsage(*myGraph, anOccDef.ChildDefId)) + const BRepGraph_ProductId aChildProduct = BRepGraph_ProductId::FromNodeId(anOccDef.ChildNodeId); + if (aChildProduct.IsValid(aStorage.NbProducts()) && !aStorage.IsRemoved(aChildProduct) + && !hasAnyActiveUsage(*myGraph, anOccDef.ChildNodeId)) { - myGraph->myData->myRootProductIds.Append(aChildProduct); + myGraph->myData->myIncStorage.ChangeRootProductIds().Append(aChildProduct); } } myGraph->markModified(theProductDefId); - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveOccurrence: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveOccurrence: post-mutation relation inconsistency"); } return true; @@ -2739,22 +4272,23 @@ bool BRepGraph::EditorView::ProductOps::RemoveShapeRoot(const BRepGraph_ProductI return false; } - // Find the shape-root occurrence (the one whose ChildDefId is a topology node). - BRepGraphInc::ProductDef& aProduct = aStorage.ChangeProduct(theProductDefId); + // Find the shape-root occurrence (the one whose ChildNodeId is a topology node). BRepGraph_OccurrenceRefId aShapeRootRefId; BRepGraph_NodeId aShapeRoot; - for (const BRepGraph_OccurrenceRefId& aRefId : aProduct.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.ProductRelations(theProductDefId).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& aOccRef = aStorage.OccurrenceRef(aRefId); - if (aOccRef.IsRemoved) + if (aStorage.IsRemoved(aRefId)) { continue; } - const BRepGraphInc::OccurrenceDef& anOcc = aStorage.Occurrence(aOccRef.OccurrenceDefId); - if (!anOcc.IsRemoved && BRepGraph_NodeId::IsTopologyKind(anOcc.ChildDefId.NodeKind)) + const BRepGraphInc::OccurrenceDef& anOcc = aStorage.Occurrence(aOccRef.ChildOccurrenceId); + if (!aStorage.IsRemoved(aOccRef.ChildOccurrenceId) + && BRepGraph_NodeId::IsTopologyKind(anOcc.ChildNodeId.NodeKind)) { aShapeRootRefId = aRefId; - aShapeRoot = anOcc.ChildDefId; + aShapeRoot = anOcc.ChildNodeId; break; } } @@ -2766,14 +4300,15 @@ bool BRepGraph::EditorView::ProductOps::RemoveShapeRoot(const BRepGraph_ProductI // Detach the shape-root occurrence from the product. const BRepGraph_OccurrenceId aShapeRootOccId = - aStorage.OccurrenceRef(aShapeRootRefId).OccurrenceDefId; + aStorage.OccurrenceRef(aShapeRootRefId).ChildOccurrenceId; detachOrderedParentRef(*myGraph, + myGraph->myData->myIncStorage, aShapeRootRefId, - aProduct.OccurrenceRefIds, + aStorage.ProductRelations(theProductDefId).OccurrenceRefIds, BRepGraph_NodeId(aShapeRootOccId), true); - // No reverse-index unbind: shape-root occurrence's ChildDefId is topology, not a product. + // No relation unbind: shape-root occurrence's ChildNodeId is topology, not a product. if (!hasAnyActiveUsage(*myGraph, aShapeRoot)) { @@ -2782,10 +4317,10 @@ bool BRepGraph::EditorView::ProductOps::RemoveShapeRoot(const BRepGraph_ProductI myGraph->markModified(theProductDefId); - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveShapeRoot: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveShapeRoot: post-mutation relation inconsistency"); } return true; @@ -2793,96 +4328,11 @@ bool BRepGraph::EditorView::ProductOps::RemoveShapeRoot(const BRepGraph_ProductI //================================================================================================= -bool BRepGraph::EditorView::ShellOps::RemoveChild(const BRepGraph_ShellId theShellDefId, - const BRepGraph_ChildRefId theChildRefId) +bool BRepGraph::EditorView::CompSolidOps::RemoveSolid( + const BRepGraph_CompSolidId theCompChildSolidId, + const BRepGraph_SolidRefId theSolidRefId) { - Standard_ASSERT_RETURN(theShellDefId.IsValid(myGraph->myData->myIncStorage.NbShells()), - "RemoveChild: shell index is out of range", - false); - Standard_ASSERT_RETURN(theChildRefId.IsValid(myGraph->myData->myIncStorage.NbChildRefs()), - "RemoveChild: child ref index is out of range", - false); - - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theShellDefId)) - { - return false; - } - - const BRepGraphInc::ChildRef& aRef = aStorage.ChildRef(theChildRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theShellDefId) - || !aRef.ChildDefId.IsValid() || !isActiveNode(aStorage, aRef.ChildDefId)) - { - return false; - } - - NCollection_DynamicArray& aShellRefIds = - aStorage.ChangeShell(theShellDefId).AuxChildRefIds; - if (!detachOrderedParentRef(*myGraph, theChildRefId, aShellRefIds, aRef.ChildDefId, true)) - { - return false; - } - - myGraph->markModified(theShellDefId); - - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) - { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveChild: post-mutation reverse index inconsistency"); - } - - return true; -} - -//================================================================================================= - -bool BRepGraph::EditorView::SolidOps::RemoveChild(const BRepGraph_SolidId theSolidDefId, - const BRepGraph_ChildRefId theChildRefId) -{ - Standard_ASSERT_RETURN(theSolidDefId.IsValid(myGraph->myData->myIncStorage.NbSolids()), - "RemoveChild: solid index is out of range", - false); - Standard_ASSERT_RETURN(theChildRefId.IsValid(myGraph->myData->myIncStorage.NbChildRefs()), - "RemoveChild: child ref index is out of range", - false); - - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theSolidDefId)) - { - return false; - } - - const BRepGraphInc::ChildRef& aRef = aStorage.ChildRef(theChildRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theSolidDefId) - || !aRef.ChildDefId.IsValid() || !isActiveNode(aStorage, aRef.ChildDefId)) - { - return false; - } - - NCollection_DynamicArray& aSolidRefIds = - aStorage.ChangeSolid(theSolidDefId).AuxChildRefIds; - if (!detachOrderedParentRef(*myGraph, theChildRefId, aSolidRefIds, aRef.ChildDefId, true)) - { - return false; - } - - myGraph->markModified(theSolidDefId); - - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) - { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveChild: post-mutation reverse index inconsistency"); - } - - return true; -} - -//================================================================================================= - -bool BRepGraph::EditorView::CompSolidOps::RemoveSolid(const BRepGraph_CompSolidId theCompSolidDefId, - const BRepGraph_SolidRefId theSolidRefId) -{ - Standard_ASSERT_RETURN(theCompSolidDefId.IsValid(myGraph->myData->myIncStorage.NbCompSolids()), + Standard_ASSERT_RETURN(theCompChildSolidId.IsValid(myGraph->myData->myIncStorage.NbCompSolids()), "RemoveSolid: compsolid index is out of range", false); Standard_ASSERT_RETURN(theSolidRefId.IsValid(myGraph->myData->myIncStorage.NbSolidRefs()), @@ -2890,37 +4340,37 @@ bool BRepGraph::EditorView::CompSolidOps::RemoveSolid(const BRepGraph_CompSolidI false); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theCompSolidDefId)) + if (!isActiveNode(aStorage, theCompChildSolidId)) { return false; } - const BRepGraphInc::SolidRef& aRef = aStorage.SolidRef(theSolidRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theCompSolidDefId) - || !aRef.SolidDefId.IsValid() || !isActiveNode(aStorage, aRef.SolidDefId)) + const BRepGraphInc::SolidRef& aRef = aStorage.SolidRef(theSolidRefId); + const NCollection_LinearVector& aCompSolidRefIdsRO = + aStorage.CompSolidRelations(theCompChildSolidId).SolidRefIds; + if (aStorage.IsRemoved(theSolidRefId) || !containsOrderedRef(aCompSolidRefIdsRO, theSolidRefId) + || !aRef.ChildSolidId.IsValid() || !isActiveNode(aStorage, aRef.ChildSolidId)) { return false; } - NCollection_DynamicArray& aCompSolidRefIds = - aStorage.ChangeCompSolid(theCompSolidDefId).SolidRefIds; - const BRepGraph_SolidId aSolidDefId = aRef.SolidDefId; + const BRepGraph_SolidId aChildSolidId = aRef.ChildSolidId; if (!detachOrderedParentRef(*myGraph, + myGraph->myData->myIncStorage, theSolidRefId, - aCompSolidRefIds, - BRepGraph_NodeId(aSolidDefId), + aCompSolidRefIdsRO, + BRepGraph_NodeId(aChildSolidId), true)) { return false; } - aStorage.ChangeReverseIndex().UnbindSolidFromCompSolid(aSolidDefId, theCompSolidDefId); - myGraph->markModified(theCompSolidDefId); + myGraph->markModified(theCompChildSolidId); - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveSolid: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveSolid: post-mutation relation inconsistency"); } return true; @@ -2928,10 +4378,10 @@ bool BRepGraph::EditorView::CompSolidOps::RemoveSolid(const BRepGraph_CompSolidI //================================================================================================= -bool BRepGraph::EditorView::SolidOps::RemoveShell(const BRepGraph_SolidId theSolidDefId, +bool BRepGraph::EditorView::SolidOps::RemoveShell(const BRepGraph_SolidId theChildSolidId, const BRepGraph_ShellRefId theShellRefId) { - Standard_ASSERT_RETURN(theSolidDefId.IsValid(myGraph->myData->myIncStorage.NbSolids()), + Standard_ASSERT_RETURN(theChildSolidId.IsValid(myGraph->myData->myIncStorage.NbSolids()), "RemoveShell: solid index is out of range", false); Standard_ASSERT_RETURN(theShellRefId.IsValid(myGraph->myData->myIncStorage.NbShellRefs()), @@ -2939,37 +4389,37 @@ bool BRepGraph::EditorView::SolidOps::RemoveShell(const BRepGraph_SolidId the false); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theSolidDefId)) + if (!isActiveNode(aStorage, theChildSolidId)) { return false; } - const BRepGraphInc::ShellRef& aRef = aStorage.ShellRef(theShellRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theSolidDefId) - || !aRef.ShellDefId.IsValid() || !isActiveNode(aStorage, aRef.ShellDefId)) + const BRepGraphInc::ShellRef& aRef = aStorage.ShellRef(theShellRefId); + const NCollection_LinearVector& aSolidRefIdsRO = + aStorage.SolidRelations(theChildSolidId).ShellRefIds; + if (aStorage.IsRemoved(theShellRefId) || !containsOrderedRef(aSolidRefIdsRO, theShellRefId) + || !aRef.ChildShellId.IsValid() || !isActiveNode(aStorage, aRef.ChildShellId)) { return false; } - NCollection_DynamicArray& aSolidRefIds = - aStorage.ChangeSolid(theSolidDefId).ShellRefIds; - const BRepGraph_ShellId aShellDefId = aRef.ShellDefId; + const BRepGraph_ShellId aChildShellId = aRef.ChildShellId; if (!detachOrderedParentRef(*myGraph, + myGraph->myData->myIncStorage, theShellRefId, - aSolidRefIds, - BRepGraph_NodeId(aShellDefId), + aSolidRefIdsRO, + BRepGraph_NodeId(aChildShellId), true)) { return false; } - aStorage.ChangeReverseIndex().UnbindShellFromSolid(aShellDefId, theSolidDefId); - myGraph->markModified(theSolidDefId); + myGraph->markModified(theChildSolidId); - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveShell: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveShell: post-mutation relation inconsistency"); } return true; @@ -2977,10 +4427,10 @@ bool BRepGraph::EditorView::SolidOps::RemoveShell(const BRepGraph_SolidId the //================================================================================================= -bool BRepGraph::EditorView::ShellOps::RemoveFace(const BRepGraph_ShellId theShellDefId, +bool BRepGraph::EditorView::ShellOps::RemoveFace(const BRepGraph_ShellId theChildShellId, const BRepGraph_FaceRefId theFaceRefId) { - Standard_ASSERT_RETURN(theShellDefId.IsValid(myGraph->myData->myIncStorage.NbShells()), + Standard_ASSERT_RETURN(theChildShellId.IsValid(myGraph->myData->myIncStorage.NbShells()), "RemoveFace: shell index is out of range", false); Standard_ASSERT_RETURN(theFaceRefId.IsValid(myGraph->myData->myIncStorage.NbFaceRefs()), @@ -2988,37 +4438,37 @@ bool BRepGraph::EditorView::ShellOps::RemoveFace(const BRepGraph_ShellId theSh false); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theShellDefId)) + if (!isActiveNode(aStorage, theChildShellId)) { return false; } - const BRepGraphInc::FaceRef& aRef = aStorage.FaceRef(theFaceRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theShellDefId) - || !aRef.FaceDefId.IsValid() || !isActiveNode(aStorage, aRef.FaceDefId)) + const BRepGraphInc::FaceRef& aRef = aStorage.FaceRef(theFaceRefId); + const NCollection_LinearVector& aShellRefIdsRO = + aStorage.ShellRelations(theChildShellId).FaceRefIds; + if (aStorage.IsRemoved(theFaceRefId) || !containsOrderedRef(aShellRefIdsRO, theFaceRefId) + || !aRef.ChildFaceId.IsValid() || !isActiveNode(aStorage, aRef.ChildFaceId)) { return false; } - NCollection_DynamicArray& aShellRefIds = - aStorage.ChangeShell(theShellDefId).FaceRefIds; - const BRepGraph_FaceId aFaceDefId = aRef.FaceDefId; + const BRepGraph_FaceId aFaceId = aRef.ChildFaceId; if (!detachOrderedParentRef(*myGraph, + myGraph->myData->myIncStorage, theFaceRefId, - aShellRefIds, - BRepGraph_NodeId(aFaceDefId), + aShellRefIdsRO, + BRepGraph_NodeId(aFaceId), true)) { return false; } - aStorage.ChangeReverseIndex().UnbindFaceFromShell(aFaceDefId, theShellDefId); - myGraph->markModified(theShellDefId); + myGraph->markModified(theChildShellId); - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveFace: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveFace: post-mutation relation inconsistency"); } return true; @@ -3026,57 +4476,10 @@ bool BRepGraph::EditorView::ShellOps::RemoveFace(const BRepGraph_ShellId theSh //================================================================================================= -bool BRepGraph::EditorView::FaceOps::RemoveVertex(const BRepGraph_FaceId theFaceDefId, - const BRepGraph_VertexRefId theVertexRefId) -{ - Standard_ASSERT_RETURN(theFaceDefId.IsValid(myGraph->myData->myIncStorage.NbFaces()), - "RemoveVertex: face index is out of range", - false); - Standard_ASSERT_RETURN(theVertexRefId.IsValid(myGraph->myData->myIncStorage.NbVertexRefs()), - "RemoveVertex: vertex ref index is out of range", - false); - - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theFaceDefId)) - { - return false; - } - - const BRepGraphInc::VertexRef& aRef = aStorage.VertexRef(theVertexRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theFaceDefId) - || !aRef.VertexDefId.IsValid() || !isActiveNode(aStorage, aRef.VertexDefId)) - { - return false; - } - - NCollection_DynamicArray& aFaceRefIds = - aStorage.ChangeFace(theFaceDefId).VertexRefIds; - if (!detachOrderedParentRef(*myGraph, - theVertexRefId, - aFaceRefIds, - BRepGraph_NodeId(aRef.VertexDefId), - true)) - { - return false; - } - - myGraph->markModified(theFaceDefId); - - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) - { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveVertex: post-mutation reverse index inconsistency"); - } - - return true; -} - -//================================================================================================= - -bool BRepGraph::EditorView::FaceOps::RemoveWire(const BRepGraph_FaceId theFaceDefId, +bool BRepGraph::EditorView::FaceOps::RemoveWire(const BRepGraph_FaceId theFaceId, const BRepGraph_WireRefId theWireRefId) { - Standard_ASSERT_RETURN(theFaceDefId.IsValid(myGraph->myData->myIncStorage.NbFaces()), + Standard_ASSERT_RETURN(theFaceId.IsValid(myGraph->myData->myIncStorage.NbFaces()), "RemoveWire: face index is out of range", false); Standard_ASSERT_RETURN(theWireRefId.IsValid(myGraph->myData->myIncStorage.NbWireRefs()), @@ -3084,37 +4487,76 @@ bool BRepGraph::EditorView::FaceOps::RemoveWire(const BRepGraph_FaceId theFac false); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theFaceDefId)) + if (!isActiveNode(aStorage, theFaceId)) { return false; } - const BRepGraphInc::WireRef& aRef = aStorage.WireRef(theWireRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theFaceDefId) || !aRef.WireDefId.IsValid() - || !isActiveNode(aStorage, aRef.WireDefId)) + const BRepGraphInc::WireRef& aRef = aStorage.WireRef(theWireRefId); + const NCollection_LinearVector& aFaceRefIdsRO = + aStorage.FaceRelations(theFaceId).WireRefIds; + if (aStorage.IsRemoved(theWireRefId) || !containsOrderedRef(aFaceRefIdsRO, theWireRefId) + || !aRef.ChildWireId.IsValid() || !isActiveNode(aStorage, aRef.ChildWireId)) { return false; } - NCollection_DynamicArray& aFaceRefIds = - aStorage.ChangeFace(theFaceDefId).WireRefIds; - const BRepGraph_WireId aWireDefId = aRef.WireDefId; + const BRepGraph_WireId aChildWireId = aRef.ChildWireId; + NCollection_LinearVector anAffectedEdges; + NCollection_LinearVector anAffectedFaces; + NCollection_LinearVector anAffectedCoEdges; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aStorage.WireRelations(aChildWireId).CoEdgeIds) + { + if (!aCoEdgeId.IsValid(aStorage.NbCoEdges()) || aStorage.IsRemoved(aCoEdgeId)) + { + continue; + } + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); + if (aCoEdge.FaceId == theFaceId) + { + anAffectedCoEdges.Append(aCoEdgeId); + } + if (aCoEdge.ChildEdgeId.IsValid(aStorage.NbEdges())) + { + appendUniqueRelationId(anAffectedEdges, aCoEdge.ChildEdgeId); + } + if (aCoEdge.FaceId.IsValid(aStorage.NbFaces())) + { + appendUniqueRelationId(anAffectedFaces, aCoEdge.FaceId); + } + } if (!detachOrderedParentRef(*myGraph, + myGraph->myData->myIncStorage, theWireRefId, - aFaceRefIds, - BRepGraph_NodeId(aWireDefId), + aFaceRefIdsRO, + BRepGraph_NodeId(aChildWireId), true)) { return false; } - - aStorage.ChangeReverseIndex().UnbindWireFromFace(aWireDefId, theFaceDefId); - myGraph->markModified(theFaceDefId); - - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + for (const BRepGraph_CoEdgeId& aCoEdgeId : anAffectedCoEdges) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveWire: post-mutation reverse index inconsistency"); + if (!aCoEdgeId.IsValid(aStorage.NbCoEdges()) || aStorage.IsRemoved(aCoEdgeId)) + { + continue; + } + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); + if (aCoEdge.FaceId.IsValid(aStorage.NbFaces())) + { + continue; + } + aStorage.MarkRemoved(aCoEdge.Curve2DRepId); + aStorage.MarkRemoved(aCoEdge.Polygon2DRepId); + aStorage.MarkRemoved(aCoEdge.PolygonOnTriRepId); + myGraph->markModified(aCoEdgeId); + } + + myGraph->markModified(theFaceId); + + if (!myGraph->myData->myIncStorage.DeferredMode()) + { + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveWire: post-mutation relation inconsistency"); } return true; @@ -3122,121 +4564,59 @@ bool BRepGraph::EditorView::FaceOps::RemoveWire(const BRepGraph_FaceId theFac //================================================================================================= -bool BRepGraph::EditorView::WireOps::RemoveCoEdge(const BRepGraph_WireId theWireDefId, - const BRepGraph_CoEdgeRefId theCoEdgeRefId) +bool BRepGraph::EditorView::WireOps::RemoveCoEdge(const BRepGraph_WireId theChildWireId, + const BRepGraph_CoEdgeId theCoEdgeId) { - Standard_ASSERT_RETURN(theWireDefId.IsValid(myGraph->myData->myIncStorage.NbWires()), + Standard_ASSERT_RETURN(theChildWireId.IsValid(myGraph->myData->myIncStorage.NbWires()), "RemoveCoEdge: wire index is out of range", false); - Standard_ASSERT_RETURN(theCoEdgeRefId.IsValid(myGraph->myData->myIncStorage.NbCoEdgeRefs()), - "RemoveCoEdge: coedge ref index is out of range", + Standard_ASSERT_RETURN(theCoEdgeId.IsValid(myGraph->myData->myIncStorage.NbCoEdges()), + "RemoveCoEdge: coedge index is out of range", false); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theWireDefId)) + if (!isActiveNode(aStorage, theChildWireId)) { return false; } - const BRepGraphInc::CoEdgeRef& aRef = aStorage.CoEdgeRef(theCoEdgeRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theWireDefId) - || !aRef.CoEdgeDefId.IsValid() || !isActiveNode(aStorage, aRef.CoEdgeDefId)) + if (!isActiveNode(aStorage, theCoEdgeId)) { return false; } - const BRepGraph_CoEdgeId aCoEdgeEntity = aRef.CoEdgeDefId; - const BRepGraphInc::CoEdgeDef& aCoEdgeDef = aStorage.CoEdge(aCoEdgeEntity); - const BRepGraph_EdgeId anEdgeId = aCoEdgeDef.EdgeDefId; - const BRepGraph_FaceId aFaceId = aCoEdgeDef.FaceDefId; - - NCollection_DynamicArray& aWireRefIds = - aStorage.ChangeWire(theWireDefId).CoEdgeRefIds; - if (!detachOrderedParentRef(*myGraph, - theCoEdgeRefId, - aWireRefIds, - BRepGraph_NodeId(aCoEdgeEntity), - true)) + const NCollection_LinearVector& aWireCoEdgeIds = + aStorage.WireRelations(theChildWireId).CoEdgeIds; + if (!findOrderedRef(aWireCoEdgeIds, theCoEdgeId).IsValid()) { return false; } + NCollection_LinearVector aRemainingCoEdges; + if (!rotateConnectedCoEdgesAfterRemoval(aStorage, aWireCoEdgeIds, theCoEdgeId, aRemainingCoEdges)) + { + return false; + } + + const BRepGraph_CoEdgeId aCoEdgeEntity = theCoEdgeId; + + aStorage.DetachCoEdgeUse(theChildWireId, theCoEdgeId); + if (!aRemainingCoEdges.IsEmpty()) + { + aStorage.SetWireCoEdges(theChildWireId, aRemainingCoEdges.ToArray1()); + } + const bool hasRemainingCoEdgeUsage = hasAnyActiveUsage(*myGraph, aCoEdgeEntity); - auto hasRemainingEdgeUseInWire = [&]() { - if (!anEdgeId.IsValid()) - { - return false; - } - - for (BRepGraph_RefsCoEdgeOfWire aRefIt(*myGraph, theWireDefId); aRefIt.More(); aRefIt.Next()) - { - const BRepGraphInc::CoEdgeRef& aRef = aStorage.CoEdgeRef(aRefIt.CurrentId()); - if (!aRef.CoEdgeDefId.IsValid()) - { - continue; - } - - const BRepGraphInc::CoEdgeDef& aCandidate = aStorage.CoEdge(aRef.CoEdgeDefId); - if (!aCandidate.IsRemoved && aCandidate.EdgeDefId == anEdgeId) - { - return true; - } - } - return false; - }; - - auto hasRemainingEdgeUseOnFace = [&]() { - if (!anEdgeId.IsValid() || !aFaceId.IsValid()) - { - return false; - } - - const NCollection_DynamicArray& aCoEdges = - aStorage.ReverseIndex().CoEdgesOfEdgeRef(anEdgeId); - for (const BRepGraph_CoEdgeId& aCandidateId : aCoEdges) - { - if (!aCandidateId.IsValid()) - { - continue; - } - if (aCandidateId == aCoEdgeEntity && !hasRemainingCoEdgeUsage) - { - continue; - } - - const BRepGraphInc::CoEdgeDef& aCandidate = aStorage.CoEdge(aCandidateId); - if (!aCandidate.IsRemoved && aCandidate.FaceDefId == aFaceId) - { - return true; - } - } - return false; - }; - - BRepGraphInc_ReverseIndex& aRevIdx = aStorage.ChangeReverseIndex(); - aRevIdx.UnbindCoEdgeFromWire(aCoEdgeEntity, theWireDefId); - - if (anEdgeId.IsValid() && !hasRemainingEdgeUseInWire()) + myGraph->markModified(theChildWireId); + if (!hasRemainingCoEdgeUsage) { - aRevIdx.UnbindEdgeFromWire(anEdgeId, theWireDefId); + myGraph->Editor().Gen().RemoveSubgraph(aCoEdgeEntity); } - - if (anEdgeId.IsValid() && !hasRemainingCoEdgeUsage) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - aRevIdx.UnbindEdgeFromCoEdge(anEdgeId, aCoEdgeEntity); - if (aFaceId.IsValid() && !hasRemainingEdgeUseOnFace()) - { - aRevIdx.UnbindEdgeFromFace(anEdgeId, aFaceId); - } - } - - myGraph->markModified(theWireDefId); - - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) - { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveCoEdge: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveCoEdge: post-mutation relation inconsistency"); } return true; @@ -3244,10 +4624,10 @@ bool BRepGraph::EditorView::WireOps::RemoveCoEdge(const BRepGraph_WireId th //================================================================================================= -bool BRepGraph::EditorView::EdgeOps::RemoveVertex(const BRepGraph_EdgeId theEdgeDefId, +bool BRepGraph::EditorView::EdgeOps::RemoveVertex(const BRepGraph_EdgeId theChildEdgeId, const BRepGraph_VertexRefId theVertexRefId) { - Standard_ASSERT_RETURN(theEdgeDefId.IsValid(myGraph->myData->myIncStorage.NbEdges()), + Standard_ASSERT_RETURN(theChildEdgeId.IsValid(myGraph->myData->myIncStorage.NbEdges()), "RemoveVertex: edge index is out of range", false); Standard_ASSERT_RETURN(theVertexRefId.IsValid(myGraph->myData->myIncStorage.NbVertexRefs()), @@ -3255,49 +4635,38 @@ bool BRepGraph::EditorView::EdgeOps::RemoveVertex(const BRepGraph_EdgeId th false); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theEdgeDefId)) + if (!isActiveNode(aStorage, theChildEdgeId)) { return false; } - const BRepGraphInc::VertexRef& aRef = aStorage.VertexRef(theVertexRefId); - if (aRef.IsRemoved || aRef.ParentId != BRepGraph_NodeId(theEdgeDefId) - || !aRef.VertexDefId.IsValid() || !isActiveNode(aStorage, aRef.VertexDefId)) + const BRepGraphInc::VertexRef& aRef = aStorage.VertexRef(theVertexRefId); + const BRepGraphInc::EdgeDef& anEdgeRO = aStorage.Edge(theChildEdgeId); + if (aStorage.IsRemoved(theVertexRefId) + || (anEdgeRO.StartVertexRefId != theVertexRefId && anEdgeRO.EndVertexRefId != theVertexRefId) + || !aRef.ChildVertexId.IsValid() || !isActiveNode(aStorage, aRef.ChildVertexId)) { return false; } - BRepGraphInc::EdgeDef& aEdge = aStorage.ChangeEdge(theEdgeDefId); + BRepGraphInc::EdgeDef& aEdge = aStorage.ChangeEdge(theChildEdgeId); bool isFound = false; bool isFixed = false; if (aEdge.StartVertexRefId == theVertexRefId) { - if (!myGraph->Editor().Gen().RemoveRef(theVertexRefId)) - { - return false; - } aEdge.StartVertexRefId = BRepGraph_VertexRefId(); isFound = true; isFixed = true; } else if (aEdge.EndVertexRefId == theVertexRefId) { - if (!myGraph->Editor().Gen().RemoveRef(theVertexRefId)) - { - return false; - } aEdge.EndVertexRefId = BRepGraph_VertexRefId(); isFound = true; isFixed = true; } else { - NCollection_DynamicArray& anEdgeRefIds = aEdge.InternalVertexRefIds; - isFound = detachOrderedParentRef(*myGraph, - theVertexRefId, - anEdgeRefIds, - BRepGraph_NodeId(aRef.VertexDefId), - true); + return false; } if (!isFound) @@ -3305,50 +4674,24 @@ bool BRepGraph::EditorView::EdgeOps::RemoveVertex(const BRepGraph_EdgeId th return false; } - const BRepGraph_VertexId aVertexDefId = aRef.VertexDefId; - if (isFixed && !hasAnyActiveUsage(*myGraph, BRepGraph_NodeId(aVertexDefId))) + const BRepGraph_VertexId aChildVertexId = aRef.ChildVertexId; + if (!isRefOwnedByAnyParent(myGraph->myData->myIncStorage, theVertexRefId) + && !myGraph->Editor().Gen().RemoveRef(theVertexRefId)) { - myGraph->Editor().Gen().RemoveSubgraph(BRepGraph_NodeId(aVertexDefId)); + return false; + } + if (isFixed && !hasAnyActiveUsage(*myGraph, BRepGraph_NodeId(aChildVertexId))) + { + myGraph->Editor().Gen().RemoveSubgraph(BRepGraph_NodeId(aChildVertexId)); } - // Unbind vertex->edge only when no other active ref from this edge points to the same vertex. - bool aStillReferenced = false; - { - const BRepGraphInc::EdgeDef& anEdgeRO = aStorage.Edge(theEdgeDefId); - auto refTargets = [&](const BRepGraph_VertexRefId theRefId) { - if (!theRefId.IsValid()) - { - return false; - } - const BRepGraphInc::VertexRef& aVR = aStorage.VertexRef(theRefId); - return !aVR.IsRemoved && aVR.VertexDefId == aVertexDefId; - }; - if (refTargets(anEdgeRO.StartVertexRefId) || refTargets(anEdgeRO.EndVertexRefId)) - { - aStillReferenced = true; - } - else - { - for (const BRepGraph_VertexRefId& anIVRefId : anEdgeRO.InternalVertexRefIds) - { - if (refTargets(anIVRefId)) - { - aStillReferenced = true; - break; - } - } - } - } - if (!aStillReferenced) - { - aStorage.ChangeReverseIndex().UnbindVertexFromEdge(aVertexDefId, theEdgeDefId); - } - myGraph->markModified(theEdgeDefId); + aStorage.RebindVertexEdge(aChildVertexId, BRepGraph_VertexId(), theChildEdgeId, theVertexRefId); + myGraph->markModified(theChildEdgeId); - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!myGraph->myData->myIncStorage.DeferredMode()) { - Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateReverseIndex(), - "RemoveVertex: post-mutation reverse index inconsistency"); + Standard_ASSERT_VOID(myGraph->myData->myIncStorage.ValidateRelations(), + "RemoveVertex: post-mutation relation inconsistency"); } return true; @@ -3357,39 +4700,41 @@ bool BRepGraph::EditorView::EdgeOps::RemoveVertex(const BRepGraph_EdgeId th //================================================================================================= BRepGraph_VertexRefId BRepGraph::EditorView::EdgeOps::ReplaceVertex( - const BRepGraph_EdgeId theEdgeDefId, + const BRepGraph_EdgeId theChildEdgeId, const BRepGraph_VertexRefId theOldVertexRefId, - const BRepGraph_VertexId theNewVertexDefId) + const BRepGraph_VertexId theNewChildVertexId) { - Standard_ASSERT_RETURN(theEdgeDefId.IsValid(myGraph->myData->myIncStorage.NbEdges()), + Standard_ASSERT_RETURN(theChildEdgeId.IsValid(myGraph->myData->myIncStorage.NbEdges()), "ReplaceVertex: edge index is out of range", BRepGraph_VertexRefId()); Standard_ASSERT_RETURN(theOldVertexRefId.IsValid(myGraph->myData->myIncStorage.NbVertexRefs()), "ReplaceVertex: old vertex ref index is out of range", BRepGraph_VertexRefId()); - Standard_ASSERT_RETURN(theNewVertexDefId.IsValid(myGraph->myData->myIncStorage.NbVertices()), + Standard_ASSERT_RETURN(theNewChildVertexId.IsValid(myGraph->myData->myIncStorage.NbVertices()), "ReplaceVertex: new vertex index is out of range", BRepGraph_VertexRefId()); BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!isActiveNode(aStorage, theEdgeDefId) || !isActiveNode(aStorage, theNewVertexDefId)) + if (!isActiveNode(aStorage, theChildEdgeId) || !isActiveNode(aStorage, theNewChildVertexId)) { return BRepGraph_VertexRefId(); } // Capture old ref fields before any mutation (vector may reallocate). - const BRepGraphInc::VertexRef& aOldRef = aStorage.VertexRef(theOldVertexRefId); - if (aOldRef.IsRemoved || aOldRef.ParentId != BRepGraph_NodeId(theEdgeDefId) - || !aOldRef.VertexDefId.IsValid()) + const BRepGraphInc::VertexRef& aOldRef = aStorage.VertexRef(theOldVertexRefId); + const BRepGraphInc::EdgeDef& anOldEdge = aStorage.Edge(theChildEdgeId); + if (aStorage.IsRemoved(theOldVertexRefId) + || (anOldEdge.StartVertexRefId != theOldVertexRefId + && anOldEdge.EndVertexRefId != theOldVertexRefId) + || !aOldRef.ChildVertexId.IsValid()) { return BRepGraph_VertexRefId(); } - const BRepGraph_VertexId aOldVertexDefId = aOldRef.VertexDefId; - const TopAbs_Orientation aOldOri = aOldRef.Orientation; - const TopLoc_Location aOldLoc = aOldRef.LocalLocation; + const BRepGraph_VertexId aOldChildVertexId = aOldRef.ChildVertexId; + const TopAbs_Orientation aOldOri = aOldRef.Orientation; // Short-circuit: no-op if the new vertex is the same as the current one. - if (aOldVertexDefId == theNewVertexDefId) + if (aOldChildVertexId == theNewChildVertexId) { return theOldVertexRefId; } @@ -3397,14 +4742,13 @@ BRepGraph_VertexRefId BRepGraph::EditorView::EdgeOps::ReplaceVertex( // Allocate the replacement ref entry. const BRepGraph_VertexRefId aNewRefId = aStorage.AppendVertexRef(); BRepGraphInc::VertexRef& aNewRef = aStorage.ChangeVertexRef(aNewRefId); - aNewRef.ParentId = BRepGraph_NodeId(theEdgeDefId); - aNewRef.VertexDefId = theNewVertexDefId; + aNewRef.ChildVertexId = theNewChildVertexId; + aNewRef.ParentEdgeId = theChildEdgeId; aNewRef.Orientation = aOldOri; - aNewRef.LocalLocation = aOldLoc; myGraph->allocateRefUID(aNewRefId); // Swap the edge's slot that owned the old ref. - BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdgeDefId); + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theChildEdgeId); if (anEdge.StartVertexRefId == theOldVertexRefId) { anEdge.StartVertexRefId = aNewRefId; @@ -3415,43 +4759,29 @@ BRepGraph_VertexRefId BRepGraph::EditorView::EdgeOps::ReplaceVertex( } else { - bool isFound = false; - for (NCollection_DynamicArray::Iterator anIt( - anEdge.InternalVertexRefIds); - anIt.More(); - anIt.Next()) - { - if (anIt.Value() == theOldVertexRefId) - { - anIt.ChangeValue() = aNewRefId; - isFound = true; - break; - } - } - if (!isFound) - { - // The ref belongs to this edge (ParentId matched) but is not owned by - // any of its slot vectors. Roll back the append and report failure. - aStorage.MarkRemovedRef(BRepGraph_RefId(aNewRefId)); - return BRepGraph_VertexRefId(); - } + // Supplemental direct vertex usages are no longer represented as persisted + // edge-owned refs. + aStorage.MarkRemovedRef(BRepGraph_RefId(aNewRefId)); + return BRepGraph_VertexRefId(); } - // Retire the old ref entry. - aStorage.MarkRemovedRef(BRepGraph_RefId(theOldVertexRefId)); - - // Keep vertex->edge reverse index in sync. - BRepGraphInc_ReverseIndex& aRevIdx = aStorage.ChangeReverseIndex(); - aRevIdx.UnbindVertexFromEdge(aOldVertexDefId, theEdgeDefId); - aRevIdx.BindVertexToEdge(theNewVertexDefId, theEdgeDefId); - - myGraph->markRefModified(aNewRefId, aNewRef); - myGraph->markModified(theEdgeDefId); - - if (!myGraph->myData->myDeferredMode.load(std::memory_order_relaxed)) + if (!isRefOwnedByAnyParent(myGraph->myData->myIncStorage, theOldVertexRefId)) { - Standard_ASSERT_VOID(aStorage.ValidateReverseIndex(), - "ReplaceVertex: post-mutation reverse index inconsistency"); + myGraph->Editor().Gen().RemoveRef(theOldVertexRefId); + } + + aStorage.RebindVertexEdge(aOldChildVertexId, + theNewChildVertexId, + theChildEdgeId, + theOldVertexRefId); + + myGraph->markRefModified(aNewRefId); + myGraph->markModified(theChildEdgeId); + + if (!myGraph->myData->myIncStorage.DeferredMode()) + { + Standard_ASSERT_VOID(aStorage.ValidateRelations(), + "ReplaceVertex: post-mutation relation inconsistency"); } return aNewRefId; @@ -3461,29 +4791,26 @@ BRepGraph_VertexRefId BRepGraph::EditorView::EdgeOps::ReplaceVertex( void BRepGraph::EditorView::CommitMutation() noexcept { - NCollection_DynamicArray anIssues; - const bool isValid = ValidateMutationBoundary(&anIssues); - Standard_ASSERT_VOID(isValid, "CommitMutation: mutation boundary consistency check failed"); - (void)isValid; - (void)anIssues; + Standard_ASSERT_VOID(ValidateMutationBoundary(nullptr), + "CommitMutation: mutation boundary consistency check failed"); } //================================================================================================= bool BRepGraph::EditorView::ValidateMutationBoundary( - NCollection_DynamicArray* const theIssues) const + NCollection_LinearVector* const theIssues) const { bool isValid = true; const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!aStorage.ValidateReverseIndex()) + if (!aStorage.ValidateRelations()) { isValid = false; if (theIssues != nullptr) { BoundaryIssue anIssue; anIssue.NodeId = BRepGraph_NodeId(); - anIssue.Description = "Mutation boundary reverse index inconsistency"; + anIssue.Description = "Mutation boundary relation inconsistency"; theIssues->Append(anIssue); } } @@ -3526,43 +4853,13 @@ bool BRepGraph::EditorView::ValidateMutationBoundary( } } - // Validate built-in layer consistency: layers must not have bindings - // for entity kinds that have zero active entities in the graph. - const occ::handle aParamLayer = - myGraph->LayerRegistry().FindLayer(); - if (!aParamLayer.IsNull() && aParamLayer->HasBindings() && aStorage.NbVertices() == 0) - { - isValid = false; - if (theIssues != nullptr) - { - BoundaryIssue anIssue; - anIssue.NodeId = BRepGraph_NodeId(); - anIssue.Description = "ParamLayer has bindings but graph has no vertices"; - theIssues->Append(anIssue); - } - } - - const occ::handle aRegularityLayer = - myGraph->LayerRegistry().FindLayer(); - if (!aRegularityLayer.IsNull() && aRegularityLayer->HasBindings() && aStorage.NbEdges() == 0) - { - isValid = false; - if (theIssues != nullptr) - { - BoundaryIssue anIssue; - anIssue.NodeId = BRepGraph_NodeId(); - anIssue.Description = "RegularityLayer has bindings but graph has no edges"; - theIssues->Append(anIssue); - } - } - - // Check that every OccurrenceDef::ChildDefId carries a valid node kind + // Check that every OccurrenceDef::ChildNodeId carries a valid node kind // (Product or a topology kind). Positional self-id invariants are enforced // structurally by the typed id system and need no runtime check. for (BRepGraph_OccurrenceIterator anOccIt(*myGraph); anOccIt.More(); anOccIt.Next()) { const BRepGraphInc::OccurrenceDef& anOcc = anOccIt.Current(); - if (!anOcc.ChildDefId.IsValid() || isValidOccurrenceChildKind(anOcc.ChildDefId.NodeKind)) + if (!anOcc.ChildNodeId.IsValid() || isValidOccurrenceChildKind(anOcc.ChildNodeId.NodeKind)) { continue; } @@ -3574,7 +4871,7 @@ bool BRepGraph::EditorView::ValidateMutationBoundary( TCollection_AsciiString aDesc("Storage occurrence child kind invalid at occurrence idx="); aDesc += TCollection_AsciiString(static_cast(anOccId.Index)); aDesc += ": child kind="; - aDesc += TCollection_AsciiString(static_cast(anOcc.ChildDefId.NodeKind)); + aDesc += TCollection_AsciiString(static_cast(anOcc.ChildNodeId.NodeKind)); anIssue.NodeId = BRepGraph_NodeId(anOccId); anIssue.Description = std::move(aDesc); theIssues->Append(anIssue); @@ -3583,3 +4880,515 @@ bool BRepGraph::EditorView::ValidateMutationBoundary( return isValid; } + +//================================================================================================= + +void BRepGraph::EditorView::CompoundOps::ReplaceChild(const BRepGraph_ChildRefId theChildRef, + const BRepGraph_NodeId theNewChild) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + const BRepGraph_CompoundId aParentCompound = aStorage.ChildRef(theChildRef).ParentCompoundId; + myGraph->Editor().requireUnlocked(aParentCompound, + "BRepGraph::EditorView::ReplaceChild(): locked compound"); + myGraph->Editor().requireNoActiveGuard( + aParentCompound, + "BRepGraph::EditorView::ReplaceChild(): guard active on compound"); + + myGraph->Editor().Gen().SetChildRefChildNodeId(theChildRef, theNewChild); +} + +//================================================================================================= + +void BRepGraph::EditorView::CompSolidOps::ReplaceSolid(const BRepGraph_SolidRefId theSolidRef, + const BRepGraph_SolidId theNewSolid) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + const BRepGraph_CompSolidId aParentCompSolid = aStorage.SolidRef(theSolidRef).ParentCompSolidId; + myGraph->Editor().requireUnlocked(aParentCompSolid, + "BRepGraph::EditorView::ReplaceSolid(): locked compsolid"); + myGraph->Editor().requireNoActiveGuard( + aParentCompSolid, + "BRepGraph::EditorView::ReplaceSolid(): guard active on compsolid"); + + myGraph->Editor().Solids().SetRefChildSolidId(theSolidRef, theNewSolid); +} + +//================================================================================================= + +bool BRepGraph::EditorView::ShellOps::RemoveFaces( + const BRepGraph_ShellId theShellId, + const NCollection_Array1& theFaceRefs) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theShellId)) + { + return false; + } + + for (const BRepGraph_FaceRefId& aFaceRefId : theFaceRefs) + { + if (!aFaceRefId.IsValid(aStorage.NbFaceRefs())) + { + return false; + } + if (aStorage.IsRemoved(aFaceRefId)) + { + return false; + } + const NCollection_LinearVector& aShellRefIdsRO = + aStorage.ShellRelations(theShellId).FaceRefIds; + if (!containsOrderedRef(aShellRefIdsRO, aFaceRefId)) + { + return false; + } + } + + // Pass 2: Remove all + for (const BRepGraph_FaceRefId& aFaceRefId : theFaceRefs) + { + if (!RemoveFace(theShellId, aFaceRefId)) + { + return false; + } + } + + return true; +} + +//================================================================================================= + +bool BRepGraph::EditorView::SolidOps::RemoveShells( + const BRepGraph_SolidId theSolidId, + const NCollection_Array1& theShellRefs) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theSolidId)) + { + return false; + } + + for (const BRepGraph_ShellRefId& aShellRefId : theShellRefs) + { + if (!aShellRefId.IsValid(aStorage.NbShellRefs())) + { + return false; + } + if (aStorage.IsRemoved(aShellRefId)) + { + return false; + } + const NCollection_LinearVector& aSolidRefIdsRO = + aStorage.SolidRelations(theSolidId).ShellRefIds; + if (!containsOrderedRef(aSolidRefIdsRO, aShellRefId)) + { + return false; + } + } + + // Pass 2: Remove all + for (const BRepGraph_ShellRefId& aShellRefId : theShellRefs) + { + if (!RemoveShell(theSolidId, aShellRefId)) + { + return false; + } + } + + return true; +} + +//================================================================================================= + +bool BRepGraph::EditorView::CompoundOps::RemoveChildren( + const BRepGraph_CompoundId theCompoundId, + const NCollection_Array1& theChildRefs) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theCompoundId)) + { + return false; + } + + for (const BRepGraph_ChildRefId& aChildRefId : theChildRefs) + { + if (!aChildRefId.IsValid(aStorage.NbChildRefs())) + { + return false; + } + if (aStorage.IsRemoved(aChildRefId)) + { + return false; + } + const NCollection_LinearVector& aCompoundRefIdsRO = + aStorage.CompoundRelations(theCompoundId).ChildRefIds; + if (!containsOrderedRef(aCompoundRefIdsRO, aChildRefId)) + { + return false; + } + } + + // Pass 2: Remove all + for (const BRepGraph_ChildRefId& aChildRefId : theChildRefs) + { + if (!RemoveChild(theCompoundId, aChildRefId)) + { + return false; + } + } + + return true; +} + +//================================================================================================= + +bool BRepGraph::EditorView::CompSolidOps::RemoveSolids( + const BRepGraph_CompSolidId theCompSolidId, + const NCollection_Array1& theSolidRefs) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theCompSolidId)) + { + return false; + } + + for (const BRepGraph_SolidRefId& aSolidRefId : theSolidRefs) + { + if (!aSolidRefId.IsValid(aStorage.NbSolidRefs())) + { + return false; + } + if (aStorage.IsRemoved(aSolidRefId)) + { + return false; + } + const NCollection_LinearVector& aCompSolidRefIdsRO = + aStorage.CompSolidRelations(theCompSolidId).SolidRefIds; + if (!containsOrderedRef(aCompSolidRefIdsRO, aSolidRefId)) + { + return false; + } + } + + // Pass 2: Remove all + for (const BRepGraph_SolidRefId& aSolidRefId : theSolidRefs) + { + if (!RemoveSolid(theCompSolidId, aSolidRefId)) + { + return false; + } + } + + return true; +} + +//================================================================================================= + +bool BRepGraph::EditorView::ProductOps::RemoveOccurrences( + const BRepGraph_ProductId theProductId, + const NCollection_Array1& theOccurrenceRefs) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theProductId)) + { + return false; + } + + for (const BRepGraph_OccurrenceRefId& anOccRefId : theOccurrenceRefs) + { + if (!anOccRefId.IsValid(aStorage.NbOccurrenceRefs())) + { + return false; + } + if (aStorage.IsRemoved(anOccRefId)) + { + return false; + } + const NCollection_LinearVector& aProductRefIdsRO = + aStorage.ProductRelations(theProductId).OccurrenceRefIds; + if (!containsOrderedRef(aProductRefIdsRO, anOccRefId)) + { + return false; + } + } + + // Pass 2: Remove all + for (const BRepGraph_OccurrenceRefId& anOccRefId : theOccurrenceRefs) + { + if (!RemoveOccurrence(theProductId, anOccRefId)) + { + return false; + } + } + + return true; +} + +//================================================================================================= + +NCollection_Array1 BRepGraph::EditorView::ShellOps::Append( + const BRepGraph_ShellId theShellEntity, + const NCollection_Array1& theFaceIds, + const NCollection_Array1& theOrientations) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Validate orientations array size + if (!theOrientations.IsEmpty() && theOrientations.Size() != theFaceIds.Size()) + { + return NCollection_Array1(); + } + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theShellEntity)) + { + return NCollection_Array1(); + } + myGraph->Editor().requireUnlocked(theShellEntity, + "BRepGraph::EditorView::Append(): locked shell"); + myGraph->Editor().requireNoActiveGuard(theShellEntity, + "BRepGraph::EditorView::Append(): guard active on shell"); + + for (const BRepGraph_FaceId& aFaceId : theFaceIds) + { + if (!isActiveNode(aStorage, aFaceId)) + { + return NCollection_Array1(); + } + } + + // Pass 2: Link all + NCollection_Array1 aResult(theFaceIds.Size()); + for (size_t anIdx = 0; anIdx < theFaceIds.Size(); ++anIdx) + { + const TopAbs_Orientation anOri = + theOrientations.IsEmpty() ? TopAbs_FORWARD : TopAbs_Orientation(theOrientations.At(anIdx)); + + aResult.ChangeAt(anIdx) = + aStorage.AttachFaceToShell(theShellEntity, theFaceIds.At(anIdx), anOri); + myGraph->allocateRefUID(aResult.At(anIdx)); + myGraph->markRefModified(aResult.At(anIdx)); + } + + return aResult; +} + +//================================================================================================= + +NCollection_Array1 BRepGraph::EditorView::SolidOps::Append( + const BRepGraph_SolidId theSolidEntity, + const NCollection_Array1& theShellIds, + const NCollection_Array1& theOrientations) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Validate orientations array size + if (!theOrientations.IsEmpty() && theOrientations.Size() != theShellIds.Size()) + { + return NCollection_Array1(); + } + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theSolidEntity)) + { + return NCollection_Array1(); + } + myGraph->Editor().requireUnlocked(theSolidEntity, + "BRepGraph::EditorView::Append(): locked solid"); + myGraph->Editor().requireNoActiveGuard(theSolidEntity, + "BRepGraph::EditorView::Append(): guard active on solid"); + + for (const BRepGraph_ShellId& aShellId : theShellIds) + { + if (!isActiveNode(aStorage, aShellId)) + { + return NCollection_Array1(); + } + } + + // Pass 2: Link all + NCollection_Array1 aResult(theShellIds.Size()); + for (size_t anIdx = 0; anIdx < theShellIds.Size(); ++anIdx) + { + const TopAbs_Orientation anOri = + theOrientations.IsEmpty() ? TopAbs_FORWARD : TopAbs_Orientation(theOrientations.At(anIdx)); + + aResult.ChangeAt(anIdx) = + aStorage.AttachShellToSolid(theSolidEntity, theShellIds.At(anIdx), anOri); + myGraph->allocateRefUID(aResult.At(anIdx)); + myGraph->markRefModified(aResult.At(anIdx)); + } + + return aResult; +} + +//================================================================================================= + +NCollection_Array1 BRepGraph::EditorView::CompoundOps::Append( + const BRepGraph_CompoundId theCompoundEntity, + const NCollection_Array1& theChildIds, + const NCollection_Array1& theOrientations) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Validate orientations array size + if (!theOrientations.IsEmpty() && theOrientations.Size() != theChildIds.Size()) + { + return NCollection_Array1(); + } + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theCompoundEntity)) + { + return NCollection_Array1(); + } + myGraph->Editor().requireUnlocked(theCompoundEntity, + "BRepGraph::EditorView::Append(): locked compound"); + myGraph->Editor().requireNoActiveGuard( + theCompoundEntity, + "BRepGraph::EditorView::Append(): guard active on compound"); + + for (const BRepGraph_NodeId& aChildId : theChildIds) + { + if (!isActiveTopologyNode(aStorage, aChildId)) + { + return NCollection_Array1(); + } + } + + // Pass 2: Link all + NCollection_Array1 aResult(theChildIds.Size()); + for (size_t anIdx = 0; anIdx < theChildIds.Size(); ++anIdx) + { + const TopAbs_Orientation anOri = + theOrientations.IsEmpty() ? TopAbs_FORWARD : TopAbs_Orientation(theOrientations.At(anIdx)); + + aResult.ChangeAt(anIdx) = aStorage.AttachChildToCompound(theCompoundEntity, + theChildIds.At(anIdx), + TopLoc_Location(), + anOri); + myGraph->allocateRefUID(aResult.At(anIdx)); + myGraph->markRefModified(aResult.At(anIdx)); + } + + return aResult; +} + +//================================================================================================= + +NCollection_Array1 BRepGraph::EditorView::CompSolidOps::Append( + const BRepGraph_CompSolidId theCompSolidEntity, + const NCollection_Array1& theSolidIds, + const NCollection_Array1& theOrientations) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Validate orientations array size + if (!theOrientations.IsEmpty() && theOrientations.Size() != theSolidIds.Size()) + { + return NCollection_Array1(); + } + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theCompSolidEntity)) + { + return NCollection_Array1(); + } + myGraph->Editor().requireUnlocked(theCompSolidEntity, + "BRepGraph::EditorView::Append(): locked compsolid"); + myGraph->Editor().requireNoActiveGuard( + theCompSolidEntity, + "BRepGraph::EditorView::Append(): guard active on compsolid"); + + for (const BRepGraph_SolidId& aSolidId : theSolidIds) + { + if (!isActiveNode(aStorage, aSolidId)) + { + return NCollection_Array1(); + } + } + + // Pass 2: Link all + NCollection_Array1 aResult(theSolidIds.Size()); + for (size_t anIdx = 0; anIdx < theSolidIds.Size(); ++anIdx) + { + const TopAbs_Orientation anOri = + theOrientations.IsEmpty() ? TopAbs_FORWARD : TopAbs_Orientation(theOrientations.At(anIdx)); + + aResult.ChangeAt(anIdx) = + aStorage.AttachSolidToCompSolid(theCompSolidEntity, theSolidIds.At(anIdx), anOri); + myGraph->allocateRefUID(aResult.At(anIdx)); + myGraph->markRefModified(aResult.At(anIdx)); + } + + return aResult; +} + +//================================================================================================= + +NCollection_Array1 BRepGraph::EditorView::ProductOps::Append( + const BRepGraph_ProductId theParentProduct, + const NCollection_Array1& theChildProducts, + const NCollection_Array1& thePlacements) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + // Validate placements array size + if (thePlacements.Size() != theChildProducts.Size()) + { + return NCollection_Array1(); + } + + // Pass 1: Validate all inputs + if (!isActiveNode(aStorage, theParentProduct)) + { + return NCollection_Array1(); + } + myGraph->Editor().requireUnlocked(theParentProduct, + "BRepGraph::EditorView::Append(): locked product"); + myGraph->Editor().requireNoActiveGuard( + theParentProduct, + "BRepGraph::EditorView::Append(): guard active on product"); + + for (const BRepGraph_ProductId& aChildProduct : theChildProducts) + { + if (!isActiveNode(aStorage, aChildProduct)) + { + return NCollection_Array1(); + } + if (aChildProduct == theParentProduct) + { + return NCollection_Array1(); + } + } + + // Pass 2: Link all + NCollection_Array1 aResult(theChildProducts.Size()); + for (size_t anIdx = 0; anIdx < theChildProducts.Size(); ++anIdx) + { + BRepGraph_OccurrenceRefId anOccRefId; + BRepGraph_OccurrenceId anOccId = Append(theParentProduct, + theChildProducts.At(anIdx), + thePlacements.At(anIdx), + BRepGraph_OccurrenceId(), + &anOccRefId); + if (!anOccId.IsValid()) + { + return NCollection_Array1(); + } + aResult.ChangeAt(anIdx) = anOccRefId; + } + + return aResult; +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.hxx index 66787020bd..7846978608 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView.hxx @@ -15,11 +15,16 @@ #define _BRepGraph_EditorView_HeaderFile #include +#include #include +#include #include +#include #include #include #include +#include +#include #include #include #include @@ -45,8 +50,9 @@ class Poly_PolygonOnTriangulation; //! faces, shells, solids, compounds) and assembly nodes (products, occurrences) //! without an existing TopoDS_Shape. //! - Field-level RAII-scoped mutation via Mut*() guards (Edges().Mut, Faces().Mut, -//! Products().Mut, Occurrences().Mut, Reps().MutSurface, etc.) with automatic cache invalidation -//! and upward SubtreeGen propagation on guard destruction. +//! Products().Mut, Occurrences().Mut, Edges().Mut, Faces().Mut, +//! etc.) with automatic cache invalidation and upward SubtreeGen propagation on +//! guard destruction. //! - Incremental shape appending, soft-deletion of nodes, and deferred invalidation //! mode for batched structural edit loops under external serialization. //! Obtained via BRepGraph::Editor(). @@ -54,7 +60,8 @@ class Poly_PolygonOnTriangulation; //! Each Ops class is accessed via a non-const reference accessor: //! theGraph.Editor().Vertices().Add(...) //! theGraph.Editor().Edges().Add(...) -//! theGraph.Editor().CoEdges().SetPCurve(...) +//! theGraph.Editor().CoEdges().Add(edge, face, curve2d, first, last, ori) +//! theGraph.Editor().Products().Add(shapeRoot, placement) //! theGraph.Editor().Gen().RemoveNode(...) //! //! Contract notes: @@ -71,99 +78,6 @@ class Poly_PolygonOnTriangulation; class BRepGraph::EditorView { public: - //! Representation mutation guards (Surface, Curve3D, Curve2D, Triangulation, - //! Polygon3D, Polygon2D, PolygonOnTri). All `Mut*()` accessors raise - //! `Standard_ProgramError` for null, out-of-range, or removed typed ids. - //! Access via `BRepGraph::EditorView::Reps()`. - class RepOps - { - public: - //! Return scoped mutable surface representation guard. - [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutSurface( - const BRepGraph_SurfaceRepId theSurface); - //! Return scoped mutable 3D curve representation guard. - [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutCurve3D( - const BRepGraph_Curve3DRepId theCurve); - //! Return scoped mutable 2D curve representation guard. - [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutCurve2D( - const BRepGraph_Curve2DRepId theCurve); - //! Return scoped mutable triangulation representation guard. - [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard - MutTriangulation(const BRepGraph_TriangulationRepId theTriangulation); - //! Return scoped mutable 3D polygon representation guard. - [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutPolygon3D( - const BRepGraph_Polygon3DRepId thePolygon); - //! Return scoped mutable 2D polygon representation guard. - [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutPolygon2D( - const BRepGraph_Polygon2DRepId thePolygon); - //! Return scoped mutable polygon-on-triangulation representation guard. - [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutPolygonOnTri( - const BRepGraph_PolygonOnTriRepId thePolygon); - - //! Set the surface handle on a SurfaceRep. - Standard_EXPORT void SetSurface(const BRepGraph_SurfaceRepId theRep, - const occ::handle& theSurface); - Standard_EXPORT void SetSurface(BRepGraph_MutGuard& theMut, - const occ::handle& theSurface); - - //! Set the 3D curve handle on a Curve3DRep. - Standard_EXPORT void SetCurve3D(const BRepGraph_Curve3DRepId theRep, - const occ::handle& theCurve); - Standard_EXPORT void SetCurve3D(BRepGraph_MutGuard& theMut, - const occ::handle& theCurve); - - //! Set the 2D curve handle on a Curve2DRep. - Standard_EXPORT void SetCurve2D(const BRepGraph_Curve2DRepId theRep, - const occ::handle& theCurve); - Standard_EXPORT void SetCurve2D(BRepGraph_MutGuard& theMut, - const occ::handle& theCurve); - - //! Set the triangulation handle on a TriangulationRep. - Standard_EXPORT void SetTriangulation(const BRepGraph_TriangulationRepId theRep, - const occ::handle& theTri); - Standard_EXPORT void SetTriangulation( - BRepGraph_MutGuard& theMut, - const occ::handle& theTri); - - //! Set the polygon handle on a Polygon3DRep. - Standard_EXPORT void SetPolygon3D(const BRepGraph_Polygon3DRepId theRep, - const occ::handle& thePolygon); - Standard_EXPORT void SetPolygon3D(BRepGraph_MutGuard& theMut, - const occ::handle& thePolygon); - - //! Set the polygon handle on a Polygon2DRep. - Standard_EXPORT void SetPolygon2D(const BRepGraph_Polygon2DRepId theRep, - const occ::handle& thePolygon); - Standard_EXPORT void SetPolygon2D(BRepGraph_MutGuard& theMut, - const occ::handle& thePolygon); - - //! Set the polygon-on-triangulation handle on a PolygonOnTriRep. - Standard_EXPORT void SetPolygonOnTri( - const BRepGraph_PolygonOnTriRepId theRep, - const occ::handle& thePolygon); - Standard_EXPORT void SetPolygonOnTri( - BRepGraph_MutGuard& theMut, - const occ::handle& thePolygon); - - //! Set the triangulation rep id linked to a PolygonOnTriRep. - Standard_EXPORT void SetPolygonOnTriTriangulationId( - const BRepGraph_PolygonOnTriRepId theRep, - const BRepGraph_TriangulationRepId theTriRep); - Standard_EXPORT void SetPolygonOnTriTriangulationId( - BRepGraph_MutGuard& theMut, - const BRepGraph_TriangulationRepId theTriRep); - - private: - friend class EditorView; - - explicit RepOps(BRepGraph* theGraph) - : myGraph(theGraph) - { - } - - BRepGraph* myGraph; - }; - //! @brief Vertex creation operations. class VertexOps { @@ -204,31 +118,19 @@ public: const double theTolerance); //! Set the orientation of a vertex reference. - Standard_EXPORT void SetRefOrientation(const BRepGraph_VertexRefId theVertexRef, - const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetRefOrientation(const BRepGraph_VertexRefId theVertexRef, + const BRepGraphInc::ParityOrientation theOrientation); //! Set the orientation inside a batched mutation scope. Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation); - - //! Set the local location of a vertex reference and fire immediate notification. - //! @param[in] theVertexRef typed vertex reference identifier - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(const BRepGraph_VertexRefId theVertexRef, - const TopLoc_Location& theLoc); - - //! Set the local location of a vertex reference inside a batched mutation scope. - //! @param[in] theMut active mutable vertex reference guard - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc); + const BRepGraphInc::ParityOrientation theOrientation); //! Rewire a vertex reference to a different vertex def (rebinds VertexToEdges if parent is //! Edge). - Standard_EXPORT void SetRefVertexDefId(const BRepGraph_VertexRefId theVertexRef, - const BRepGraph_VertexId theVertex); - Standard_EXPORT void SetRefVertexDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_VertexId theVertex); + Standard_EXPORT void SetRefChildVertexId(const BRepGraph_VertexRefId theVertexRef, + const BRepGraph_VertexId theVertex); + Standard_EXPORT void SetRefChildVertexId(BRepGraph_MutGuard& theMut, + const BRepGraph_VertexId theVertex); private: friend class EditorView; @@ -261,18 +163,6 @@ public: const double theLast, const double theTolerance); - //! Add an internal or external direct vertex usage to an edge definition. - //! The vertex is stored in EdgeDef.InternalVertexRefIds; boundary start/end - //! vertices remain owned by StartVertexRefId and EndVertexRefId. - //! @param[in] theEdgeEntity typed edge definition identifier - //! @param[in] theVertexEntity typed vertex definition identifier - //! @param[in] theOri orientation of the direct vertex usage on the edge - //! @return typed vertex reference identifier, or invalid if inputs are not active - [[nodiscard]] Standard_EXPORT BRepGraph_VertexRefId - AddInternalVertex(const BRepGraph_EdgeId theEdgeEntity, - const BRepGraph_VertexId theVertexEntity, - const TopAbs_Orientation theOri = TopAbs_INTERNAL); - //! Split a single edge definition at a vertex and 3D-curve parameter. //! Creates two new EdgeDef slots, splits all PCurve nodes at the corresponding //! 2D parameter, and updates every wire that contained the original edge. @@ -287,53 +177,40 @@ public: BRepGraph_EdgeId& theSubA, BRepGraph_EdgeId& theSubB); - //! Detach one exact direct vertex ref from an edge definition. - //! Supports both boundary fixed slots (StartVertexRefId / EndVertexRefId) and - //! entries stored in EdgeDef.InternalVertexRefIds. - //! @param[in] theEdgeDefId edge definition identifier + //! Detach one exact edge-owned vertex ref from an edge definition. + //! Supports only the persisted boundary slots (StartVertexRefId / + //! EndVertexRefId). Supplemental direct-vertex usages are stored in + //! BRepGraph_LayerTopoSupplement and are not removed through this API. + //! @param[in] theChildEdgeId edge definition identifier //! @param[in] theVertexRefId exact edge-owned vertex reference identifier //! @return true if the active edge-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveVertex(const BRepGraph_EdgeId theEdgeDefId, + [[nodiscard]] Standard_EXPORT bool RemoveVertex(const BRepGraph_EdgeId theChildEdgeId, const BRepGraph_VertexRefId theVertexRefId); //! Remap one edge-owned vertex reference to point at a different vertex //! definition, preserving the existing orientation and local location. //! Intended for boundary-vertex substitution without a full edge rebuild //! (e.g. stitching shared endpoints after a ShapeFix pass). - //! @param[in] theEdgeDefId edge owning the vertex reference - //! @param[in] theOldVertexRefId exact vertex reference to remap (boundary or internal) - //! @param[in] theNewVertexDefId replacement vertex definition + //! @param[in] theChildEdgeId edge owning the vertex reference + //! @param[in] theOldVertexRefId exact boundary vertex reference to remap + //! @param[in] theNewChildVertexId replacement vertex definition //! @return typed id of the newly created vertex reference, or invalid if //! any input was inactive or the old ref did not belong to this edge [[nodiscard]] Standard_EXPORT BRepGraph_VertexRefId - ReplaceVertex(const BRepGraph_EdgeId theEdgeDefId, + ReplaceVertex(const BRepGraph_EdgeId theChildEdgeId, const BRepGraph_VertexRefId theOldVertexRefId, - const BRepGraph_VertexId theNewVertexDefId); + const BRepGraph_VertexId theNewChildVertexId); //! Return scoped mutable edge definition guard. [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard Mut( const BRepGraph_EdgeId theEdge); - //! Returns true iff the edge appears as a seam on the given face (two CoEdges - //! of theEdge share theFace). - //! @param[in] theEdge typed edge definition identifier - //! @param[in] theFace typed face definition identifier - //! @return true if the edge is a seam on the given face - [[nodiscard]] Standard_EXPORT bool IsSeamOnFace(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace) const; - - //! Set the geometric regularity (C^k) for an edge across a pair of faces in - //! BRepGraph_LayerRegularity. theFace1 == theFace2 sets the seam continuity - //! across the closed-surface seam line. Requires the layer to be registered. - //! @param[in] theEdge typed edge definition identifier - //! @param[in] theFace1 first face (or seam face when theFace2 == theFace1) - //! @param[in] theFace2 second face - //! @param[in] theContinuity continuity (GeomAbs_Shape) - //! @return true if written; false if the layer is not registered - Standard_EXPORT bool SetRegularity(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2, - const GeomAbs_Shape theContinuity); + //! Reverse the edge: swap StartVertexRefId and EndVertexRefId. Used by + //! healing/sewing when a caller wants the edge's boundary order flipped. + //! Does not alter the parametric range (callers needing reparametrization + //! should follow up with SetParamRange). + //! @param[in] theEdge edge definition identifier + Standard_EXPORT void Reverse(const BRepGraph_EdgeId theEdge); //! Set the tolerance of an edge definition and fire immediate notification. //! @param[in] theEdge typed edge definition identifier @@ -354,46 +231,38 @@ public: const double theFirst, const double theLast); - //! Set the SameParameter flag of an edge definition. - Standard_EXPORT void SetSameParameter(const BRepGraph_EdgeId theEdge, - const bool theSameParameter); - Standard_EXPORT void SetSameParameter(BRepGraph_MutGuard& theMut, - const bool theSameParameter); + //! Set the 3D curve on an edge. Creates an owned EdgeCurve3DRep record + //! and an associated Curve3DRep for edge geometry access. + //! @param[in] theEdge edge definition identifier + //! @param[in] theCurve 3D curve geometry (must not be null) + //! @param[in] theFirst first curve parameter + //! @param[in] theLast last curve parameter + Standard_EXPORT void SetCurve(const BRepGraph_EdgeId theEdge, + const occ::handle& theCurve, + const double theFirst, + const double theLast); - //! Set the SameRange flag of an edge definition. - Standard_EXPORT void SetSameRange(const BRepGraph_EdgeId theEdge, const bool theSameRange); - Standard_EXPORT void SetSameRange(BRepGraph_MutGuard& theMut, - const bool theSameRange); + //! Clear the 3D curve on an edge. Removes the owned use record binding. + //! @param[in] theEdge edge definition identifier + Standard_EXPORT void ClearCurve(const BRepGraph_EdgeId theEdge); - //! Set the IsDegenerate flag of an edge definition. - Standard_EXPORT void SetDegenerate(const BRepGraph_EdgeId theEdge, const bool theIsDegenerate); - Standard_EXPORT void SetDegenerate(BRepGraph_MutGuard& theMut, - const bool theIsDegenerate); + //! Set the persistent 3D polygon on an edge. Creates an owned EdgePolygon3DRep record. + //! @param[in] theEdge edge definition identifier + //! @param[in] thePolygon 3D polygon (must not be null) + Standard_EXPORT void SetPersistentPolygon3D(const BRepGraph_EdgeId theEdge, + const occ::handle& thePolygon); - //! Set the Curve3DRep id bound to an edge (invalid id clears the binding). - Standard_EXPORT void SetCurve3DRepId(const BRepGraph_EdgeId theEdge, - const BRepGraph_Curve3DRepId theRep); - Standard_EXPORT void SetCurve3DRepId(BRepGraph_MutGuard& theMut, - const BRepGraph_Curve3DRepId theRep); + //! Clear the persistent 3D polygon on an edge. + //! @param[in] theEdge edge definition identifier + Standard_EXPORT void ClearPersistentPolygon3D(const BRepGraph_EdgeId theEdge); - //! Set the Polygon3DRep id bound to an edge (invalid id clears the binding). - Standard_EXPORT void SetPolygon3DRepId(const BRepGraph_EdgeId theEdge, - const BRepGraph_Polygon3DRepId theRep); - Standard_EXPORT void SetPolygon3DRepId(BRepGraph_MutGuard& theMut, - const BRepGraph_Polygon3DRepId theRep); - - //! Set the IsClosed flag (StartVertex == EndVertex topology) of an edge. - Standard_EXPORT void SetIsClosed(const BRepGraph_EdgeId theEdge, const bool theIsClosed); - Standard_EXPORT void SetIsClosed(BRepGraph_MutGuard& theMut, - const bool theIsClosed); - - //! Set the start vertex-ref id (rebinds VertexToEdges). Caller maintains reverse indices. + //! Set the start vertex-ref id and rebind the vertex-to-edge relation. Standard_EXPORT void SetStartVertexRefId(const BRepGraph_EdgeId theEdge, const BRepGraph_VertexRefId theVertexRef); Standard_EXPORT void SetStartVertexRefId(BRepGraph_MutGuard& theMut, const BRepGraph_VertexRefId theVertexRef); - //! Set the end vertex-ref id (rebinds VertexToEdges). Caller maintains reverse indices. + //! Set the end vertex-ref id and rebind the vertex-to-edge relation. Standard_EXPORT void SetEndVertexRefId(const BRepGraph_EdgeId theEdge, const BRepGraph_VertexRefId theVertexRef); Standard_EXPORT void SetEndVertexRefId(BRepGraph_MutGuard& theMut, @@ -414,16 +283,6 @@ public: class CoEdgeOps { public: - //! Create a new Curve2DRep in storage and return its typed identifier. - //! Use this when assigning a new PCurve to an existing CoEdge entity - //! via Editor().MutCoEdge() inside a larger mutation sequence. - //! For one-shot creation and binding of a face-context PCurve, use - //! AddPCurve(). - //! @param[in] theCurve2d the 2D parametric curve handle - //! @return typed Curve2DRep identifier, or invalid if the curve is null - [[nodiscard]] Standard_EXPORT BRepGraph_Curve2DRepId - CreateCurve2DRep(const occ::handle& theCurve2d); - //! Assign or clear the PCurve bound to an existing coedge. //! Creates a new Curve2DRep for non-null curves and stores its id on the coedge. //! Pass a null handle to clear the stored PCurve binding. @@ -432,35 +291,41 @@ public: Standard_EXPORT void SetPCurve(const BRepGraph_CoEdgeId theCoEdge, const occ::handle& theCurve2d); - //! Attach a PCurve to an edge for a given face context. - //! Creates a new CoEdge entity with Curve2DRep and updates reverse indices. + //! Create a new CoEdge entity linking an edge with an orientation. + //! The CoEdge is free-floating (no parent wire); bind it to a wire + //! via WireOps::Add(). + //! @param[in] theEdge typed edge definition identifier + //! @param[in] theOrientation orientation of the edge in the wire + //! @return typed coedge identifier, or invalid if the edge is invalid + [[nodiscard]] Standard_EXPORT BRepGraph_CoEdgeId + Add(const BRepGraph_EdgeId theEdge, const BRepGraphInc::ParityOrientation theOrientation); + + //! Create a new CoEdge entity with a PCurve for a given edge-face pair. + //! Creates a new CoEdge entity with Curve2DRep and updates relation tables. //! This always appends a new CoEdge entry for the edge-face pair; callers //! should avoid duplicate creation unless multiple bindings are intentional //! for the modeled topology. - //! Prefer this route when the caller needs to add a face-context PCurve in - //! one operation. For editing an already identified CoEdge inside a larger - //! mutation sequence, use CreateCurve2DRep() with Editor().MutCoEdge(). + //! For editing an already identified CoEdge inside a larger + //! mutation sequence, use CoEdges().SetPCurve(). //! @param[in] theEdgeEntity typed edge definition identifier //! @param[in] theFaceEntity typed face definition identifier //! @param[in] theCurve2d 2D curve geometry //! @param[in] theFirst first curve parameter //! @param[in] theLast last curve parameter //! @param[in] theEdgeOrientation edge orientation on the face - Standard_EXPORT void AddPCurve(const BRepGraph_EdgeId theEdgeEntity, - const BRepGraph_FaceId theFaceEntity, - const occ::handle& theCurve2d, - const double theFirst, - const double theLast, - const TopAbs_Orientation theEdgeOrientation = TopAbs_FORWARD); + //! @return typed coedge identifier, or invalid if inputs are not active + [[nodiscard]] Standard_EXPORT BRepGraph_CoEdgeId + Add(const BRepGraph_EdgeId theEdgeEntity, + const BRepGraph_FaceId theFaceEntity, + const occ::handle& theCurve2d, + const double theFirst, + const double theLast, + const BRepGraphInc::ParityOrientation theEdgeOrientation = TopAbs_FORWARD); //! Return scoped mutable coedge definition guard. [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard Mut( const BRepGraph_CoEdgeId theCoEdge); - //! Return scoped mutable coedge reference guard. - [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( - const BRepGraph_CoEdgeRefId theCoEdgeRef); - //! Set the parametric range of a coedge definition and fire immediate notification. //! @param[in] theCoEdge typed coedge definition identifier //! @param[in] theFirst new first parameter value @@ -477,83 +342,61 @@ public: double theFirst, double theLast); - //! Set the local location of a coedge reference and fire immediate notification. - //! @param[in] theCoEdgeRef typed coedge reference identifier - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(const BRepGraph_CoEdgeRefId theCoEdgeRef, - const TopLoc_Location& theLoc); - - //! Set the local location of a coedge reference inside a batched mutation scope. - //! @param[in] theMut active mutable coedge reference guard - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc); - - //! Rewire a coedge reference to a different coedge def (rebinds CoEdgeToWires + EdgeToWires). - Standard_EXPORT void SetRefCoEdgeDefId(const BRepGraph_CoEdgeRefId theCoEdgeRef, - const BRepGraph_CoEdgeId theCoEdge); - Standard_EXPORT void SetRefCoEdgeDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_CoEdgeId theCoEdge); - //! Set the orientation of a coedge definition. - Standard_EXPORT void SetOrientation(const BRepGraph_CoEdgeId theCoEdge, - const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetOrientation(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraphInc::ParityOrientation theOrientation); Standard_EXPORT void SetOrientation(BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation); + const BRepGraphInc::ParityOrientation theOrientation); - //! Set the UV box (UV1 = at ParamFirst, UV2 = at ParamLast) of a coedge definition. - Standard_EXPORT void SetUVBox(const BRepGraph_CoEdgeId theCoEdge, - const gp_Pnt2d& theUV1, - const gp_Pnt2d& theUV2); - Standard_EXPORT void SetUVBox(BRepGraph_MutGuard& theMut, - const gp_Pnt2d& theUV1, - const gp_Pnt2d& theUV2); + //! Set the PCurve on a coedge. Creates an owned CoEdgeCurve2DRep record. + //! @param[in] theCoEdge coedge definition identifier + //! @param[in] theCurve2d 2D curve geometry (must not be null) + //! @param[in] theFirst first curve parameter + //! @param[in] theLast last curve parameter + Standard_EXPORT void SetPCurve(const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& theCurve2d, + const double theFirst, + const double theLast); - //! Continuity is a property of (Edge, Face1, Face2) and lives in - //! BRepGraph_LayerRegularity. Use EditorView::EdgeOps::SetRegularity to write, - //! BRepGraph_Tool::Edge::Continuity to read. + //! Clear the PCurve on a coedge. + //! @param[in] theCoEdge coedge definition identifier + Standard_EXPORT void ClearPCurve(const BRepGraph_CoEdgeId theCoEdge); - //! Set the Curve2DRep id bound to a coedge (invalid id clears the binding). - Standard_EXPORT void SetCurve2DRepId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_Curve2DRepId theRep); - Standard_EXPORT void SetCurve2DRepId(BRepGraph_MutGuard& theMut, - const BRepGraph_Curve2DRepId theRep); + //! Set the persistent 2D polygon on a coedge. + //! @param[in] theCoEdge coedge definition identifier + //! @param[in] thePolygon 2D polygon (must not be null) + Standard_EXPORT void SetPersistentPolygon2D(const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& thePolygon); - //! Set the Polygon2DRep id bound to a coedge. - Standard_EXPORT void SetPolygon2DRepId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_Polygon2DRepId theRep); - Standard_EXPORT void SetPolygon2DRepId(BRepGraph_MutGuard& theMut, - const BRepGraph_Polygon2DRepId theRep); + //! Set the persistent polygon-on-triangulation on a coedge. + //! The triangulation is resolved via CoEdgeDef.FaceId -> FaceDef.TriangulationRepId. + //! @param[in] theCoEdge coedge definition identifier + //! @param[in] thePolygon polygon-on-triangulation (must not be null) + Standard_EXPORT void SetPersistentPolygonOnTri( + const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& thePolygon); - //! Set the PolygonOnTriRep id bound to a coedge. - Standard_EXPORT void SetPolygonOnTriRepId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_PolygonOnTriRepId theRep); - Standard_EXPORT void SetPolygonOnTriRepId(BRepGraph_MutGuard& theMut, - const BRepGraph_PolygonOnTriRepId theRep); - - //! Drop face-bound parametric payload (PCurve, param range, continuity, UVs) + //! Drop face-bound parametric representation (PCurve, param range, continuity, UVs) //! while keeping structural links - used when the owning face is removed. - Standard_EXPORT void ClearPCurveBinding(const BRepGraph_CoEdgeId theCoEdge); - Standard_EXPORT void ClearPCurveBinding(BRepGraph_MutGuard& theMut); + Standard_EXPORT void ResetPCurveBinding(const BRepGraph_CoEdgeId theCoEdge); + Standard_EXPORT void ResetPCurveBinding(BRepGraph_MutGuard& theMut); //! Set the seam-pair id linking two coedges of a seam edge (invalid breaks the link). // To establish seam-ness, ensure two CoEdges exist on the same (Edge, Face) // with opposite orientations; the seam relation is then queryable via // BRepGraph_Tool::CoEdge::SeamPair. - //! Rewire a coedge to a different parent edge (rebinds EdgeToCoEdges, EdgeToWires, - //! EdgeToFaces). Caller maintains reverse indices. - Standard_EXPORT void SetEdgeDefId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_EdgeId theEdge); - Standard_EXPORT void SetEdgeDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_EdgeId theEdge); + //! Rewire a coedge to a different child edge and rebind edge parent/use relations. + Standard_EXPORT void SetChildEdgeId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_EdgeId theEdge); + Standard_EXPORT void SetChildEdgeId(BRepGraph_MutGuard& theMut, + const BRepGraph_EdgeId theEdge); - //! Rewire a coedge to a different owning face (rebinds EdgeToFaces). Caller maintains reverse - //! indices. - Standard_EXPORT void SetFaceDefId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_FaceId theFace); - Standard_EXPORT void SetFaceDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_FaceId theFace); + //! Rewire a coedge to a different owning face and rebind edge-to-face relations. + Standard_EXPORT void SetFaceId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_FaceId theFace); + Standard_EXPORT void SetFaceId(BRepGraph_MutGuard& theMut, + const BRepGraph_FaceId theFace); private: friend class EditorView; @@ -570,36 +413,113 @@ public: class WireOps { public: - //! Add a wire definition to the graph. - //! Each pair is (EdgeDefId, OrientationInWire). - //! @param[in] theEdges ordered edge entries + //! Status returned by wire coedge-order prechecks. + enum class CoEdgeOrderStatus + { + Ready, //!< Input is valid and already connected in given order. + Reordered, //!< Input is valid after internal canonical reordering. + AlreadyCurrent, //!< Input equals current stored order; mutation can be skipped. + AlreadyContained, //!< Append precheck found the coedge already in the wire. + Empty, //!< Input contains no coedges. + InvalidWire, //!< Wire id is invalid or removed. + SizeMismatch, //!< Input size differs from current wire coedge count. + DuplicateCoEdge, //!< Input repeats a coedge id. + InvalidCoEdge, //!< Input references an invalid, removed, or incomplete coedge. + CoEdgeAlreadyBound, //!< Add precheck found a coedge already owned by a wire. + CoEdgeNotOwnedByWire, //!< Set-order precheck found a coedge not owned by the wire. + NotPermutation, //!< Set-order input is not a permutation of current coedges. + Disconnected //!< Coedges cannot form a connected chain. + }; + + //! Status returned by wire edge-replacement prechecks. + enum class ReplaceEdgeStatus + { + Ready, //!< Replacement is valid and preserves wire connectivity. + AlreadyCurrent, //!< Old and new edge are the same and no mutation is needed. + InvalidWire, //!< Wire id is invalid or removed. + InvalidOldEdge, //!< Old edge id is invalid, removed, or not used by the wire. + InvalidNewEdge, //!< New edge id is invalid or removed. + Disconnected //!< Replacement would break the ordered coedge chain. + }; + + //! Precheck free-floating CoEdges for WireOps::Add(). + //! @param[in] theCoEdgeIds candidate coedge identifiers + //! @return status describing whether the input can form a wire + [[nodiscard]] Standard_EXPORT CoEdgeOrderStatus + CheckCoEdgeOrder(const NCollection_Array1& theCoEdgeIds) const; + + //! Precheck owned CoEdges for WireOps::SetCoEdgeOrder(). + //! @param[in] theWire wire definition identifier + //! @param[in] theCoEdgeIds candidate coedge identifiers + //! @return status describing whether the input can replace the stored order + [[nodiscard]] Standard_EXPORT CoEdgeOrderStatus + CheckCoEdgeOrder(const BRepGraph_WireId theWire, + const NCollection_Array1& theCoEdgeIds) const; + + //! Precheck appending a free CoEdge to an existing wire. + //! @param[in] theWire wire definition identifier + //! @param[in] theCoEdgeId free coedge candidate + //! @return status describing whether the append can preserve connected order + [[nodiscard]] Standard_EXPORT CoEdgeOrderStatus + CheckAppendCoEdge(const BRepGraph_WireId theWire, const BRepGraph_CoEdgeId theCoEdgeId) const; + + //! Precheck replacing one edge by another in an existing wire. + //! @param[in] theWire wire definition identifier + //! @param[in] theOldEdge edge currently used by one or more wire coedges + //! @param[in] theNewEdge replacement edge + //! @param[in] theReversed if true, replacement coedge orientation is reversed + //! @return status describing whether replacement preserves connected order + [[nodiscard]] Standard_EXPORT ReplaceEdgeStatus + CheckReplaceEdge(const BRepGraph_WireId theWire, + const BRepGraph_EdgeId theOldEdge, + const BRepGraph_EdgeId theNewEdge, + const bool theReversed) const; + + //! Add a wire definition from pre-created CoEdges. + //! Each CoEdge must be free-floating (no parent wire yet). + //! The method binds all CoEdges to the new wire and updates relation tables. + //! @param[in] theCoEdgeIds ordered coedge identifiers //! @return typed wire definition identifier, or invalid if any referenced - //! edge entry is invalid - [[nodiscard]] Standard_EXPORT BRepGraph_WireId Add( - const NCollection_DynamicArray>& theEdges); + //! coedge is invalid or already bound to a wire + [[nodiscard]] Standard_EXPORT BRepGraph_WireId + Add(const NCollection_Array1& theCoEdgeIds); //! Replace one edge with another in a wire definition. //! Updates the CoEdge's EdgeIdx to point to the new edge, adjusts orientation - //! if theReversed, and incrementally updates reverse indices. - //! @param[in] theWireDefId wire definition identifier + //! if theReversed, and incrementally updates relation tables. + //! @param[in] theChildWireId wire definition identifier //! @param[in] theOldEdgeEntity edge to replace //! @param[in] theNewEdgeEntity replacement edge //! @param[in] theReversed if true, reverse the orientation of the replacement - Standard_EXPORT void ReplaceEdge(const BRepGraph_WireId theWireDefId, + Standard_EXPORT void ReplaceEdge(const BRepGraph_WireId theChildWireId, const BRepGraph_EdgeId theOldEdgeEntity, const BRepGraph_EdgeId theNewEdgeEntity, const bool theReversed); - //! Detach one exact coedge ref from a wire definition. - //! Use BRepGraph_RefsCoEdgeOfWire::CurrentId() when removing from a wire - //! iterator. The method removes the exact CoEdgeRef entry, erases it from - //! the wire's ordered ref sequence, updates reverse indices, and prunes the - //! CoEdge node when it has no other active usages. - //! @param[in] theWireDefId wire definition identifier - //! @param[in] theCoEdgeRefId exact wire-owned coedge reference identifier + //! Detach one exact coedge entry from a wire definition. + //! Use BRepGraph_CoEdgesOfWire::CurrentId() when removing from a wire + //! iterator. The method removes the exact ordered coedge entry, updates + //! relation tables, and prunes the CoEdge node when it has no other active + //! usages. + //! @param[in] theChildWireId wire definition identifier + //! @param[in] theCoEdgeId exact wire-owned coedge identifier //! @return true if the active wire-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveCoEdge(const BRepGraph_WireId theWireDefId, - const BRepGraph_CoEdgeRefId theCoEdgeRefId); + [[nodiscard]] Standard_EXPORT bool RemoveCoEdge(const BRepGraph_WireId theChildWireId, + const BRepGraph_CoEdgeId theCoEdgeId); + + //! Reverse the wire: flip the order of the wire's CoEdgeIds and flip each + //! owned CoEdge's orientation. Used by healing/sewing to invert a loop. + //! @param[in] theWire wire definition identifier + Standard_EXPORT void Reverse(const BRepGraph_WireId theWire); + + //! Replace the ordered CoEdge relation vector with a permutation of its + //! current content. + //! @param[in] theWire wire definition identifier + //! @param[in] theCoEdgeIds new ordered CoEdge identifiers + //! @return true if the order was accepted and applied + [[nodiscard]] Standard_EXPORT bool SetCoEdgeOrder( + const BRepGraph_WireId theWire, + const NCollection_Array1& theCoEdgeIds); //! Return scoped mutable wire definition guard. [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard Mut( @@ -609,45 +529,17 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_WireRefId theWireRef); - //! Set the IsClosed flag of a wire definition and fire immediate notification. - //! @param[in] theWire typed wire definition identifier - //! @param[in] theIsClosed new closed state - Standard_EXPORT void SetIsClosed(const BRepGraph_WireId theWire, bool theIsClosed); - - //! Set the IsClosed flag of a wire definition inside a batched mutation scope. - //! @param[in] theMut active mutable wire guard - //! @param[in] theIsClosed new closed state - Standard_EXPORT void SetIsClosed(BRepGraph_MutGuard& theMut, - bool theIsClosed); - - //! Set the local location of a wire reference and fire immediate notification. - //! @param[in] theWireRef typed wire reference identifier - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(const BRepGraph_WireRefId theWireRef, - const TopLoc_Location& theLoc); - - //! Set the local location of a wire reference inside a batched mutation scope. - //! @param[in] theMut active mutable wire reference guard - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc); - - //! Set the IsOuter flag on a wire reference. - Standard_EXPORT void SetRefIsOuter(const BRepGraph_WireRefId theWireRef, const bool theIsOuter); - Standard_EXPORT void SetRefIsOuter(BRepGraph_MutGuard& theMut, - const bool theIsOuter); - //! Set the orientation of a wire reference. - Standard_EXPORT void SetRefOrientation(const BRepGraph_WireRefId theWireRef, - const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetRefOrientation(const BRepGraph_WireRefId theWireRef, + const BRepGraphInc::ParityOrientation theOrientation); Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation); + const BRepGraphInc::ParityOrientation theOrientation); //! Rewire a wire reference to a different wire def (rebinds WireToFaces if parent is Face). - Standard_EXPORT void SetRefWireDefId(const BRepGraph_WireRefId theWireRef, - const BRepGraph_WireId theWire); - Standard_EXPORT void SetRefWireDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_WireId theWire); + Standard_EXPORT void SetRefChildWireId(const BRepGraph_WireRefId theWireRef, + const BRepGraph_WireId theWire); + Standard_EXPORT void SetRefChildWireId(BRepGraph_MutGuard& theMut, + const BRepGraph_WireId theWire); private: friend class EditorView; @@ -672,39 +564,31 @@ public: //! @return typed face definition identifier, or invalid if any referenced //! wire id is out of range or removed [[nodiscard]] Standard_EXPORT BRepGraph_FaceId - Add(const occ::handle& theSurface, - const BRepGraph_WireId theOuterWire, - const NCollection_DynamicArray& theInnerWires, - const double theTolerance); + Add(const occ::handle& theSurface, + const BRepGraph_WireId theOuterWire, + const NCollection_Array1& theInnerWires, + const double theTolerance); - //! Add a direct INTERNAL/EXTERNAL vertex usage to a face definition. - //! @param[in] theFaceEntity typed face definition identifier - //! @param[in] theVertexEntity typed vertex definition identifier - //! @param[in] theOri orientation of the direct vertex usage on the face - //! @return typed vertex reference identifier, or invalid if inputs are not active - [[nodiscard]] Standard_EXPORT BRepGraph_VertexRefId - AddVertex(const BRepGraph_FaceId theFaceEntity, - const BRepGraph_VertexId theVertexEntity, - const TopAbs_Orientation theOri = TopAbs_INTERNAL); - - //! Detach one exact direct vertex ref from a face definition. - //! Use BRepGraph_RefsVertexOfFace::CurrentId() when removing from a face - //! direct-vertex iterator. - //! @param[in] theFaceDefId face definition identifier - //! @param[in] theVertexRefId exact face-owned vertex reference identifier - //! @return true if the active face-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveVertex(const BRepGraph_FaceId theFaceDefId, - const BRepGraph_VertexRefId theVertexRefId); + //! Append a wire usage to an existing face definition. + //! @param[in] theFaceEntity typed face definition identifier + //! @param[in] theWireEntity typed wire definition identifier + //! @param[in] theOri orientation of the wire usage on the face + //! @return typed wire reference identifier, or invalid if inputs are not + //! active + [[nodiscard]] Standard_EXPORT BRepGraph_WireRefId + Append(const BRepGraph_FaceId theFaceEntity, + const BRepGraph_WireId theWireEntity, + const BRepGraphInc::ParityOrientation theOri = TopAbs_FORWARD); //! Detach one exact wire ref from a face definition. //! Use BRepGraph_RefsWireOfFace::CurrentId() when removing from a face //! iterator. The method removes the exact WireRef entry, erases it from - //! the face's ordered ref sequence, rebuilds reverse indices, and prunes the + //! the face's ordered ref sequence, updates relation tables, and prunes the //! Wire subtree when it has no other active usages. - //! @param[in] theFaceDefId face definition identifier + //! @param[in] theFaceId face definition identifier //! @param[in] theWireRefId exact face-owned wire reference identifier //! @return true if the active face-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveWire(const BRepGraph_FaceId theFaceDefId, + [[nodiscard]] Standard_EXPORT bool RemoveWire(const BRepGraph_FaceId theFaceId, const BRepGraph_WireRefId theWireRefId); //! Return scoped mutable face definition guard. @@ -726,63 +610,46 @@ public: Standard_EXPORT void SetTolerance(BRepGraph_MutGuard& theMut, double theTolerance); - //! Set the NaturalRestriction flag of a face definition and fire immediate notification. - //! @param[in] theFace typed face definition identifier - //! @param[in] theNaturalRestriction new flag value - Standard_EXPORT void SetNaturalRestriction(const BRepGraph_FaceId theFace, - bool theNaturalRestriction); + //! Set the surface on a face. Creates an owned FaceSurfaceRep record + //! and an associated SurfaceRep for face geometry access. + //! @param[in] theFace face definition identifier + //! @param[in] theSurface surface geometry (must not be null) + Standard_EXPORT void SetSurface(const BRepGraph_FaceId theFace, + const occ::handle& theSurface); - //! Set the NaturalRestriction flag inside a batched mutation scope. - //! @param[in] theMut active mutable face guard - //! @param[in] theNaturalRestriction new flag value - Standard_EXPORT void SetNaturalRestriction(BRepGraph_MutGuard& theMut, - bool theNaturalRestriction); + //! Clear the surface on a face. Removes the owned use record binding. + //! @param[in] theFace face definition identifier + Standard_EXPORT void ClearSurface(const BRepGraph_FaceId theFace); - //! Set the triangulation representation id and fire immediate notification. - //! Pass an invalid id to clear the triangulation binding. - //! @param[in] theFace typed face definition identifier - //! @param[in] theRep new triangulation rep identifier (may be invalid to clear) - Standard_EXPORT void SetTriangulationRep(const BRepGraph_FaceId theFace, - const BRepGraph_TriangulationRepId theRep); + //! Set the persistent triangulation on a face. Creates an owned FaceTriangulationRep record. + //! Also creates a TriangulationRep for backward compatibility. + //! @param[in] theFace face definition identifier + //! @param[in] theTriangulation triangulation mesh (must not be null) + Standard_EXPORT void SetPersistentTriangulation( + const BRepGraph_FaceId theFace, + const occ::handle& theTriangulation); - Standard_EXPORT void SetTriangulationRep(BRepGraph_MutGuard& theMut, - const BRepGraph_TriangulationRepId theRep); - - //! Set the SurfaceRep id bound to a face (invalid id clears the binding). - Standard_EXPORT void SetSurfaceRepId(const BRepGraph_FaceId theFace, - const BRepGraph_SurfaceRepId theRep); - Standard_EXPORT void SetSurfaceRepId(BRepGraph_MutGuard& theMut, - const BRepGraph_SurfaceRepId theRep); + //! Clear the persistent triangulation on a face. + //! @param[in] theFace face definition identifier + Standard_EXPORT void ClearPersistentTriangulation(const BRepGraph_FaceId theFace); //! Set the orientation of a face reference and fire immediate notification. //! @param[in] theFaceRef typed face reference identifier //! @param[in] theOrientation new orientation value - Standard_EXPORT void SetRefOrientation(const BRepGraph_FaceRefId theFaceRef, - TopAbs_Orientation theOrientation); + Standard_EXPORT void SetRefOrientation(const BRepGraph_FaceRefId theFaceRef, + BRepGraphInc::ParityOrientation theOrientation); //! Set the orientation of a face reference inside a batched mutation scope. //! @param[in] theMut active mutable face reference guard //! @param[in] theOrientation new orientation value Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, - TopAbs_Orientation theOrientation); - - //! Set the local location of a face reference and fire immediate notification. - //! @param[in] theFaceRef typed face reference identifier - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(const BRepGraph_FaceRefId theFaceRef, - const TopLoc_Location& theLoc); - - //! Set the local location of a face reference inside a batched mutation scope. - //! @param[in] theMut active mutable face reference guard - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc); + BRepGraphInc::ParityOrientation theOrientation); //! Rewire a face reference to a different face def (rebinds FaceToShells if parent is Shell). - Standard_EXPORT void SetRefFaceDefId(const BRepGraph_FaceRefId theFaceRef, - const BRepGraph_FaceId theFace); - Standard_EXPORT void SetRefFaceDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_FaceId theFace); + Standard_EXPORT void SetRefFaceId(const BRepGraph_FaceRefId theFaceRef, + const BRepGraph_FaceId theFace); + Standard_EXPORT void SetRefFaceId(BRepGraph_MutGuard& theMut, + const BRepGraph_FaceId theFace); private: friend class EditorView; @@ -803,46 +670,48 @@ public: //! @return typed shell definition identifier [[nodiscard]] Standard_EXPORT BRepGraph_ShellId Add(); - //! Link a face to a shell. + //! Append a face to a shell. //! Appends FaceRef and stores its FaceRefId in shell FaceRefIds. //! @param[in] theShellEntity typed shell definition identifier //! @param[in] theFaceEntity typed face definition identifier //! @param[in] theOri orientation of the face in the shell //! @return typed face reference identifier, or invalid if inputs are not active - Standard_EXPORT BRepGraph_FaceRefId AddFace(const BRepGraph_ShellId theShellEntity, - const BRepGraph_FaceId theFaceEntity, - const TopAbs_Orientation theOri = TopAbs_FORWARD); + Standard_EXPORT BRepGraph_FaceRefId + Append(const BRepGraph_ShellId theShellEntity, + const BRepGraph_FaceId theFaceEntity, + const BRepGraphInc::ParityOrientation theOri = TopAbs_FORWARD); - //! Link an auxiliary non-face child to a shell. - //! Supported child kinds are Wire and Edge. + //! Batch-append multiple faces to a shell. + //! Two-pass: validates all inputs first, then links all. //! @param[in] theShellEntity typed shell definition identifier - //! @param[in] theChildEntity typed child definition identifier - //! @param[in] theOri orientation of the child in the shell - //! @return typed child reference identifier, or invalid if inputs are not active - [[nodiscard]] Standard_EXPORT BRepGraph_ChildRefId - AddChild(const BRepGraph_ShellId theShellEntity, - const BRepGraph_NodeId theChildEntity, - const TopAbs_Orientation theOri = TopAbs_FORWARD); + //! @param[in] theFaceIds face definition identifiers to append + //! @param[in] theOrientations optional parity orientations (empty = all FORWARD) + //! @return array of created face reference ids, empty on validation failure + [[nodiscard]] Standard_EXPORT NCollection_Array1 Append( + const BRepGraph_ShellId theShellEntity, + const NCollection_Array1& theFaceIds, + const NCollection_Array1& theOrientations = + NCollection_Array1()); //! Detach one exact face ref from a shell definition. //! Use BRepGraph_RefsFaceOfShell::CurrentId() when removing from a shell //! iterator. The method removes the exact FaceRef entry, erases it from the - //! shell's ordered ref sequence, rebuilds reverse indices, and prunes the + //! shell's ordered ref sequence, updates relation tables, and prunes the //! Face subtree when it has no other active usages. - //! @param[in] theShellDefId shell definition identifier + //! @param[in] theChildShellId shell definition identifier //! @param[in] theFaceRefId exact shell-owned face reference identifier //! @return true if the active shell-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveFace(const BRepGraph_ShellId theShellDefId, + [[nodiscard]] Standard_EXPORT bool RemoveFace(const BRepGraph_ShellId theChildShellId, const BRepGraph_FaceRefId theFaceRefId); - //! Detach one exact child ref from a shell auxiliary-child sequence. - //! Use BRepGraph_RefsChildOfShell::CurrentId() when removing from a shell - //! aux-child iterator. - //! @param[in] theShellDefId shell definition identifier - //! @param[in] theChildRefId exact shell-owned child reference identifier - //! @return true if the active shell-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveChild(const BRepGraph_ShellId theShellDefId, - const BRepGraph_ChildRefId theChildRefId); + //! Batch-remove multiple face refs from a shell definition. + //! All-or-nothing: validates all inputs first, then removes all. + //! @param[in] theShellId shell definition identifier + //! @param[in] theFaceRefs face reference identifiers to remove + //! @return true if all refs were successfully removed + [[nodiscard]] Standard_EXPORT bool RemoveFaces( + const BRepGraph_ShellId theShellId, + const NCollection_Array1& theFaceRefs); //! Return scoped mutable shell definition guard. [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard Mut( @@ -852,39 +721,19 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_ShellRefId theShellRef); - //! Set the local location of a shell reference and fire immediate notification. - //! @param[in] theShellRef typed shell reference identifier - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(const BRepGraph_ShellRefId theShellRef, - const TopLoc_Location& theLoc); - - //! Set the local location of a shell reference inside a batched mutation scope. - //! @param[in] theMut active mutable shell reference guard - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc); - //! Set the orientation of a shell reference. - Standard_EXPORT void SetRefOrientation(const BRepGraph_ShellRefId theShellRef, - const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetRefOrientation(const BRepGraph_ShellRefId theShellRef, + const BRepGraphInc::ParityOrientation theOrientation); //! Set the orientation inside a batched mutation scope. Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation); + const BRepGraphInc::ParityOrientation theOrientation); - //! Rewire a shell reference to a different shell def (rebinds ShellToSolids if parent is - //! Solid). - Standard_EXPORT void SetRefShellDefId(const BRepGraph_ShellRefId theShellRef, - const BRepGraph_ShellId theShell); - Standard_EXPORT void SetRefShellDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_ShellId theShell); - - //! Set the IsClosed flag of a shell definition. - Standard_EXPORT void SetIsClosed(const BRepGraph_ShellId theShell, const bool theIsClosed); - - //! Set the IsClosed flag inside a batched mutation scope. - Standard_EXPORT void SetIsClosed(BRepGraph_MutGuard& theMut, - const bool theIsClosed); + //! Rewire a shell reference to a different shell def (rebinds ShellToSolid if parent is Solid). + Standard_EXPORT void SetRefChildShellId(const BRepGraph_ShellRefId theShellRef, + const BRepGraph_ShellId theShell); + Standard_EXPORT void SetRefChildShellId(BRepGraph_MutGuard& theMut, + const BRepGraph_ShellId theShell); private: friend class EditorView; @@ -905,46 +754,48 @@ public: //! @return typed solid definition identifier [[nodiscard]] Standard_EXPORT BRepGraph_SolidId Add(); - //! Link a shell to a solid. + //! Append a shell to a solid. //! Appends ShellRef and stores its ShellRefId in solid ShellRefIds. //! @param[in] theSolidEntity typed solid definition identifier //! @param[in] theShellEntity typed shell definition identifier //! @param[in] theOri orientation of the shell in the solid //! @return typed shell reference identifier, or invalid if inputs are not active - Standard_EXPORT BRepGraph_ShellRefId AddShell(const BRepGraph_SolidId theSolidEntity, - const BRepGraph_ShellId theShellEntity, - const TopAbs_Orientation theOri = TopAbs_FORWARD); + Standard_EXPORT BRepGraph_ShellRefId + Append(const BRepGraph_SolidId theSolidEntity, + const BRepGraph_ShellId theShellEntity, + const BRepGraphInc::ParityOrientation theOri = TopAbs_FORWARD); - //! Link an auxiliary non-shell child to a solid. - //! Supported child kinds are Edge and Vertex. + //! Batch-append multiple shells to a solid. + //! Two-pass: validates all inputs first, then links all. //! @param[in] theSolidEntity typed solid definition identifier - //! @param[in] theChildEntity typed child definition identifier - //! @param[in] theOri orientation of the child in the solid - //! @return typed child reference identifier, or invalid if inputs are not active - [[nodiscard]] Standard_EXPORT BRepGraph_ChildRefId - AddChild(const BRepGraph_SolidId theSolidEntity, - const BRepGraph_NodeId theChildEntity, - const TopAbs_Orientation theOri = TopAbs_FORWARD); + //! @param[in] theShellIds shell definition identifiers to append + //! @param[in] theOrientations optional parity orientations (empty = all FORWARD) + //! @return array of created shell reference ids, empty on validation failure + [[nodiscard]] Standard_EXPORT NCollection_Array1 Append( + const BRepGraph_SolidId theSolidEntity, + const NCollection_Array1& theShellIds, + const NCollection_Array1& theOrientations = + NCollection_Array1()); //! Detach one exact shell ref from a solid definition. //! Use BRepGraph_RefsShellOfSolid::CurrentId() when removing from a solid //! iterator. The method removes the exact ShellRef entry, erases it from the - //! solid's ordered ref sequence, rebuilds reverse indices, and prunes the + //! solid's ordered ref sequence, updates relation tables, and prunes the //! Shell subtree when it has no other active usages. - //! @param[in] theSolidDefId solid definition identifier + //! @param[in] theChildSolidId solid definition identifier //! @param[in] theShellRefId exact solid-owned shell reference identifier //! @return true if the active solid-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveShell(const BRepGraph_SolidId theSolidDefId, + [[nodiscard]] Standard_EXPORT bool RemoveShell(const BRepGraph_SolidId theChildSolidId, const BRepGraph_ShellRefId theShellRefId); - //! Detach one exact child ref from a solid auxiliary-child sequence. - //! Use BRepGraph_RefsChildOfSolid::CurrentId() when removing from a solid - //! aux-child iterator. - //! @param[in] theSolidDefId solid definition identifier - //! @param[in] theChildRefId exact solid-owned child reference identifier - //! @return true if the active solid-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveChild(const BRepGraph_SolidId theSolidDefId, - const BRepGraph_ChildRefId theChildRefId); + //! Batch-remove multiple shell refs from a solid definition. + //! All-or-nothing: validates all inputs first, then removes all. + //! @param[in] theSolidId solid definition identifier + //! @param[in] theShellRefs shell reference identifiers to remove + //! @return true if all refs were successfully removed + [[nodiscard]] Standard_EXPORT bool RemoveShells( + const BRepGraph_SolidId theSolidId, + const NCollection_Array1& theShellRefs); //! Return scoped mutable solid definition guard. [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard Mut( @@ -954,32 +805,20 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard MutRef( const BRepGraph_SolidRefId theSolidRef); - //! Set the local location of a solid reference and fire immediate notification. - //! @param[in] theSolidRef typed solid reference identifier - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(const BRepGraph_SolidRefId theSolidRef, - const TopLoc_Location& theLoc); - - //! Set the local location of a solid reference inside a batched mutation scope. - //! @param[in] theMut active mutable solid reference guard - //! @param[in] theLoc new local location - Standard_EXPORT void SetRefLocalLocation(BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc); - //! Set the orientation of a solid reference. - Standard_EXPORT void SetRefOrientation(const BRepGraph_SolidRefId theSolidRef, - const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetRefOrientation(const BRepGraph_SolidRefId theSolidRef, + const BRepGraphInc::ParityOrientation theOrientation); //! Set the orientation inside a batched mutation scope. Standard_EXPORT void SetRefOrientation(BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation); + const BRepGraphInc::ParityOrientation theOrientation); //! Rewire a solid reference to a different solid def (rebinds SolidToCompSolid if parent is //! CompSolid). - Standard_EXPORT void SetRefSolidDefId(const BRepGraph_SolidRefId theSolidRef, - const BRepGraph_SolidId theSolid); - Standard_EXPORT void SetRefSolidDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_SolidId theSolid); + Standard_EXPORT void SetRefChildSolidId(const BRepGraph_SolidRefId theSolidRef, + const BRepGraph_SolidId theSolid); + Standard_EXPORT void SetRefChildSolidId(BRepGraph_MutGuard& theMut, + const BRepGraph_SolidId theSolid); private: friend class EditorView; @@ -996,11 +835,11 @@ public: class CompoundOps { public: - //! Add a compound definition with child definitions. - //! @param[in] theChildEntities child definition NodeIds + //! Add a compound entity with ordered child usages. + //! @param[in] theChildEntities child node identifiers //! @return typed compound definition identifier [[nodiscard]] Standard_EXPORT BRepGraph_CompoundId - Add(const NCollection_DynamicArray& theChildEntities); + Add(const NCollection_Array1& theChildEntities); //! Append a single child to an existing compound definition. //! @param[in] theCompoundEntity typed compound definition identifier @@ -1008,14 +847,26 @@ public: //! @param[in] theOri orientation of the child in the compound //! @return typed child reference identifier, or invalid if inputs are not active [[nodiscard]] Standard_EXPORT BRepGraph_ChildRefId - AddChild(const BRepGraph_CompoundId theCompoundEntity, - const BRepGraph_NodeId theChildEntity, - const TopAbs_Orientation theOri = TopAbs_FORWARD); + Append(const BRepGraph_CompoundId theCompoundEntity, + const BRepGraph_NodeId theChildEntity, + const BRepGraphInc::ParityOrientation theOri = TopAbs_FORWARD); + + //! Batch-append multiple children to an existing compound definition. + //! Two-pass: validates all inputs first, then links all. + //! @param[in] theCompoundEntity typed compound definition identifier + //! @param[in] theChildIds child node identifiers to append + //! @param[in] theOrientations optional parity orientations (empty = all FORWARD) + //! @return array of created child reference ids, empty on validation failure + [[nodiscard]] Standard_EXPORT NCollection_Array1 Append( + const BRepGraph_CompoundId theCompoundEntity, + const NCollection_Array1& theChildIds, + const NCollection_Array1& theOrientations = + NCollection_Array1()); //! Detach one exact child ref from a compound definition. //! Use BRepGraph_RefsChildOfParent::CurrentId() when removing from a compound //! iterator. The method removes the exact ChildRef entry, erases it from the - //! compound's ordered ref sequence, rebuilds reverse indices, and prunes the + //! compound's ordered ref sequence, updates relation tables, and prunes the //! child subtree when it has no other active usages. //! @param[in] theCompoundDefId compound definition identifier //! @param[in] theChildRefId exact compound-owned child reference identifier @@ -1023,10 +874,26 @@ public: [[nodiscard]] Standard_EXPORT bool RemoveChild(const BRepGraph_CompoundId theCompoundDefId, const BRepGraph_ChildRefId theChildRefId); + //! Batch-remove multiple child refs from a compound definition. + //! All-or-nothing: validates all inputs first, then removes all. + //! @param[in] theCompoundId compound definition identifier + //! @param[in] theChildRefs child reference identifiers to remove + //! @return true if all refs were successfully removed + [[nodiscard]] Standard_EXPORT bool RemoveChildren( + const BRepGraph_CompoundId theCompoundId, + const NCollection_Array1& theChildRefs); + //! Return scoped mutable compound definition guard. [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard Mut( const BRepGraph_CompoundId theCompound); + //! Replace the child node of an existing child reference in a compound. + //! Delegates to Gen().SetChildRefChildNodeId(). + //! @param[in] theChildRef typed child reference identifier + //! @param[in] theNewChild new child node identifier + Standard_EXPORT void ReplaceChild(const BRepGraph_ChildRefId theChildRef, + const BRepGraph_NodeId theNewChild); + private: friend class EditorView; @@ -1042,11 +909,11 @@ public: class CompSolidOps { public: - //! Add a compsolid definition with child solid definitions. - //! @param[in] theSolidEntities typed child solid definition identifiers + //! Add a compsolid entity with ordered solid usages. + //! @param[in] theSolidEntities typed child solid identifiers //! @return typed compsolid definition identifier [[nodiscard]] Standard_EXPORT BRepGraph_CompSolidId - Add(const NCollection_DynamicArray& theSolidEntities); + Add(const NCollection_Array1& theSolidEntities); //! Append a single solid to an existing compsolid definition. //! @param[in] theCompSolidEntity typed compsolid definition identifier @@ -1054,25 +921,53 @@ public: //! @param[in] theOri orientation of the solid in the compsolid //! @return typed solid reference identifier, or invalid if inputs are not active [[nodiscard]] Standard_EXPORT BRepGraph_SolidRefId - AddSolid(const BRepGraph_CompSolidId theCompSolidEntity, - const BRepGraph_SolidId theSolidEntity, - const TopAbs_Orientation theOri = TopAbs_FORWARD); + Append(const BRepGraph_CompSolidId theCompSolidEntity, + const BRepGraph_SolidId theSolidEntity, + const BRepGraphInc::ParityOrientation theOri = TopAbs_FORWARD); + + //! Batch-append multiple solids to an existing compsolid definition. + //! Two-pass: validates all inputs first, then links all. + //! @param[in] theCompSolidEntity typed compsolid definition identifier + //! @param[in] theSolidIds solid definition identifiers to append + //! @param[in] theOrientations optional parity orientations (empty = all FORWARD) + //! @return array of created solid reference ids, empty on validation failure + [[nodiscard]] Standard_EXPORT NCollection_Array1 Append( + const BRepGraph_CompSolidId theCompSolidEntity, + const NCollection_Array1& theSolidIds, + const NCollection_Array1& theOrientations = + NCollection_Array1()); //! Detach one exact solid ref from a compsolid definition. //! Use BRepGraph_RefsSolidOfCompSolid::CurrentId() when removing from a //! compsolid iterator. The method removes the exact SolidRef entry, erases it - //! from the compsolid's ordered ref sequence, rebuilds reverse indices, and + //! from the compsolid's ordered ref sequence, updates relation tables, and //! prunes the Solid subtree when it has no other active usages. - //! @param[in] theCompSolidDefId compsolid definition identifier + //! @param[in] theCompChildSolidId compsolid definition identifier //! @param[in] theSolidRefId exact compsolid-owned solid reference identifier //! @return true if the active compsolid-owned usage was removed - [[nodiscard]] Standard_EXPORT bool RemoveSolid(const BRepGraph_CompSolidId theCompSolidDefId, + [[nodiscard]] Standard_EXPORT bool RemoveSolid(const BRepGraph_CompSolidId theCompChildSolidId, const BRepGraph_SolidRefId theSolidRefId); + //! Batch-remove multiple solid refs from a compsolid definition. + //! All-or-nothing: validates all inputs first, then removes all. + //! @param[in] theCompSolidId compsolid definition identifier + //! @param[in] theSolidRefs solid reference identifiers to remove + //! @return true if all refs were successfully removed + [[nodiscard]] Standard_EXPORT bool RemoveSolids( + const BRepGraph_CompSolidId theCompSolidId, + const NCollection_Array1& theSolidRefs); + //! Return scoped mutable comp-solid definition guard. [[nodiscard]] Standard_EXPORT BRepGraph_MutGuard Mut( const BRepGraph_CompSolidId theCompSolid); + //! Replace the solid of an existing solid reference in a compsolid. + //! Delegates to Solids().SetRefChildSolidId(). + //! @param[in] theSolidRef typed solid reference identifier + //! @param[in] theNewSolid new solid definition identifier + Standard_EXPORT void ReplaceSolid(const BRepGraph_SolidRefId theSolidRef, + const BRepGraph_SolidId theNewSolid); + private: friend class EditorView; @@ -1085,24 +980,31 @@ public: }; //! @brief Product and assembly low-level reconstruction primitives. - //! Wire two existing entities together; for shape ingestion use BRepGraph_Builder::Add(). + //! Wire two existing entities together; for shape ingestion use BRepGraph::ShapesView::Add(). class ProductOps { public: //! Create a Product wrapping an existing topology root via an Occurrence. + //! The product is NOT added to document roots; call AppendDocumentRoot() explicitly + //! when this Product is a document root. //! @param[in] theShapeRoot root topology NodeId for the part //! @param[in] thePlacement local placement stored on the root OccurrenceRef //! @return typed product definition identifier, or invalid if the root is //! not an active topology definition node [[nodiscard]] Standard_EXPORT BRepGraph_ProductId - LinkProductToTopology(const BRepGraph_NodeId theShapeRoot, - const TopLoc_Location& thePlacement = TopLoc_Location()); + Add(const BRepGraph_NodeId theShapeRoot, + const TopLoc_Location& thePlacement = TopLoc_Location()); - //! Create a Product with no direct shape root; can later own child occurrences. + //! Create an empty Product with no direct shape root; can later own child occurrences. + //! The product is NOT added to document roots; call AppendDocumentRoot() explicitly + //! when this Product is a document root. //! @return typed product definition identifier - [[nodiscard]] Standard_EXPORT BRepGraph_ProductId CreateEmptyProduct(); + [[nodiscard]] Standard_EXPORT BRepGraph_ProductId Add(); - //! Link two existing Products via a fresh Occurrence. + //! Add an active Product to document roots if it is not already listed. + Standard_EXPORT void AppendDocumentRoot(const BRepGraph_ProductId theProductId); + + //! Append two existing Products via a fresh Occurrence. //! @param[in] theParentProduct typed parent product identifier //! @param[in] theReferencedProduct typed child product identifier being instantiated //! @param[in] thePlacement local placement relative to parent @@ -1110,16 +1012,27 @@ public: //! @param[out] theOutOccurrenceRefId optional out: typed ref id of the inserted OccurrenceRef //! @return typed occurrence definition identifier, or invalid if the chain is not active [[nodiscard]] Standard_EXPORT BRepGraph_OccurrenceId - LinkProducts(const BRepGraph_ProductId theParentProduct, - const BRepGraph_ProductId theReferencedProduct, - const TopLoc_Location& thePlacement, - const BRepGraph_OccurrenceId theParentOccurrence = BRepGraph_OccurrenceId(), - BRepGraph_OccurrenceRefId* theOutOccurrenceRefId = nullptr); + Append(const BRepGraph_ProductId theParentProduct, + const BRepGraph_ProductId theReferencedProduct, + const TopLoc_Location& thePlacement, + const BRepGraph_OccurrenceId theParentOccurrence = BRepGraph_OccurrenceId(), + BRepGraph_OccurrenceRefId* theOutOccurrenceRefId = nullptr); + + //! Batch-append multiple child products to a parent product via fresh Occurrences. + //! Two-pass: validates all inputs first, then links all. + //! @param[in] theParentProduct typed parent product identifier + //! @param[in] theChildProducts child product identifiers to instantiate + //! @param[in] thePlacements local placements per child (must match child count) + //! @return array of created occurrence reference ids, empty on validation failure + [[nodiscard]] Standard_EXPORT NCollection_Array1 Append( + const BRepGraph_ProductId theParentProduct, + const NCollection_Array1& theChildProducts, + const NCollection_Array1& thePlacements); //! Detach one exact occurrence ref from a product definition. //! Use BRepGraph_RefsOccurrenceOfProduct::CurrentId() when removing from a //! product iterator. The method removes the exact OccurrenceRef entry, erases - //! it from the product's ordered ref sequence, rebuilds reverse indices, and + //! it from the product's ordered ref sequence, updates relation tables, and //! prunes the occurrence subtree when it has no other active usages. //! @param[in] theProductDefId product definition identifier //! @param[in] theOccurrenceRefId exact product-owned occurrence reference identifier @@ -1128,6 +1041,15 @@ public: const BRepGraph_ProductId theProductDefId, const BRepGraph_OccurrenceRefId theOccurrenceRefId); + //! Batch-remove multiple occurrence refs from a product definition. + //! All-or-nothing: validates all inputs first, then removes all. + //! @param[in] theProductId product definition identifier + //! @param[in] theOccurrenceRefs occurrence reference identifiers to remove + //! @return true if all refs were successfully removed + [[nodiscard]] Standard_EXPORT bool RemoveOccurrences( + const BRepGraph_ProductId theProductId, + const NCollection_Array1& theOccurrenceRefs); + //! Detach the scalar shape-root ownership from a product definition. //! If no other active product owns the same topology root afterward, the root //! subgraph is pruned as orphaned. The product loses its direct shape root; @@ -1178,19 +1100,21 @@ public: const TopLoc_Location& theLoc); //! Set the child node referenced by an occurrence definition. - //! The child kind must be a topology root or a Product - invalid kinds are - //! accepted but the resulting graph will fail Validate. - Standard_EXPORT void SetChildDefId(const BRepGraph_OccurrenceId theOccurrence, - const BRepGraph_NodeId theChildDefId); + //! Invalid or removed occurrence ids are ignored. The child must be an + //! active topology node or an active Product; invalid, removed, and + //! Occurrence child ids are ignored. + Standard_EXPORT void SetChildNodeId(const BRepGraph_OccurrenceId theOccurrence, + const BRepGraph_NodeId theChildNodeId); - //! Set the child node id inside a batched mutation scope. - Standard_EXPORT void SetChildDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_NodeId theChildDefId); + //! Set the child node id inside a batched mutation scope. Invalid, removed, + //! and Occurrence child ids are ignored. + Standard_EXPORT void SetChildNodeId(BRepGraph_MutGuard& theMut, + const BRepGraph_NodeId theChildNodeId); //! Rewire an occurrence reference to a different occurrence def (rebinds ProductToOccurrences). - Standard_EXPORT void SetRefOccurrenceDefId(const BRepGraph_OccurrenceRefId theOccurrenceRef, - const BRepGraph_OccurrenceId theOccurrence); - Standard_EXPORT void SetRefOccurrenceDefId( + Standard_EXPORT void SetRefChildOccurrenceId(const BRepGraph_OccurrenceRefId theOccurrenceRef, + const BRepGraph_OccurrenceId theOccurrence); + Standard_EXPORT void SetRefChildOccurrenceId( BRepGraph_MutGuard& theMut, const BRepGraph_OccurrenceId theOccurrence); @@ -1213,15 +1137,19 @@ public: //! @param[in] theNode node to remove Standard_EXPORT void RemoveNode(const BRepGraph_NodeId theNode); - //! Mark a node as removed with a known replacement (sewing/deduplicate). + //! Replace a node by another active node and mark the old node as removed. //! For Edge nodes: all CoEdges referencing the removed edge are reparented to - //! the replacement edge (EdgeIdx updated, reverse index rebound). This prevents + //! the replacement edge (ChildEdgeId updated, relation entries rebound). This prevents //! orphaned CoEdges that would disappear from CoEdgesOfEdge() queries. - //! Layers are notified with both old and replacement NodeIds for data migration. + //! If the replacement is active, layers receive OnNodeReplaced(theNode, + //! theReplacement) for structural data migration. If the replacement is invalid + //! or inactive, the operation falls back to OnNodeRemoved(theNode), matching pure + //! deletion. Semantic history records are not inferred here; algorithms should + //! record operation-specific history. //! @param[in] theNode node to remove //! @param[in] theReplacement node that replaces theNode - Standard_EXPORT void RemoveNode(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement); + Standard_EXPORT void ReplaceNode(const BRepGraph_NodeId theNode, + const BRepGraph_NodeId theReplacement); //! Mark a node and all its descendants as removed (cascading soft deletion). //! @param[in] theNode root node to remove @@ -1250,13 +1178,6 @@ public: const BRepGraph_RefId theRef, const bool theToPruneOrphanedChild); - //! Mark a representation entry as removed (soft deletion). - //! Invalid or already-removed ids are ignored. - //! Owning topology entities are marked modified so generation-based caches - //! and read helpers observe the representation as absent. - //! @param[in] theRep representation to remove - Standard_EXPORT void RemoveRep(const BRepGraph_RepId theRep); - //! Return scoped mutable child reference guard. ChildRef is generic (the //! child node can be of any kind), so its Mut accessor lives on the //! cross-kind Gen() rather than on a per-kind Ops. @@ -1270,18 +1191,20 @@ public: const TopLoc_Location& theLoc); //! Set the orientation of a child reference. - Standard_EXPORT void SetChildRefOrientation(const BRepGraph_ChildRefId theChildRef, - const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetChildRefOrientation( + const BRepGraph_ChildRefId theChildRef, + const BRepGraphInc::ParityOrientation theOrientation); //! Set the orientation inside a batched mutation scope. - Standard_EXPORT void SetChildRefOrientation(BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation); + Standard_EXPORT void SetChildRefOrientation( + BRepGraph_MutGuard& theMut, + const BRepGraphInc::ParityOrientation theOrientation); //! Rewire a child reference to a different child def (rebinds CompoundsOf). - Standard_EXPORT void SetChildRefChildDefId(const BRepGraph_ChildRefId theChildRef, - const BRepGraph_NodeId theChild); - Standard_EXPORT void SetChildRefChildDefId(BRepGraph_MutGuard& theMut, - const BRepGraph_NodeId theChild); + Standard_EXPORT void SetChildRefChildNodeId(const BRepGraph_ChildRefId theChildRef, + const BRepGraph_NodeId theChild); + Standard_EXPORT void SetChildRefChildNodeId(BRepGraph_MutGuard& theMut, + const BRepGraph_NodeId theChild); //! Set the local location of a child reference inside a batched mutation scope. //! @param[in] theMut active mutable child reference guard @@ -1299,11 +1222,23 @@ public: ModifierT&& theModifier, const TCollection_AsciiString& theOpLabel) { - NCollection_DynamicArray aReplacements = - std::forward(theModifier)(*myGraph, theTarget); + auto aProducedReplacements = std::forward(theModifier)(*myGraph, theTarget); + NCollection_LinearVector aReplacements(aProducedReplacements.Size()); + for (const BRepGraph_NodeId& aNode : aProducedReplacements) + { + aReplacements.Append(aNode); + } applyModificationImpl(theTarget, std::move(aReplacements), theOpLabel); } + //! Clean up forward references to removed nodes in relation tables and + //! references. After one or more RemoveNode calls, other entities may + //! still hold stale child references pointing to removed nodes. This method + //! marks those stale references as removed, detaches them from parent + //! arrays, and updates relation entries for consistency. + //! @post ValidateRelations() passes. + Standard_EXPORT void CleanupRemovedReferences(); + private: friend class EditorView; @@ -1314,7 +1249,7 @@ public: Standard_EXPORT void applyModificationImpl( const BRepGraph_NodeId theTarget, - NCollection_DynamicArray&& theReplacements, + NCollection_LinearVector&& theReplacements, const TCollection_AsciiString& theOpLabel); BRepGraph* myGraph; @@ -1357,8 +1292,11 @@ public: //! Return generic node, reference, and representation removal operations. [[nodiscard]] GenOps& Gen() { return myGenOps; } - //! Return representation (surface, curve, triangulation, polygon) mutation operations. - [[nodiscard]] RepOps& Reps() { return myRepOps; } + //! Return runtime supplement attachment operations. + [[nodiscard]] BRepGraph_SupplementEditor Supplement() + { + return BRepGraph_SupplementEditor(*myGraph); + } //! Begin deferred invalidation mode. //! While active, markModified() only increments OwnGen + SubtreeGen and @@ -1393,7 +1331,7 @@ public: }; //! Finalize a batch of mutations. - //! Validates reverse-index consistency and asserts active entity counts + //! Validates relation consistency and asserts active entity counts //! match actual entity state. //! Call this after manual batch mutation loops, or rely on //! BRepGraph_DeferredScope to call it automatically at scope exit. @@ -1403,7 +1341,7 @@ public: //! @param[out] theIssues optional destination for detailed issues //! @return true if no issues were found [[nodiscard]] Standard_EXPORT bool ValidateMutationBoundary( - NCollection_DynamicArray* const theIssues = nullptr) const; + NCollection_LinearVector* const theIssues = nullptr) const; private: friend class BRepGraph; @@ -1422,11 +1360,51 @@ private: myCompSolidOps(theGraph), myProductOps(theGraph), myOccurrenceOps(theGraph), - myGenOps(theGraph), - myRepOps(theGraph) + myGenOps(theGraph) { } + [[nodiscard]] Standard_EXPORT bool isOwned(const BRepGraph_ItemId theItem) const; + + [[nodiscard]] bool isOwned(const BRepGraph_NodeId theNode) const + { + return isOwned(BRepGraph_ItemId(theNode)); + } + + [[nodiscard]] bool isOwned(const BRepGraph_RefId theRef) const + { + return isOwned(BRepGraph_ItemId(theRef)); + } + + Standard_EXPORT void requireUnlocked(const BRepGraph_ItemId theItem, + const char* theOperation) const; + + void requireUnlocked(const BRepGraph_NodeId theNode, const char* theOperation) const + { + requireUnlocked(BRepGraph_ItemId(theNode), theOperation); + } + + void requireUnlocked(const BRepGraph_RefId theRef, const char* theOperation) const + { + requireUnlocked(BRepGraph_ItemId(theRef), theOperation); + } + + //! Verify no active MutGuard holds the given item. + //! Used by structural operations (Remove*, Replace*, Add*) to prevent + //! topology changes while a guard is active on the target item. + Standard_EXPORT void requireNoActiveGuard(const BRepGraph_ItemId theItem, + const char* theOperation) const; + + void requireNoActiveGuard(const BRepGraph_NodeId theNode, const char* theOperation) const + { + requireNoActiveGuard(BRepGraph_ItemId(theNode), theOperation); + } + + void requireNoActiveGuard(const BRepGraph_RefId theRef, const char* theOperation) const + { + requireNoActiveGuard(BRepGraph_ItemId(theRef), theOperation); + } + BRepGraph* myGraph; VertexOps myVertexOps; EdgeOps myEdgeOps; @@ -1440,7 +1418,6 @@ private: ProductOps myProductOps; OccurrenceOps myOccurrenceOps; GenOps myGenOps; - RepOps myRepOps; }; #endif // _BRepGraph_EditorView_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Mut.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Mut.cxx index 5dfa994569..c8ba27ac8f 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Mut.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Mut.cxx @@ -12,11 +12,11 @@ // commercial license or contractual agreement. #include + #include #include #include #include - #include namespace @@ -61,45 +61,6 @@ static bool isNodeIndexInRange(const BRepGraphInc_Storage& theStorage, //================================================================================================== -static const BRepGraphInc::BaseDef* topoEntity(const BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theNode) -{ - if (!isNodeIndexInRange(theStorage, theNode)) - { - return nullptr; - } - - switch (theNode.NodeKind) - { - case BRepGraph_NodeId::Kind::Vertex: - return &theStorage.Vertex(BRepGraph_VertexId(theNode)); - case BRepGraph_NodeId::Kind::Edge: - return &theStorage.Edge(BRepGraph_EdgeId(theNode)); - case BRepGraph_NodeId::Kind::CoEdge: - return &theStorage.CoEdge(BRepGraph_CoEdgeId(theNode)); - case BRepGraph_NodeId::Kind::Wire: - return &theStorage.Wire(BRepGraph_WireId(theNode)); - case BRepGraph_NodeId::Kind::Face: - return &theStorage.Face(BRepGraph_FaceId(theNode)); - case BRepGraph_NodeId::Kind::Shell: - return &theStorage.Shell(BRepGraph_ShellId(theNode)); - case BRepGraph_NodeId::Kind::Solid: - return &theStorage.Solid(BRepGraph_SolidId(theNode)); - case BRepGraph_NodeId::Kind::Compound: - return &theStorage.Compound(BRepGraph_CompoundId(theNode)); - case BRepGraph_NodeId::Kind::CompSolid: - return &theStorage.CompSolid(BRepGraph_CompSolidId(theNode)); - case BRepGraph_NodeId::Kind::Product: - return &theStorage.Product(BRepGraph_ProductId(theNode)); - case BRepGraph_NodeId::Kind::Occurrence: - return &theStorage.Occurrence(BRepGraph_OccurrenceId(theNode)); - } - - return nullptr; -} - -//================================================================================================== - static bool isRefIndexInRange(const BRepGraphInc_Storage& theStorage, const BRepGraph_RefId theRefId) { @@ -116,8 +77,6 @@ static bool isRefIndexInRange(const BRepGraphInc_Storage& theStorage, return theRefId.IsValid(theStorage.NbFaceRefs()); case BRepGraph_RefId::Kind::Wire: return theRefId.IsValid(theStorage.NbWireRefs()); - case BRepGraph_RefId::Kind::CoEdge: - return theRefId.IsValid(theStorage.NbCoEdgeRefs()); case BRepGraph_RefId::Kind::Vertex: return theRefId.IsValid(theStorage.NbVertexRefs()); case BRepGraph_RefId::Kind::Solid: @@ -133,42 +92,39 @@ static bool isRefIndexInRange(const BRepGraphInc_Storage& theStorage, //================================================================================================== -static bool isRepIndexInRange(const BRepGraphInc_Storage& theStorage, - const BRepGraph_RepId theRepId) -{ - if (!theRepId.IsValid()) - { - return false; - } - - switch (theRepId.RepKind) - { - case BRepGraph_RepId::Kind::Surface: - return theRepId.IsValid(theStorage.NbSurfaces()); - case BRepGraph_RepId::Kind::Curve3D: - return theRepId.IsValid(theStorage.NbCurves3D()); - case BRepGraph_RepId::Kind::Curve2D: - return theRepId.IsValid(theStorage.NbCurves2D()); - case BRepGraph_RepId::Kind::Triangulation: - return theRepId.IsValid(theStorage.NbTriangulations()); - case BRepGraph_RepId::Kind::Polygon3D: - return theRepId.IsValid(theStorage.NbPolygons3D()); - case BRepGraph_RepId::Kind::Polygon2D: - return theRepId.IsValid(theStorage.NbPolygons2D()); - case BRepGraph_RepId::Kind::PolygonOnTri: - return theRepId.IsValid(theStorage.NbPolygonsOnTri()); - } - - return false; -} - -//================================================================================================== - [[maybe_unused]] static bool isActiveNode(const BRepGraphInc_Storage& theStorage, const BRepGraph_NodeId theNode) { - const BRepGraphInc::BaseDef* aDef = topoEntity(theStorage, theNode); - return aDef != nullptr && !aDef->IsRemoved; + if (!isNodeIndexInRange(theStorage, theNode)) + { + return false; + } + switch (theNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + return !theStorage.IsRemoved(BRepGraph_VertexId(theNode)); + case BRepGraph_NodeId::Kind::Edge: + return !theStorage.IsRemoved(BRepGraph_EdgeId(theNode)); + case BRepGraph_NodeId::Kind::CoEdge: + return !theStorage.IsRemoved(BRepGraph_CoEdgeId(theNode)); + case BRepGraph_NodeId::Kind::Wire: + return !theStorage.IsRemoved(BRepGraph_WireId(theNode)); + case BRepGraph_NodeId::Kind::Face: + return !theStorage.IsRemoved(BRepGraph_FaceId(theNode)); + case BRepGraph_NodeId::Kind::Shell: + return !theStorage.IsRemoved(BRepGraph_ShellId(theNode)); + case BRepGraph_NodeId::Kind::Solid: + return !theStorage.IsRemoved(BRepGraph_SolidId(theNode)); + case BRepGraph_NodeId::Kind::Compound: + return !theStorage.IsRemoved(BRepGraph_CompoundId(theNode)); + case BRepGraph_NodeId::Kind::CompSolid: + return !theStorage.IsRemoved(BRepGraph_CompSolidId(theNode)); + case BRepGraph_NodeId::Kind::Product: + return !theStorage.IsRemoved(BRepGraph_ProductId(theNode)); + case BRepGraph_NodeId::Kind::Occurrence: + return !theStorage.IsRemoved(BRepGraph_OccurrenceId(theNode)); + } + return false; } //================================================================================================== @@ -184,21 +140,19 @@ static bool isRepIndexInRange(const BRepGraphInc_Storage& theStorage, switch (theRefId.RefKind) { case BRepGraph_RefId::Kind::Shell: - return !theStorage.ShellRef(BRepGraph_ShellRefId(theRefId)).IsRemoved; + return !theStorage.IsRemoved(BRepGraph_ShellRefId(theRefId)); case BRepGraph_RefId::Kind::Face: - return !theStorage.FaceRef(BRepGraph_FaceRefId(theRefId)).IsRemoved; + return !theStorage.IsRemoved(BRepGraph_FaceRefId(theRefId)); case BRepGraph_RefId::Kind::Wire: - return !theStorage.WireRef(BRepGraph_WireRefId(theRefId)).IsRemoved; - case BRepGraph_RefId::Kind::CoEdge: - return !theStorage.CoEdgeRef(BRepGraph_CoEdgeRefId(theRefId)).IsRemoved; + return !theStorage.IsRemoved(BRepGraph_WireRefId(theRefId)); case BRepGraph_RefId::Kind::Vertex: - return !theStorage.VertexRef(BRepGraph_VertexRefId(theRefId)).IsRemoved; + return !theStorage.IsRemoved(BRepGraph_VertexRefId(theRefId)); case BRepGraph_RefId::Kind::Solid: - return !theStorage.SolidRef(BRepGraph_SolidRefId(theRefId)).IsRemoved; + return !theStorage.IsRemoved(BRepGraph_SolidRefId(theRefId)); case BRepGraph_RefId::Kind::Child: - return !theStorage.ChildRef(BRepGraph_ChildRefId(theRefId)).IsRemoved; + return !theStorage.IsRemoved(BRepGraph_ChildRefId(theRefId)); case BRepGraph_RefId::Kind::Occurrence: - return !theStorage.OccurrenceRef(BRepGraph_OccurrenceRefId(theRefId)).IsRemoved; + return !theStorage.IsRemoved(BRepGraph_OccurrenceRefId(theRefId)); } return false; @@ -206,62 +160,96 @@ static bool isRepIndexInRange(const BRepGraphInc_Storage& theStorage, //================================================================================================== -[[maybe_unused]] static bool isActiveRep(const BRepGraphInc_Storage& theStorage, - const BRepGraph_RepId theRepId) +[[maybe_unused]] static bool isOwnedNode(const BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theNodeId) { - if (!isRepIndexInRange(theStorage, theRepId)) + if (!isNodeIndexInRange(theStorage, theNodeId)) { return false; } - - switch (theRepId.RepKind) + switch (theNodeId.NodeKind) { - case BRepGraph_RepId::Kind::Surface: - return !theStorage.SurfaceRep(BRepGraph_SurfaceRepId(theRepId)).IsRemoved; - case BRepGraph_RepId::Kind::Curve3D: - return !theStorage.Curve3DRep(BRepGraph_Curve3DRepId(theRepId)).IsRemoved; - case BRepGraph_RepId::Kind::Curve2D: - return !theStorage.Curve2DRep(BRepGraph_Curve2DRepId(theRepId)).IsRemoved; - case BRepGraph_RepId::Kind::Triangulation: - return !theStorage.TriangulationRep(BRepGraph_TriangulationRepId(theRepId)).IsRemoved; - case BRepGraph_RepId::Kind::Polygon3D: - return !theStorage.Polygon3DRep(BRepGraph_Polygon3DRepId(theRepId)).IsRemoved; - case BRepGraph_RepId::Kind::Polygon2D: - return !theStorage.Polygon2DRep(BRepGraph_Polygon2DRepId(theRepId)).IsRemoved; - case BRepGraph_RepId::Kind::PolygonOnTri: - return !theStorage.PolygonOnTriRep(BRepGraph_PolygonOnTriRepId(theRepId)).IsRemoved; + case BRepGraph_NodeId::Kind::Vertex: + return theStorage.IsOwned(BRepGraph_VertexId(theNodeId)); + case BRepGraph_NodeId::Kind::Edge: + return theStorage.IsOwned(BRepGraph_EdgeId(theNodeId)); + case BRepGraph_NodeId::Kind::CoEdge: + return theStorage.IsOwned(BRepGraph_CoEdgeId(theNodeId)); + case BRepGraph_NodeId::Kind::Wire: + return theStorage.IsOwned(BRepGraph_WireId(theNodeId)); + case BRepGraph_NodeId::Kind::Face: + return theStorage.IsOwned(BRepGraph_FaceId(theNodeId)); + case BRepGraph_NodeId::Kind::Shell: + return theStorage.IsOwned(BRepGraph_ShellId(theNodeId)); + case BRepGraph_NodeId::Kind::Solid: + return theStorage.IsOwned(BRepGraph_SolidId(theNodeId)); + case BRepGraph_NodeId::Kind::Compound: + return theStorage.IsOwned(BRepGraph_CompoundId(theNodeId)); + case BRepGraph_NodeId::Kind::CompSolid: + return theStorage.IsOwned(BRepGraph_CompSolidId(theNodeId)); + case BRepGraph_NodeId::Kind::Product: + return theStorage.IsOwned(BRepGraph_ProductId(theNodeId)); + case BRepGraph_NodeId::Kind::Occurrence: + return theStorage.IsOwned(BRepGraph_OccurrenceId(theNodeId)); } - return false; } //================================================================================================== static void validateMutableNodeId(const BRepGraphInc_Storage& theStorage [[maybe_unused]], - const BRepGraph_NodeId theNodeId [[maybe_unused]]) + const BRepGraph_NodeId theNodeId [[maybe_unused]], + const BRepGraph& /*theGraph*/) { Standard_ProgramError_Raise_if(!isActiveNode(theStorage, theNodeId), "BRepGraph::EditorView::Mut*(): invalid node id"); + Standard_ProgramError_Raise_if(isOwnedNode(theStorage, theNodeId), + "BRepGraph::EditorView::Mut*(): owned node"); } //================================================================================================== static void validateMutableRefId(const BRepGraphInc_Storage& theStorage [[maybe_unused]], - const BRepGraph_RefId theRefId [[maybe_unused]]) + const BRepGraph_RefId theRefId [[maybe_unused]], + const BRepGraph& /*theGraph*/) { Standard_ProgramError_Raise_if(!isActiveRef(theStorage, theRefId), "BRepGraph::EditorView::Mut*(): invalid reference id"); + switch (theRefId.RefKind) + { + case BRepGraph_RefId::Kind::Shell: + Standard_ProgramError_Raise_if(theStorage.IsOwned(BRepGraph_ShellRefId(theRefId)), + "BRepGraph::EditorView::Mut*(): owned reference"); + break; + case BRepGraph_RefId::Kind::Face: + Standard_ProgramError_Raise_if(theStorage.IsOwned(BRepGraph_FaceRefId(theRefId)), + "BRepGraph::EditorView::Mut*(): owned reference"); + break; + case BRepGraph_RefId::Kind::Wire: + Standard_ProgramError_Raise_if(theStorage.IsOwned(BRepGraph_WireRefId(theRefId)), + "BRepGraph::EditorView::Mut*(): owned reference"); + break; + case BRepGraph_RefId::Kind::Vertex: + Standard_ProgramError_Raise_if(theStorage.IsOwned(BRepGraph_VertexRefId(theRefId)), + "BRepGraph::EditorView::Mut*(): owned reference"); + break; + case BRepGraph_RefId::Kind::Solid: + Standard_ProgramError_Raise_if(theStorage.IsOwned(BRepGraph_SolidRefId(theRefId)), + "BRepGraph::EditorView::Mut*(): owned reference"); + break; + case BRepGraph_RefId::Kind::Child: + Standard_ProgramError_Raise_if(theStorage.IsOwned(BRepGraph_ChildRefId(theRefId)), + "BRepGraph::EditorView::Mut*(): owned reference"); + break; + case BRepGraph_RefId::Kind::Occurrence: + Standard_ProgramError_Raise_if(theStorage.IsOwned(BRepGraph_OccurrenceRefId(theRefId)), + "BRepGraph::EditorView::Mut*(): owned reference"); + break; + } } //================================================================================================== -static void validateMutableRepId(const BRepGraphInc_Storage& theStorage [[maybe_unused]], - const BRepGraph_RepId theRepId [[maybe_unused]]) -{ - Standard_ProgramError_Raise_if(!isActiveRep(theStorage, theRepId), - "BRepGraph::EditorView::Mut*(): invalid representation id"); -} - } // namespace //================================================================================================== @@ -270,8 +258,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::VertexOps::Mu const BRepGraph_VertexId theVertex) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theVertex)); - return BRepGraph_MutGuard(myGraph, + validateMutableNodeId(aStorage, BRepGraph_NodeId(theVertex), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theVertex)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeVertex(theVertex), theVertex); } @@ -282,42 +273,13 @@ BRepGraph_MutGuard BRepGraph::EditorView::EdgeOps::Mut( const BRepGraph_EdgeId theEdge) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theEdge)); - return BRepGraph_MutGuard(myGraph, &aStorage.ChangeEdge(theEdge), theEdge); -} - -//================================================================================================== - -bool BRepGraph::EditorView::EdgeOps::IsSeamOnFace(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace) const -{ - if (!theEdge.IsValid() || !theFace.IsValid()) - { - return false; - } - bool hasOrientation = false; - TopAbs_Orientation anOrientation = TopAbs_FORWARD; - for (BRepGraph_CoEdgesOfEdge anIt(*myGraph, myGraph->Topo().Edges().CoEdges(theEdge)); - anIt.More(); - anIt.Next()) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = anIt.Definition(); - if (aCoEdge.FaceDefId != theFace) - { - continue; - } - if (!hasOrientation) - { - anOrientation = aCoEdge.Orientation; - hasOrientation = true; - continue; - } - if (aCoEdge.Orientation != anOrientation) - { - return true; - } - } - return false; + validateMutableNodeId(aStorage, BRepGraph_NodeId(theEdge), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theEdge)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, + &aStorage.ChangeEdge(theEdge), + theEdge); } //================================================================================================== @@ -326,8 +288,13 @@ BRepGraph_MutGuard BRepGraph::EditorView::WireOps::Mut( const BRepGraph_WireId theWire) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theWire)); - return BRepGraph_MutGuard(myGraph, &aStorage.ChangeWire(theWire), theWire); + validateMutableNodeId(aStorage, BRepGraph_NodeId(theWire), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theWire)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, + &aStorage.ChangeWire(theWire), + theWire); } //================================================================================================== @@ -336,8 +303,13 @@ BRepGraph_MutGuard BRepGraph::EditorView::FaceOps::Mut( const BRepGraph_FaceId theFace) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theFace)); - return BRepGraph_MutGuard(myGraph, &aStorage.ChangeFace(theFace), theFace); + validateMutableNodeId(aStorage, BRepGraph_NodeId(theFace), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theFace)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, + &aStorage.ChangeFace(theFace), + theFace); } //================================================================================================== @@ -346,8 +318,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::ShellOps::Mut( const BRepGraph_ShellId theShell) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theShell)); - return BRepGraph_MutGuard(myGraph, + validateMutableNodeId(aStorage, BRepGraph_NodeId(theShell), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theShell)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeShell(theShell), theShell); } @@ -358,8 +333,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::SolidOps::Mut( const BRepGraph_SolidId theSolid) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theSolid)); - return BRepGraph_MutGuard(myGraph, + validateMutableNodeId(aStorage, BRepGraph_NodeId(theSolid), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theSolid)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeSolid(theSolid), theSolid); } @@ -370,8 +348,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::CompoundOps const BRepGraph_CompoundId theCompound) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theCompound)); - return BRepGraph_MutGuard(myGraph, + validateMutableNodeId(aStorage, BRepGraph_NodeId(theCompound), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theCompound)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeCompound(theCompound), theCompound); } @@ -382,8 +363,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::CompSolidO const BRepGraph_CompSolidId theCompSolid) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theCompSolid)); - return BRepGraph_MutGuard(myGraph, + validateMutableNodeId(aStorage, BRepGraph_NodeId(theCompSolid), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theCompSolid)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeCompSolid(theCompSolid), theCompSolid); } @@ -394,8 +378,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::CoEdgeOps::Mu const BRepGraph_CoEdgeId theCoEdge) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theCoEdge)); - return BRepGraph_MutGuard(myGraph, + validateMutableNodeId(aStorage, BRepGraph_NodeId(theCoEdge), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theCoEdge)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeCoEdge(theCoEdge), theCoEdge); } @@ -406,8 +393,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::ProductOps:: const BRepGraph_ProductId theProduct) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theProduct)); - return BRepGraph_MutGuard(myGraph, + validateMutableNodeId(aStorage, BRepGraph_NodeId(theProduct), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theProduct)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeProduct(theProduct), theProduct); } @@ -418,8 +408,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::Occurrenc const BRepGraph_OccurrenceId theOccurrence) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableNodeId(aStorage, BRepGraph_NodeId(theOccurrence)); - return BRepGraph_MutGuard(myGraph, + validateMutableNodeId(aStorage, BRepGraph_NodeId(theOccurrence), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theOccurrence)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeOccurrence(theOccurrence), theOccurrence); } @@ -430,8 +423,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::VertexOps::Mu const BRepGraph_VertexRefId theVertexRef) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRefId(aStorage, BRepGraph_RefId(theVertexRef)); - return BRepGraph_MutGuard(myGraph, + validateMutableRefId(aStorage, BRepGraph_RefId(theVertexRef), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theVertexRef)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeVertexRef(theVertexRef), theVertexRef); } @@ -442,8 +438,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::ShellOps::MutR const BRepGraph_ShellRefId theShellRef) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRefId(aStorage, BRepGraph_RefId(theShellRef)); - return BRepGraph_MutGuard(myGraph, + validateMutableRefId(aStorage, BRepGraph_RefId(theShellRef), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theShellRef)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeShellRef(theShellRef), theShellRef); } @@ -454,8 +453,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::FaceOps::MutRef const BRepGraph_FaceRefId theFaceRef) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRefId(aStorage, BRepGraph_RefId(theFaceRef)); - return BRepGraph_MutGuard(myGraph, + validateMutableRefId(aStorage, BRepGraph_RefId(theFaceRef), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theFaceRef)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeFaceRef(theFaceRef), theFaceRef); } @@ -466,32 +468,26 @@ BRepGraph_MutGuard BRepGraph::EditorView::WireOps::MutRef const BRepGraph_WireRefId theWireRef) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRefId(aStorage, BRepGraph_RefId(theWireRef)); - return BRepGraph_MutGuard(myGraph, + validateMutableRefId(aStorage, BRepGraph_RefId(theWireRef), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theWireRef)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeWireRef(theWireRef), theWireRef); } //================================================================================================== -BRepGraph_MutGuard BRepGraph::EditorView::CoEdgeOps::MutRef( - const BRepGraph_CoEdgeRefId theCoEdgeRef) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRefId(aStorage, BRepGraph_RefId(theCoEdgeRef)); - return BRepGraph_MutGuard(myGraph, - &aStorage.ChangeCoEdgeRef(theCoEdgeRef), - theCoEdgeRef); -} - -//================================================================================================== - BRepGraph_MutGuard BRepGraph::EditorView::SolidOps::MutRef( const BRepGraph_SolidRefId theSolidRef) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRefId(aStorage, BRepGraph_RefId(theSolidRef)); - return BRepGraph_MutGuard(myGraph, + validateMutableRefId(aStorage, BRepGraph_RefId(theSolidRef), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theSolidRef)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeSolidRef(theSolidRef), theSolidRef); } @@ -502,8 +498,11 @@ BRepGraph_MutGuard BRepGraph::EditorView::GenOps::MutChi const BRepGraph_ChildRefId theChildRef) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRefId(aStorage, BRepGraph_RefId(theChildRef)); - return BRepGraph_MutGuard(myGraph, + validateMutableRefId(aStorage, BRepGraph_RefId(theChildRef), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theChildRef)), + "BRepGraph::EditorView::Mut*(): guard already active"); + return BRepGraph_MutGuard(*myGraph, + aStorage, &aStorage.ChangeChildRef(theChildRef), theChildRef); } @@ -514,95 +513,12 @@ BRepGraph_MutGuard BRepGraph::EditorView::Occurrenc const BRepGraph_OccurrenceRefId theOccurrenceRef) { BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRefId(aStorage, BRepGraph_RefId(theOccurrenceRef)); + validateMutableRefId(aStorage, BRepGraph_RefId(theOccurrenceRef), *myGraph); + Standard_ProgramError_Raise_if(aStorage.IsGuarded(BRepGraph_ItemId(theOccurrenceRef)), + "BRepGraph::EditorView::Mut*(): guard already active"); return BRepGraph_MutGuard( - myGraph, + *myGraph, + aStorage, &aStorage.ChangeOccurrenceRef(theOccurrenceRef), theOccurrenceRef); } - -//================================================================================================== - -BRepGraph_MutGuard BRepGraph::EditorView::RepOps::MutSurface( - const BRepGraph_SurfaceRepId theSurface) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRepId(aStorage, BRepGraph_RepId(theSurface)); - return BRepGraph_MutGuard(myGraph, - &aStorage.ChangeSurfaceRep(theSurface), - theSurface); -} - -//================================================================================================== - -BRepGraph_MutGuard BRepGraph::EditorView::RepOps::MutCurve3D( - const BRepGraph_Curve3DRepId theCurve) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRepId(aStorage, BRepGraph_RepId(theCurve)); - return BRepGraph_MutGuard(myGraph, - &aStorage.ChangeCurve3DRep(theCurve), - theCurve); -} - -//================================================================================================== - -BRepGraph_MutGuard BRepGraph::EditorView::RepOps::MutCurve2D( - const BRepGraph_Curve2DRepId theCurve) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRepId(aStorage, BRepGraph_RepId(theCurve)); - return BRepGraph_MutGuard(myGraph, - &aStorage.ChangeCurve2DRep(theCurve), - theCurve); -} - -//================================================================================================== - -BRepGraph_MutGuard BRepGraph::EditorView::RepOps::MutTriangulation( - const BRepGraph_TriangulationRepId theTriangulation) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRepId(aStorage, BRepGraph_RepId(theTriangulation)); - return BRepGraph_MutGuard( - myGraph, - &aStorage.ChangeTriangulationRep(theTriangulation), - theTriangulation); -} - -//================================================================================================== - -BRepGraph_MutGuard BRepGraph::EditorView::RepOps::MutPolygon3D( - const BRepGraph_Polygon3DRepId thePolygon) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRepId(aStorage, BRepGraph_RepId(thePolygon)); - return BRepGraph_MutGuard(myGraph, - &aStorage.ChangePolygon3DRep(thePolygon), - thePolygon); -} - -//================================================================================================== - -BRepGraph_MutGuard BRepGraph::EditorView::RepOps::MutPolygon2D( - const BRepGraph_Polygon2DRepId thePolygon) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRepId(aStorage, BRepGraph_RepId(thePolygon)); - return BRepGraph_MutGuard(myGraph, - &aStorage.ChangePolygon2DRep(thePolygon), - thePolygon); -} - -//================================================================================================== - -BRepGraph_MutGuard BRepGraph::EditorView::RepOps::MutPolygonOnTri( - const BRepGraph_PolygonOnTriRepId thePolygon) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - validateMutableRepId(aStorage, BRepGraph_RepId(thePolygon)); - return BRepGraph_MutGuard( - myGraph, - &aStorage.ChangePolygonOnTriRep(thePolygon), - thePolygon); -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Setters.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Setters.cxx index aadff10ef4..c48f5754c4 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Setters.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_EditorView_Setters.cxx @@ -13,8 +13,8 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -27,36 +27,126 @@ namespace { - -//! True if no other active coedge of theEdge references theFace. -bool isLastCoEdgeOfEdgeOnFace(const BRepGraphInc_Storage& theStorage, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace, - const BRepGraph_CoEdgeId theExcluding) +bool isValidOccurrenceChildKind(const BRepGraph_NodeId::Kind theNodeKind) { - const NCollection_DynamicArray* aCoEdges = - theStorage.ReverseIndex().CoEdgesOfEdge(theEdge); - if (aCoEdges == nullptr) + return theNodeKind == BRepGraph_NodeId::Kind::Product + || BRepGraph_NodeId::IsTopologyKind(theNodeKind); +} + +bool isActiveOccurrenceChild(const BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theChild) +{ + if (!theChild.IsValid() || !isValidOccurrenceChildKind(theChild.NodeKind)) { - return true; + return false; } - for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCoEdges) + + switch (theChild.NodeKind) { - if (aCoEdgeId == theExcluding) - { - continue; - } - if (!aCoEdgeId.IsValid(theStorage.NbCoEdges())) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aCoEdgeId); - if (!aCoEdge.IsRemoved && aCoEdge.FaceDefId == theFace) - { + case BRepGraph_NodeId::Kind::Vertex: + return BRepGraph_VertexId(theChild).IsValid(theStorage.NbVertices()) + && !theStorage.IsRemoved(BRepGraph_VertexId(theChild)); + case BRepGraph_NodeId::Kind::Edge: + return BRepGraph_EdgeId(theChild).IsValid(theStorage.NbEdges()) + && !theStorage.IsRemoved(BRepGraph_EdgeId(theChild)); + case BRepGraph_NodeId::Kind::CoEdge: + return BRepGraph_CoEdgeId(theChild).IsValid(theStorage.NbCoEdges()) + && !theStorage.IsRemoved(BRepGraph_CoEdgeId(theChild)); + case BRepGraph_NodeId::Kind::Wire: + return BRepGraph_WireId(theChild).IsValid(theStorage.NbWires()) + && !theStorage.IsRemoved(BRepGraph_WireId(theChild)); + case BRepGraph_NodeId::Kind::Face: + return BRepGraph_FaceId(theChild).IsValid(theStorage.NbFaces()) + && !theStorage.IsRemoved(BRepGraph_FaceId(theChild)); + case BRepGraph_NodeId::Kind::Shell: + return BRepGraph_ShellId(theChild).IsValid(theStorage.NbShells()) + && !theStorage.IsRemoved(BRepGraph_ShellId(theChild)); + case BRepGraph_NodeId::Kind::Solid: + return BRepGraph_SolidId(theChild).IsValid(theStorage.NbSolids()) + && !theStorage.IsRemoved(BRepGraph_SolidId(theChild)); + case BRepGraph_NodeId::Kind::Compound: + return BRepGraph_CompoundId(theChild).IsValid(theStorage.NbCompounds()) + && !theStorage.IsRemoved(BRepGraph_CompoundId(theChild)); + case BRepGraph_NodeId::Kind::CompSolid: + return BRepGraph_CompSolidId(theChild).IsValid(theStorage.NbCompSolids()) + && !theStorage.IsRemoved(BRepGraph_CompSolidId(theChild)); + case BRepGraph_NodeId::Kind::Product: + return BRepGraph_ProductId(theChild).IsValid(theStorage.NbProducts()) + && !theStorage.IsRemoved(BRepGraph_ProductId(theChild)); + case BRepGraph_NodeId::Kind::Occurrence: return false; + } + + return false; +} + +bool isActiveOccurrence(const BRepGraphInc_Storage& theStorage, + const BRepGraph_OccurrenceId theOccurrence) +{ + return theOccurrence.IsValid(theStorage.NbOccurrences()) && !theStorage.IsRemoved(theOccurrence); +} + +void clearCoEdgeFaceScopedRepresentations(BRepGraphInc_Storage& theStorage, + BRepGraphInc::CoEdgeDef& theCoEdge) +{ + if (theCoEdge.Curve2DRepId.IsValid(theStorage.NbCoEdgeCurves2D())) + { + theStorage.MarkRemoved(theCoEdge.Curve2DRepId); + } + theCoEdge.Curve2DRepId = BRepGraph_CoEdgeCurve2DRepId(); + + if (theCoEdge.Polygon2DRepId.IsValid(theStorage.NbCoEdgePolygons2D())) + { + theStorage.MarkRemoved(theCoEdge.Polygon2DRepId); + } + theCoEdge.Polygon2DRepId = BRepGraph_CoEdgePolygon2DRepId(); + + if (theCoEdge.PolygonOnTriRepId.IsValid(theStorage.NbCoEdgePolygonsOnTri())) + { + theStorage.MarkRemoved(theCoEdge.PolygonOnTriRepId); + } + theCoEdge.PolygonOnTriRepId = BRepGraph_CoEdgePolygonOnTriRepId(); +} + +void rebindOccurrenceChild(BRepGraphInc_Storage& theStorage, + const BRepGraph_OccurrenceId theOccurrence, + const BRepGraph_NodeId theOldChild, + const BRepGraph_NodeId theNewChild) +{ + theStorage.RebindOccurrenceChild(theOccurrence, theOldChild, theNewChild); +} + +bool occurrenceHasOtherActiveRef(const BRepGraphInc_Storage& theStorage, + const BRepGraph_OccurrenceId theOccurrence, + const BRepGraph_OccurrenceRefId theExcludingRef) +{ + if (!theOccurrence.IsValid(theStorage.NbOccurrences())) + { + return false; + } + for (BRepGraph_OccurrenceRefId aRefId = BRepGraph_OccurrenceRefId::Start(); + aRefId.IsValid(theStorage.NbOccurrenceRefs()); + ++aRefId) + { + if (aRefId == theExcludingRef) + { + continue; + } + const BRepGraphInc::OccurrenceRef& aRef = theStorage.OccurrenceRef(aRefId); + if (!theStorage.IsRemoved(aRefId) && aRef.ChildOccurrenceId == theOccurrence) + { + return true; } } - return true; + return false; +} + +void rebindOccurrenceRefParentProduct(BRepGraphInc_Storage& theStorage, + const BRepGraph_OccurrenceRefId theOccurrenceRef, + const BRepGraph_OccurrenceId theOldOccurrence, + const BRepGraph_OccurrenceId theNewOccurrence) +{ + theStorage.RebindOccurrenceRef(theOccurrenceRef, theOldOccurrence, theNewOccurrence); } } // namespace @@ -66,6 +156,7 @@ bool isLastCoEdgeOfEdgeOnFace(const BRepGraphInc_Storage& theStorage, void BRepGraph::EditorView::VertexOps::SetPoint(const BRepGraph_VertexId theVertex, const gp_Pnt& thePoint) { + myGraph->Editor().requireUnlocked(theVertex, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeVertex(theVertex).Point = thePoint; myGraph->markModified(theVertex); } @@ -83,6 +174,7 @@ void BRepGraph::EditorView::VertexOps::SetPoint(BRepGraph_MutGuardEditor().requireUnlocked(theEdge, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeEdge(theEdge).Tolerance = theTolerance; myGraph->markModified(theEdge); } @@ -97,31 +189,19 @@ void BRepGraph::EditorView::EdgeOps::SetTolerance(BRepGraph_MutGuard aLayer = - myGraph->LayerRegistry().FindLayer(); - if (aLayer.IsNull()) - { - return false; - } - aLayer->SetRegularity(theEdge, theFace1, theFace2, theContinuity); - myGraph->markModified(theEdge); - return true; -} - -//================================================================================================= - void BRepGraph::EditorView::CoEdgeOps::SetParamRange(const BRepGraph_CoEdgeId theCoEdge, double theFirst, double theLast) { - BRepGraphInc::CoEdgeDef& aDef = myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge); - aDef.ParamFirst = theFirst; - aDef.ParamLast = theLast; + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aDef = aStorage.ChangeCoEdge(theCoEdge); + if (aDef.Curve2DRepId.IsValid() && aDef.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D())) + { + BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.ChangeCoEdgeCurve2DRep(aDef.Curve2DRepId); + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + } myGraph->markModified(theCoEdge); } @@ -132,24 +212,14 @@ void BRepGraph::EditorView::CoEdgeOps::SetParamRange( double theFirst, double theLast) { - theMut.Internal().ParamFirst = theFirst; - theMut.Internal().ParamLast = theLast; -} - -//================================================================================================= - -void BRepGraph::EditorView::WireOps::SetIsClosed(const BRepGraph_WireId theWire, bool theIsClosed) -{ - myGraph->myData->myIncStorage.ChangeWire(theWire).IsClosed = theIsClosed; - myGraph->markModified(theWire); -} - -//================================================================================================= - -void BRepGraph::EditorView::WireOps::SetIsClosed(BRepGraph_MutGuard& theMut, - bool theIsClosed) -{ - theMut.Internal().IsClosed = theIsClosed; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aDef = theMut.Internal(); + if (aDef.Curve2DRepId.IsValid() && aDef.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D())) + { + BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.ChangeCoEdgeCurve2DRep(aDef.Curve2DRepId); + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + } } //================================================================================================= @@ -157,6 +227,7 @@ void BRepGraph::EditorView::WireOps::SetIsClosed(BRepGraph_MutGuardEditor().requireUnlocked(theFace, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeFace(theFace).Tolerance = theTolerance; myGraph->markModified(theFace); } @@ -171,36 +242,11 @@ void BRepGraph::EditorView::FaceOps::SetTolerance(BRepGraph_MutGuardmyData->myIncStorage.ChangeFace(theFace).NaturalRestriction = theNaturalRestriction; - myGraph->markModified(theFace); -} - -//================================================================================================= - -void BRepGraph::EditorView::FaceOps::SetNaturalRestriction( - BRepGraph_MutGuard& theMut, - bool theNaturalRestriction) -{ - theMut.Internal().NaturalRestriction = theNaturalRestriction; -} - -//================================================================================================= - -void BRepGraph::EditorView::FaceOps::SetTriangulationRep(const BRepGraph_FaceId theFace, - const BRepGraph_TriangulationRepId theRep) -{ - myGraph->myData->myIncStorage.ChangeFace(theFace).TriangulationRepId = theRep; - myGraph->markModified(theFace); -} - -//================================================================================================= - -void BRepGraph::EditorView::FaceOps::SetRefOrientation(const BRepGraph_FaceRefId theFaceRef, - TopAbs_Orientation theOrientation) +void BRepGraph::EditorView::FaceOps::SetRefOrientation( + const BRepGraph_FaceRefId theFaceRef, + BRepGraphInc::ParityOrientation theOrientation) { + myGraph->Editor().requireUnlocked(theFaceRef, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeFaceRef(theFaceRef).Orientation = theOrientation; myGraph->markRefModified(theFaceRef); } @@ -209,35 +255,18 @@ void BRepGraph::EditorView::FaceOps::SetRefOrientation(const BRepGraph_FaceRefId void BRepGraph::EditorView::FaceOps::SetRefOrientation( BRepGraph_MutGuard& theMut, - TopAbs_Orientation theOrientation) + BRepGraphInc::ParityOrientation theOrientation) { theMut.Internal().Orientation = theOrientation; } //================================================================================================= -void BRepGraph::EditorView::FaceOps::SetRefLocalLocation(const BRepGraph_FaceRefId theFaceRef, - const TopLoc_Location& theLoc) -{ - myGraph->myData->myIncStorage.ChangeFaceRef(theFaceRef).LocalLocation = theLoc; - myGraph->markRefModified(theFaceRef); -} - -//================================================================================================= - -void BRepGraph::EditorView::FaceOps::SetRefLocalLocation( - BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc) -{ - theMut.Internal().LocalLocation = theLoc; -} - -//================================================================================================= - void BRepGraph::EditorView::OccurrenceOps::SetRefLocalLocation( const BRepGraph_OccurrenceRefId theOccurrenceRef, const TopLoc_Location& theLoc) { + myGraph->Editor().requireUnlocked(theOccurrenceRef, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeOccurrenceRef(theOccurrenceRef).LocalLocation = theLoc; myGraph->markRefModified(theOccurrenceRef); } @@ -253,99 +282,10 @@ void BRepGraph::EditorView::OccurrenceOps::SetRefLocalLocation( //================================================================================================= -void BRepGraph::EditorView::VertexOps::SetRefLocalLocation(const BRepGraph_VertexRefId theVertexRef, - const TopLoc_Location& theLoc) -{ - myGraph->myData->myIncStorage.ChangeVertexRef(theVertexRef).LocalLocation = theLoc; - myGraph->markRefModified(theVertexRef); -} - -//================================================================================================= - -void BRepGraph::EditorView::VertexOps::SetRefLocalLocation( - BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc) -{ - theMut.Internal().LocalLocation = theLoc; -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetRefLocalLocation(const BRepGraph_CoEdgeRefId theCoEdgeRef, - const TopLoc_Location& theLoc) -{ - myGraph->myData->myIncStorage.ChangeCoEdgeRef(theCoEdgeRef).LocalLocation = theLoc; - myGraph->markRefModified(theCoEdgeRef); -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetRefLocalLocation( - BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc) -{ - theMut.Internal().LocalLocation = theLoc; -} - -//================================================================================================= - -void BRepGraph::EditorView::WireOps::SetRefLocalLocation(const BRepGraph_WireRefId theWireRef, - const TopLoc_Location& theLoc) -{ - myGraph->myData->myIncStorage.ChangeWireRef(theWireRef).LocalLocation = theLoc; - myGraph->markRefModified(theWireRef); -} - -//================================================================================================= - -void BRepGraph::EditorView::WireOps::SetRefLocalLocation( - BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc) -{ - theMut.Internal().LocalLocation = theLoc; -} - -//================================================================================================= - -void BRepGraph::EditorView::ShellOps::SetRefLocalLocation(const BRepGraph_ShellRefId theShellRef, - const TopLoc_Location& theLoc) -{ - myGraph->myData->myIncStorage.ChangeShellRef(theShellRef).LocalLocation = theLoc; - myGraph->markRefModified(theShellRef); -} - -//================================================================================================= - -void BRepGraph::EditorView::ShellOps::SetRefLocalLocation( - BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc) -{ - theMut.Internal().LocalLocation = theLoc; -} - -//================================================================================================= - -void BRepGraph::EditorView::SolidOps::SetRefLocalLocation(const BRepGraph_SolidRefId theSolidRef, - const TopLoc_Location& theLoc) -{ - myGraph->myData->myIncStorage.ChangeSolidRef(theSolidRef).LocalLocation = theLoc; - myGraph->markRefModified(theSolidRef); -} - -//================================================================================================= - -void BRepGraph::EditorView::SolidOps::SetRefLocalLocation( - BRepGraph_MutGuard& theMut, - const TopLoc_Location& theLoc) -{ - theMut.Internal().LocalLocation = theLoc; -} - -//================================================================================================= - void BRepGraph::EditorView::GenOps::SetChildRefLocalLocation(const BRepGraph_ChildRefId theChildRef, const TopLoc_Location& theLoc) { + myGraph->Editor().requireUnlocked(theChildRef, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeChildRef(theChildRef).LocalLocation = theLoc; myGraph->markRefModified(theChildRef); } @@ -365,9 +305,15 @@ void BRepGraph::EditorView::EdgeOps::SetParamRange(const BRepGraph_EdgeId theEdg const double theFirst, const double theLast) { - BRepGraphInc::EdgeDef& aDef = myGraph->myData->myIncStorage.ChangeEdge(theEdge); - aDef.ParamFirst = theFirst; - aDef.ParamLast = theLast; + myGraph->Editor().requireUnlocked(theEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::EdgeDef& aDef = aStorage.ChangeEdge(theEdge); + if (aDef.Curve3DRepId.IsValid() && aDef.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D())) + { + BRepGraphInc::EdgeCurve3DRep& aUse = aStorage.ChangeEdgeCurve3DRep(aDef.Curve3DRepId); + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + } myGraph->markModified(theEdge); } @@ -378,131 +324,23 @@ void BRepGraph::EditorView::EdgeOps::SetParamRange( const double theFirst, const double theLast) { - theMut.Internal().ParamFirst = theFirst; - theMut.Internal().ParamLast = theLast; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::EdgeDef& aDef = theMut.Internal(); + if (aDef.Curve3DRepId.IsValid() && aDef.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D())) + { + BRepGraphInc::EdgeCurve3DRep& aUse = aStorage.ChangeEdgeCurve3DRep(aDef.Curve3DRepId); + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + } } //================================================================================================= -void BRepGraph::EditorView::EdgeOps::SetSameParameter(const BRepGraph_EdgeId theEdge, - const bool theSameParameter) -{ - myGraph->myData->myIncStorage.ChangeEdge(theEdge).SameParameter = theSameParameter; - myGraph->markModified(theEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetSameParameter( - BRepGraph_MutGuard& theMut, - const bool theSameParameter) -{ - theMut.Internal().SameParameter = theSameParameter; -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetSameRange(const BRepGraph_EdgeId theEdge, - const bool theSameRange) -{ - myGraph->myData->myIncStorage.ChangeEdge(theEdge).SameRange = theSameRange; - myGraph->markModified(theEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetSameRange(BRepGraph_MutGuard& theMut, - const bool theSameRange) -{ - theMut.Internal().SameRange = theSameRange; -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetDegenerate(const BRepGraph_EdgeId theEdge, - const bool theIsDegenerate) -{ - myGraph->myData->myIncStorage.ChangeEdge(theEdge).IsDegenerate = theIsDegenerate; - myGraph->markModified(theEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetDegenerate( - BRepGraph_MutGuard& theMut, - const bool theIsDegenerate) -{ - theMut.Internal().IsDegenerate = theIsDegenerate; -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetCurve3DRepId(const BRepGraph_EdgeId theEdge, - const BRepGraph_Curve3DRepId theRep) -{ - myGraph->myData->myIncStorage.ChangeEdge(theEdge).Curve3DRepId = theRep; - myGraph->markModified(theEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetCurve3DRepId( - BRepGraph_MutGuard& theMut, - const BRepGraph_Curve3DRepId theRep) -{ - theMut.Internal().Curve3DRepId = theRep; -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetPolygon3DRepId(const BRepGraph_EdgeId theEdge, - const BRepGraph_Polygon3DRepId theRep) -{ - myGraph->myData->myIncStorage.ChangeEdge(theEdge).Polygon3DRepId = theRep; - myGraph->markModified(theEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetPolygon3DRepId( - BRepGraph_MutGuard& theMut, - const BRepGraph_Polygon3DRepId theRep) -{ - theMut.Internal().Polygon3DRepId = theRep; -} - -//================================================================================================= - -void BRepGraph::EditorView::FaceOps::SetTriangulationRep( - BRepGraph_MutGuard& theMut, - const BRepGraph_TriangulationRepId theRep) -{ - theMut.Internal().TriangulationRepId = theRep; -} - -//================================================================================================= - -void BRepGraph::EditorView::FaceOps::SetSurfaceRepId(const BRepGraph_FaceId theFace, - const BRepGraph_SurfaceRepId theRep) -{ - myGraph->myData->myIncStorage.ChangeFace(theFace).SurfaceRepId = theRep; - myGraph->markModified(theFace); -} - -//================================================================================================= - -void BRepGraph::EditorView::FaceOps::SetSurfaceRepId( - BRepGraph_MutGuard& theMut, - const BRepGraph_SurfaceRepId theRep) -{ - theMut.Internal().SurfaceRepId = theRep; -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetOrientation(const BRepGraph_CoEdgeId theCoEdge, - const TopAbs_Orientation theOrientation) +void BRepGraph::EditorView::CoEdgeOps::SetOrientation( + const BRepGraph_CoEdgeId theCoEdge, + const BRepGraphInc::ParityOrientation theOrientation) { + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).Orientation = theOrientation; myGraph->markModified(theCoEdge); } @@ -511,136 +349,53 @@ void BRepGraph::EditorView::CoEdgeOps::SetOrientation(const BRepGraph_CoEdgeId t void BRepGraph::EditorView::CoEdgeOps::SetOrientation( BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation) + const BRepGraphInc::ParityOrientation theOrientation) { theMut.Internal().Orientation = theOrientation; } //================================================================================================= -void BRepGraph::EditorView::CoEdgeOps::SetUVBox(const BRepGraph_CoEdgeId theCoEdge, - const gp_Pnt2d& theUV1, - const gp_Pnt2d& theUV2) +void BRepGraph::EditorView::CoEdgeOps::ResetPCurveBinding(const BRepGraph_CoEdgeId theCoEdge) { - BRepGraphInc::CoEdgeDef& aDef = myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge); - aDef.UV1 = theUV1; - aDef.UV2 = theUV2; + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aDef = aStorage.ChangeCoEdge(theCoEdge); + const BRepGraph_CoEdgeCurve2DRepId anOldId = aDef.Curve2DRepId; + aDef.Curve2DRepId = BRepGraph_CoEdgeCurve2DRepId(); + if (anOldId.IsValid() && anOldId.IsValid(aStorage.NbCoEdgeCurves2D())) + { + BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.ChangeCoEdgeCurve2DRep(anOldId); + aUse.ParamFirst = 0.0; + aUse.ParamLast = 0.0; + } myGraph->markModified(theCoEdge); } //================================================================================================= -void BRepGraph::EditorView::CoEdgeOps::SetUVBox(BRepGraph_MutGuard& theMut, - const gp_Pnt2d& theUV1, - const gp_Pnt2d& theUV2) -{ - theMut.Internal().UV1 = theUV1; - theMut.Internal().UV2 = theUV2; -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetCurve2DRepId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_Curve2DRepId theRep) -{ - myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).Curve2DRepId = theRep; - myGraph->markModified(theCoEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetCurve2DRepId( - BRepGraph_MutGuard& theMut, - const BRepGraph_Curve2DRepId theRep) -{ - theMut.Internal().Curve2DRepId = theRep; -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetPolygon2DRepId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_Polygon2DRepId theRep) -{ - myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).Polygon2DRepId = theRep; - myGraph->markModified(theCoEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetPolygon2DRepId( - BRepGraph_MutGuard& theMut, - const BRepGraph_Polygon2DRepId theRep) -{ - theMut.Internal().Polygon2DRepId = theRep; -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetPolygonOnTriRepId( - const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_PolygonOnTriRepId theRep) -{ - myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge).PolygonOnTriRepId = theRep; - myGraph->markModified(theCoEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetPolygonOnTriRepId( - BRepGraph_MutGuard& theMut, - const BRepGraph_PolygonOnTriRepId theRep) -{ - theMut.Internal().PolygonOnTriRepId = theRep; -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::ClearPCurveBinding(const BRepGraph_CoEdgeId theCoEdge) -{ - BRepGraphInc::CoEdgeDef& aDef = myGraph->myData->myIncStorage.ChangeCoEdge(theCoEdge); - aDef.Curve2DRepId = BRepGraph_Curve2DRepId(); - aDef.ParamFirst = 0.0; - aDef.ParamLast = 0.0; - aDef.UV1 = gp_Pnt2d(); - aDef.UV2 = gp_Pnt2d(); - myGraph->markModified(theCoEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::ClearPCurveBinding( +void BRepGraph::EditorView::CoEdgeOps::ResetPCurveBinding( BRepGraph_MutGuard& theMut) { - theMut.Internal().Curve2DRepId = BRepGraph_Curve2DRepId(); - theMut.Internal().ParamFirst = 0.0; - theMut.Internal().ParamLast = 0.0; - theMut.Internal().UV1 = gp_Pnt2d(); - theMut.Internal().UV2 = gp_Pnt2d(); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aDef = theMut.Internal(); + const BRepGraph_CoEdgeCurve2DRepId anOldId = aDef.Curve2DRepId; + aDef.Curve2DRepId = BRepGraph_CoEdgeCurve2DRepId(); + if (anOldId.IsValid() && anOldId.IsValid(aStorage.NbCoEdgeCurves2D())) + { + BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.ChangeCoEdgeCurve2DRep(anOldId); + aUse.ParamFirst = 0.0; + aUse.ParamLast = 0.0; + } } //================================================================================================= -void BRepGraph::EditorView::WireOps::SetRefIsOuter(const BRepGraph_WireRefId theWireRef, - const bool theIsOuter) -{ - myGraph->myData->myIncStorage.ChangeWireRef(theWireRef).IsOuter = theIsOuter; - myGraph->markRefModified(theWireRef); -} - -//================================================================================================= - -void BRepGraph::EditorView::WireOps::SetRefIsOuter( - BRepGraph_MutGuard& theMut, - const bool theIsOuter) -{ - theMut.Internal().IsOuter = theIsOuter; -} - -//================================================================================================= - -void BRepGraph::EditorView::WireOps::SetRefOrientation(const BRepGraph_WireRefId theWireRef, - const TopAbs_Orientation theOrientation) +void BRepGraph::EditorView::WireOps::SetRefOrientation( + const BRepGraph_WireRefId theWireRef, + const BRepGraphInc::ParityOrientation theOrientation) { + myGraph->Editor().requireUnlocked(theWireRef, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeWireRef(theWireRef).Orientation = theOrientation; myGraph->markRefModified(theWireRef); } @@ -649,16 +404,18 @@ void BRepGraph::EditorView::WireOps::SetRefOrientation(const BRepGraph_WireRefId void BRepGraph::EditorView::WireOps::SetRefOrientation( BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation) + const BRepGraphInc::ParityOrientation theOrientation) { theMut.Internal().Orientation = theOrientation; } //================================================================================================= -void BRepGraph::EditorView::ShellOps::SetRefOrientation(const BRepGraph_ShellRefId theShellRef, - const TopAbs_Orientation theOrientation) +void BRepGraph::EditorView::ShellOps::SetRefOrientation( + const BRepGraph_ShellRefId theShellRef, + const BRepGraphInc::ParityOrientation theOrientation) { + myGraph->Editor().requireUnlocked(theShellRef, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeShellRef(theShellRef).Orientation = theOrientation; myGraph->markRefModified(theShellRef); } @@ -667,34 +424,17 @@ void BRepGraph::EditorView::ShellOps::SetRefOrientation(const BRepGraph_ShellRef void BRepGraph::EditorView::ShellOps::SetRefOrientation( BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation) + const BRepGraphInc::ParityOrientation theOrientation) { theMut.Internal().Orientation = theOrientation; } //================================================================================================= -void BRepGraph::EditorView::ShellOps::SetIsClosed(const BRepGraph_ShellId theShell, - const bool theIsClosed) -{ - myGraph->myData->myIncStorage.ChangeShell(theShell).IsClosed = theIsClosed; - myGraph->markModified(theShell); -} - -//================================================================================================= - -void BRepGraph::EditorView::ShellOps::SetIsClosed( - BRepGraph_MutGuard& theMut, - const bool theIsClosed) -{ - theMut.Internal().IsClosed = theIsClosed; -} - -//================================================================================================= - void BRepGraph::EditorView::VertexOps::SetTolerance(const BRepGraph_VertexId theVertex, const double theTolerance) { + myGraph->Editor().requireUnlocked(theVertex, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeVertex(theVertex).Tolerance = theTolerance; myGraph->markModified(theVertex); } @@ -710,9 +450,11 @@ void BRepGraph::EditorView::VertexOps::SetTolerance( //================================================================================================= -void BRepGraph::EditorView::VertexOps::SetRefOrientation(const BRepGraph_VertexRefId theVertexRef, - const TopAbs_Orientation theOrientation) +void BRepGraph::EditorView::VertexOps::SetRefOrientation( + const BRepGraph_VertexRefId theVertexRef, + const BRepGraphInc::ParityOrientation theOrientation) { + myGraph->Editor().requireUnlocked(theVertexRef, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeVertexRef(theVertexRef).Orientation = theOrientation; myGraph->markRefModified(theVertexRef); } @@ -721,33 +463,18 @@ void BRepGraph::EditorView::VertexOps::SetRefOrientation(const BRepGraph_VertexR void BRepGraph::EditorView::VertexOps::SetRefOrientation( BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation) + const BRepGraphInc::ParityOrientation theOrientation) { theMut.Internal().Orientation = theOrientation; } //================================================================================================= -void BRepGraph::EditorView::EdgeOps::SetIsClosed(const BRepGraph_EdgeId theEdge, - const bool theIsClosed) -{ - myGraph->myData->myIncStorage.ChangeEdge(theEdge).IsClosed = theIsClosed; - myGraph->markModified(theEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::EdgeOps::SetIsClosed(BRepGraph_MutGuard& theMut, - const bool theIsClosed) -{ - theMut.Internal().IsClosed = theIsClosed; -} - -//================================================================================================= - -void BRepGraph::EditorView::SolidOps::SetRefOrientation(const BRepGraph_SolidRefId theSolidRef, - const TopAbs_Orientation theOrientation) +void BRepGraph::EditorView::SolidOps::SetRefOrientation( + const BRepGraph_SolidRefId theSolidRef, + const BRepGraphInc::ParityOrientation theOrientation) { + myGraph->Editor().requireUnlocked(theSolidRef, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeSolidRef(theSolidRef).Orientation = theOrientation; myGraph->markRefModified(theSolidRef); } @@ -756,34 +483,65 @@ void BRepGraph::EditorView::SolidOps::SetRefOrientation(const BRepGraph_SolidRef void BRepGraph::EditorView::SolidOps::SetRefOrientation( BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation) + const BRepGraphInc::ParityOrientation theOrientation) { theMut.Internal().Orientation = theOrientation; } //================================================================================================= -void BRepGraph::EditorView::OccurrenceOps::SetChildDefId(const BRepGraph_OccurrenceId theOccurrence, - const BRepGraph_NodeId theChildDefId) +void BRepGraph::EditorView::OccurrenceOps::SetChildNodeId( + const BRepGraph_OccurrenceId theOccurrence, + const BRepGraph_NodeId theChildNodeId) { - myGraph->myData->myIncStorage.ChangeOccurrence(theOccurrence).ChildDefId = theChildDefId; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!isActiveOccurrence(aStorage, theOccurrence)) + { + return; + } + if (!isActiveOccurrenceChild(aStorage, theChildNodeId)) + { + return; + } + myGraph->Editor().requireUnlocked(theOccurrence, "BRepGraph::EditorView: locked item"); + BRepGraphInc::OccurrenceDef& anOcc = aStorage.ChangeOccurrence(theOccurrence); + if (anOcc.ChildNodeId == theChildNodeId) + { + return; + } + const BRepGraph_NodeId anOldChild = anOcc.ChildNodeId; + anOcc.ChildNodeId = theChildNodeId; + rebindOccurrenceChild(aStorage, theOccurrence, anOldChild, theChildNodeId); myGraph->markModified(theOccurrence); } //================================================================================================= -void BRepGraph::EditorView::OccurrenceOps::SetChildDefId( +void BRepGraph::EditorView::OccurrenceOps::SetChildNodeId( BRepGraph_MutGuard& theMut, - const BRepGraph_NodeId theChildDefId) + const BRepGraph_NodeId theChildNodeId) { - theMut.Internal().ChildDefId = theChildDefId; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!isActiveOccurrenceChild(aStorage, theChildNodeId)) + { + return; + } + if (theMut->ChildNodeId == theChildNodeId) + { + return; + } + const BRepGraph_NodeId anOldChild = theMut->ChildNodeId; + theMut.Internal().ChildNodeId = theChildNodeId; + rebindOccurrenceChild(aStorage, theMut.Id(), anOldChild, theChildNodeId); } //================================================================================================= -void BRepGraph::EditorView::GenOps::SetChildRefOrientation(const BRepGraph_ChildRefId theChildRef, - const TopAbs_Orientation theOrientation) +void BRepGraph::EditorView::GenOps::SetChildRefOrientation( + const BRepGraph_ChildRefId theChildRef, + const BRepGraphInc::ParityOrientation theOrientation) { + myGraph->Editor().requireUnlocked(theChildRef, "BRepGraph::EditorView: locked item"); myGraph->myData->myIncStorage.ChangeChildRef(theChildRef).Orientation = theOrientation; myGraph->markRefModified(theChildRef); } @@ -792,154 +550,13 @@ void BRepGraph::EditorView::GenOps::SetChildRefOrientation(const BRepGraph_Child void BRepGraph::EditorView::GenOps::SetChildRefOrientation( BRepGraph_MutGuard& theMut, - const TopAbs_Orientation theOrientation) + const BRepGraphInc::ParityOrientation theOrientation) { theMut.Internal().Orientation = theOrientation; } //================================================================================================= -void BRepGraph::EditorView::RepOps::SetSurface(const BRepGraph_SurfaceRepId theRep, - const occ::handle& theSurface) -{ - myGraph->myData->myIncStorage.ChangeSurfaceRep(theRep).Surface = theSurface; - myGraph->markRepModified(theRep); -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetSurface(BRepGraph_MutGuard& theMut, - const occ::handle& theSurface) -{ - theMut.Internal().Surface = theSurface; -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetCurve3D(const BRepGraph_Curve3DRepId theRep, - const occ::handle& theCurve) -{ - myGraph->myData->myIncStorage.ChangeCurve3DRep(theRep).Curve = theCurve; - myGraph->markRepModified(theRep); -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetCurve3D(BRepGraph_MutGuard& theMut, - const occ::handle& theCurve) -{ - theMut.Internal().Curve = theCurve; -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetCurve2D(const BRepGraph_Curve2DRepId theRep, - const occ::handle& theCurve) -{ - myGraph->myData->myIncStorage.ChangeCurve2DRep(theRep).Curve = theCurve; - myGraph->markRepModified(theRep); -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetCurve2D(BRepGraph_MutGuard& theMut, - const occ::handle& theCurve) -{ - theMut.Internal().Curve = theCurve; -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetTriangulation(const BRepGraph_TriangulationRepId theRep, - const occ::handle& theTri) -{ - myGraph->myData->myIncStorage.ChangeTriangulationRep(theRep).Triangulation = theTri; - myGraph->markRepModified(theRep); -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetTriangulation( - BRepGraph_MutGuard& theMut, - const occ::handle& theTri) -{ - theMut.Internal().Triangulation = theTri; -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetPolygon3D(const BRepGraph_Polygon3DRepId theRep, - const occ::handle& thePolygon) -{ - myGraph->myData->myIncStorage.ChangePolygon3DRep(theRep).Polygon = thePolygon; - myGraph->markRepModified(theRep); -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetPolygon3D( - BRepGraph_MutGuard& theMut, - const occ::handle& thePolygon) -{ - theMut.Internal().Polygon = thePolygon; -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetPolygon2D(const BRepGraph_Polygon2DRepId theRep, - const occ::handle& thePolygon) -{ - myGraph->myData->myIncStorage.ChangePolygon2DRep(theRep).Polygon = thePolygon; - myGraph->markRepModified(theRep); -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetPolygon2D( - BRepGraph_MutGuard& theMut, - const occ::handle& thePolygon) -{ - theMut.Internal().Polygon = thePolygon; -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetPolygonOnTri( - const BRepGraph_PolygonOnTriRepId theRep, - const occ::handle& thePolygon) -{ - myGraph->myData->myIncStorage.ChangePolygonOnTriRep(theRep).Polygon = thePolygon; - myGraph->markRepModified(theRep); -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetPolygonOnTri( - BRepGraph_MutGuard& theMut, - const occ::handle& thePolygon) -{ - theMut.Internal().Polygon = thePolygon; -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetPolygonOnTriTriangulationId( - const BRepGraph_PolygonOnTriRepId theRep, - const BRepGraph_TriangulationRepId theTriRep) -{ - myGraph->myData->myIncStorage.ChangePolygonOnTriRep(theRep).TriangulationRepId = theTriRep; - myGraph->markRepModified(theRep); -} - -//================================================================================================= - -void BRepGraph::EditorView::RepOps::SetPolygonOnTriTriangulationId( - BRepGraph_MutGuard& theMut, - const BRepGraph_TriangulationRepId theTriRep) -{ - theMut.Internal().TriangulationRepId = theTriRep; -} - //================================================================================================= namespace @@ -951,43 +568,7 @@ BRepGraph_VertexId vertexFromRef(const BRepGraphInc_Storage& theStorage, { return BRepGraph_VertexId(); } - return theStorage.VertexRef(theRef).VertexDefId; -} - -bool isLastVertexUsageOnEdge(const BRepGraphInc_Storage& theStorage, - const BRepGraph_EdgeId theEdge, - const BRepGraph_VertexId theVtx, - const BRepGraph_VertexRefId theExcludingRef) -{ - if (!theEdge.IsValid(theStorage.NbEdges()) || !theVtx.IsValid()) - { - return true; - } - const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(theEdge); - auto refResolvesTo = [&](const BRepGraph_VertexRefId aRefId) -> bool { - if (!aRefId.IsValid(theStorage.NbVertexRefs()) || aRefId == theExcludingRef) - { - return false; - } - const BRepGraphInc::VertexRef& aRef = theStorage.VertexRef(aRefId); - return !aRef.IsRemoved && aRef.VertexDefId == theVtx; - }; - if (refResolvesTo(anEdge.StartVertexRefId)) - { - return false; - } - if (refResolvesTo(anEdge.EndVertexRefId)) - { - return false; - } - for (const BRepGraph_VertexRefId& anIntRefId : anEdge.InternalVertexRefIds) - { - if (refResolvesTo(anIntRefId)) - { - return false; - } - } - return true; + return theStorage.VertexRef(theRef).ChildVertexId; } void rebindVertexEdge(BRepGraphInc_Storage& theStorage, @@ -996,36 +577,73 @@ void rebindVertexEdge(BRepGraphInc_Storage& theStorage, const BRepGraph_EdgeId theEdge, const BRepGraph_VertexRefId theExcludingRef) { - if (theOldVtx == theNewVtx) + theStorage.RebindVertexEdge(theOldVtx, theNewVtx, theEdge, theExcludingRef); +} + +bool isVertexRefOwnedByActiveEdge(const BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexRefId theVertexRef) +{ + if (!theVertexRef.IsValid()) { - return; + return false; } - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldVtx.IsValid() - && isLastVertexUsageOnEdge(theStorage, theEdge, theOldVtx, theExcludingRef)) + const BRepGraph_VertexId aVertexId = vertexFromRef(theStorage, theVertexRef); + if (!aVertexId.IsValid(theStorage.NbVertices())) { - aRI.UnbindVertexFromEdge(theOldVtx, theEdge); + return false; } - if (theNewVtx.IsValid()) + for (const BRepGraph_EdgeId& anEdgeId : theStorage.VertexRelations(aVertexId).EdgeIds) { - aRI.BindVertexToEdge(theNewVtx, theEdge); + if (!anEdgeId.IsValid(theStorage.NbEdges()) || theStorage.IsRemoved(anEdgeId)) + { + continue; + } + const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(anEdgeId); + if (anEdge.StartVertexRefId == theVertexRef || anEdge.EndVertexRefId == theVertexRef) + { + return true; + } } + return false; +} + +bool isUsableUnownedVertexRef(const BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexRefId theVertexRef) +{ + return !theVertexRef.IsValid() + || (theVertexRef.IsValid(theStorage.NbVertexRefs()) && !theStorage.IsRemoved(theVertexRef) + && !isVertexRefOwnedByActiveEdge(theStorage, theVertexRef)); +} + +void rebindVertexRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexRefId theVertexRef, + const BRepGraph_VertexId theOldVtx, + const BRepGraph_VertexId theNewVtx) +{ + theStorage.RebindVertexRef(theVertexRef, theOldVtx, theNewVtx); } } // namespace void BRepGraph::EditorView::EdgeOps::SetStartVertexRefId(const BRepGraph_EdgeId theEdge, const BRepGraph_VertexRefId theVertexRef) { - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); if (anEdge.StartVertexRefId == theVertexRef) { return; } + Standard_ASSERT_RETURN(isUsableUnownedVertexRef(aStorage, theVertexRef), + "SetStartVertexRefId: vertex ref must be active and unowned", + Standard_VOID_RETURN); + const BRepGraph_VertexRefId anOldRef = anEdge.StartVertexRefId; + myGraph->Editor().requireUnlocked(anOldRef, "BRepGraph::EditorView: locked item"); const BRepGraph_VertexId anOldVtx = vertexFromRef(aStorage, anEdge.StartVertexRefId); const BRepGraph_VertexId aNewVtx = vertexFromRef(aStorage, theVertexRef); anEdge.StartVertexRefId = theVertexRef; rebindVertexEdge(aStorage, anOldVtx, aNewVtx, theEdge, BRepGraph_VertexRefId()); + myGraph->Editor().Gen().RemoveRef(anOldRef); myGraph->markModified(theEdge); } @@ -1037,11 +655,17 @@ void BRepGraph::EditorView::EdgeOps::SetStartVertexRefId( { return; } - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + Standard_ASSERT_RETURN(isUsableUnownedVertexRef(aStorage, theVertexRef), + "SetStartVertexRefId: vertex ref must be active and unowned", + Standard_VOID_RETURN); + const BRepGraph_VertexRefId anOldRef = theMut->StartVertexRefId; + myGraph->Editor().requireUnlocked(anOldRef, "BRepGraph::EditorView: locked item"); const BRepGraph_VertexId anOldVtx = vertexFromRef(aStorage, theMut->StartVertexRefId); const BRepGraph_VertexId aNewVtx = vertexFromRef(aStorage, theVertexRef); theMut.Internal().StartVertexRefId = theVertexRef; rebindVertexEdge(aStorage, anOldVtx, aNewVtx, theMut.Id(), BRepGraph_VertexRefId()); + myGraph->Editor().Gen().RemoveRef(anOldRef); } //================================================================================================= @@ -1049,16 +673,23 @@ void BRepGraph::EditorView::EdgeOps::SetStartVertexRefId( void BRepGraph::EditorView::EdgeOps::SetEndVertexRefId(const BRepGraph_EdgeId theEdge, const BRepGraph_VertexRefId theVertexRef) { - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); if (anEdge.EndVertexRefId == theVertexRef) { return; } + Standard_ASSERT_RETURN(isUsableUnownedVertexRef(aStorage, theVertexRef), + "SetEndVertexRefId: vertex ref must be active and unowned", + Standard_VOID_RETURN); + const BRepGraph_VertexRefId anOldRef = anEdge.EndVertexRefId; + myGraph->Editor().requireUnlocked(anOldRef, "BRepGraph::EditorView: locked item"); const BRepGraph_VertexId anOldVtx = vertexFromRef(aStorage, anEdge.EndVertexRefId); const BRepGraph_VertexId aNewVtx = vertexFromRef(aStorage, theVertexRef); anEdge.EndVertexRefId = theVertexRef; rebindVertexEdge(aStorage, anOldVtx, aNewVtx, theEdge, BRepGraph_VertexRefId()); + myGraph->Editor().Gen().RemoveRef(anOldRef); myGraph->markModified(theEdge); } @@ -1070,871 +701,703 @@ void BRepGraph::EditorView::EdgeOps::SetEndVertexRefId( { return; } - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + Standard_ASSERT_RETURN(isUsableUnownedVertexRef(aStorage, theVertexRef), + "SetEndVertexRefId: vertex ref must be active and unowned", + Standard_VOID_RETURN); + const BRepGraph_VertexRefId anOldRef = theMut->EndVertexRefId; + myGraph->Editor().requireUnlocked(anOldRef, "BRepGraph::EditorView: locked item"); const BRepGraph_VertexId anOldVtx = vertexFromRef(aStorage, theMut->EndVertexRefId); const BRepGraph_VertexId aNewVtx = vertexFromRef(aStorage, theVertexRef); theMut.Internal().EndVertexRefId = theVertexRef; rebindVertexEdge(aStorage, anOldVtx, aNewVtx, theMut.Id(), BRepGraph_VertexRefId()); + myGraph->Editor().Gen().RemoveRef(anOldRef); } //================================================================================================= namespace { -//! True if no active CoEdge in theWire (other than theExcluding) references theEdge. -bool isLastCoEdgeOfEdgeInWire(const BRepGraphInc_Storage& theStorage, - const BRepGraph_EdgeId theEdge, - const BRepGraph_WireId theWire, - const BRepGraph_CoEdgeId theExcluding) -{ - if (!theWire.IsValid(theStorage.NbWires())) - { - return true; - } - const BRepGraphInc::WireDef& aWireDef = theStorage.Wire(theWire); - for (const BRepGraph_CoEdgeRefId& aRefId : aWireDef.CoEdgeRefIds) - { - if (!aRefId.IsValid(theStorage.NbCoEdgeRefs())) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef = theStorage.CoEdgeRef(aRefId); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) - { - continue; - } - if (aRef.CoEdgeDefId == theExcluding) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aRef.CoEdgeDefId); - if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId == theEdge) - { - return false; - } - } - return true; -} - -//! Apply rev-index updates for CoEdge.EdgeDefId rewrite. -void rebindCoEdgeEdge(BRepGraphInc_Storage& theStorage, - const BRepGraph_CoEdgeId theCoEdge, - const BRepGraphInc::CoEdgeDef& theDef, - const BRepGraph_EdgeId theOldEdge, - const BRepGraph_EdgeId theNewEdge) -{ - if (theOldEdge == theNewEdge) - { - return; - } - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldEdge.IsValid()) - { - aRI.UnbindEdgeFromCoEdge(theOldEdge, theCoEdge); - } - if (theNewEdge.IsValid()) - { - aRI.BindEdgeToCoEdge(theNewEdge, theCoEdge); - } - // Wires that own this coedge: rebind edge-to-wire only when no other coedge of the - // same wire still references the old edge. - const NCollection_DynamicArray* aWires = aRI.WiresOfCoEdge(theCoEdge); - if (aWires != nullptr) - { - for (const BRepGraph_WireId& aWireId : *aWires) - { - if (theOldEdge.IsValid() - && isLastCoEdgeOfEdgeInWire(theStorage, theOldEdge, aWireId, theCoEdge)) - { - aRI.UnbindEdgeFromWire(theOldEdge, aWireId); - } - if (theNewEdge.IsValid()) - { - aRI.BindEdgeToWire(theNewEdge, aWireId); - } - } - } - // Face binding follows the (edge, face) pair; if the coedge has a face, rebind. - if (theDef.FaceDefId.IsValid()) - { - if (theOldEdge.IsValid() - && isLastCoEdgeOfEdgeOnFace(theStorage, theOldEdge, theDef.FaceDefId, theCoEdge)) - { - aRI.UnbindEdgeFromFace(theOldEdge, theDef.FaceDefId); - } - if (theNewEdge.IsValid()) - { - aRI.BindEdgeToFace(theNewEdge, theDef.FaceDefId); - } - } -} - -//! Apply rev-index updates for CoEdge.FaceDefId rewrite. -void rebindCoEdgeFace(BRepGraphInc_Storage& theStorage, +//! Refresh derived relations after CoEdge.ChildEdgeId rewrite. +void rebindCoEdgeEdge(BRepGraphInc_Storage& theStorage, const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theOldFace, - const BRepGraph_FaceId theNewFace) + const BRepGraph_EdgeId theOldEdge, + const BRepGraph_EdgeId theNewEdge) { - if (theOldFace == theNewFace || !theEdge.IsValid()) - { - return; - } - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldFace.IsValid() && isLastCoEdgeOfEdgeOnFace(theStorage, theEdge, theOldFace, theCoEdge)) - { - aRI.UnbindEdgeFromFace(theEdge, theOldFace); - } - if (theNewFace.IsValid()) - { - aRI.BindEdgeToFace(theEdge, theNewFace); - } -} -} // namespace - -void BRepGraph::EditorView::CoEdgeOps::SetEdgeDefId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_EdgeId theEdge) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::CoEdgeDef& aDef = aStorage.ChangeCoEdge(theCoEdge); - if (aDef.EdgeDefId == theEdge) - { - return; - } - const BRepGraph_EdgeId anOldEdge = aDef.EdgeDefId; - aDef.EdgeDefId = theEdge; - rebindCoEdgeEdge(aStorage, theCoEdge, aDef, anOldEdge, theEdge); - myGraph->markModified(theCoEdge); -} - -void BRepGraph::EditorView::CoEdgeOps::SetEdgeDefId( - BRepGraph_MutGuard& theMut, - const BRepGraph_EdgeId theEdge) -{ - if (theMut->EdgeDefId == theEdge) - { - return; - } - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_EdgeId anOldEdge = theMut->EdgeDefId; - theMut.Internal().EdgeDefId = theEdge; - rebindCoEdgeEdge(aStorage, theMut.Id(), *theMut, anOldEdge, theEdge); -} - -//================================================================================================= - -void BRepGraph::EditorView::CoEdgeOps::SetFaceDefId(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_FaceId theFace) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::CoEdgeDef& aDef = aStorage.ChangeCoEdge(theCoEdge); - if (aDef.FaceDefId == theFace) - { - return; - } - const BRepGraph_FaceId anOldFace = aDef.FaceDefId; - const BRepGraph_EdgeId anEdge = aDef.EdgeDefId; - aDef.FaceDefId = theFace; - rebindCoEdgeFace(aStorage, theCoEdge, anEdge, anOldFace, theFace); - myGraph->markModified(theCoEdge); -} - -void BRepGraph::EditorView::CoEdgeOps::SetFaceDefId( - BRepGraph_MutGuard& theMut, - const BRepGraph_FaceId theFace) -{ - if (theMut->FaceDefId == theFace) - { - return; - } - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_FaceId anOldFace = theMut->FaceDefId; - const BRepGraph_EdgeId anEdge = theMut->EdgeDefId; - theMut.Internal().FaceDefId = theFace; - rebindCoEdgeFace(aStorage, theMut.Id(), anEdge, anOldFace, theFace); -} - -//================================================================================================= - -namespace -{ -void rebindVertexRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParent, - const BRepGraph_VertexId theOldVtx, - const BRepGraph_VertexId theNewVtx, - const BRepGraph_VertexRefId theMutatedRef) -{ - if (theOldVtx == theNewVtx) - { - return; - } - if (theParent.NodeKind != BRepGraph_NodeId::Kind::Edge) - { - return; - } - const BRepGraph_EdgeId anEdge(theParent); - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldVtx.IsValid() && isLastVertexUsageOnEdge(theStorage, anEdge, theOldVtx, theMutatedRef)) - { - aRI.UnbindVertexFromEdge(theOldVtx, anEdge); - } - if (theNewVtx.IsValid()) - { - aRI.BindVertexToEdge(theNewVtx, anEdge); - } -} -} // namespace - -void BRepGraph::EditorView::VertexOps::SetRefVertexDefId(const BRepGraph_VertexRefId theVertexRef, - const BRepGraph_VertexId theVertex) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::VertexRef& aRef = aStorage.ChangeVertexRef(theVertexRef); - if (aRef.VertexDefId == theVertex) - { - return; - } - const BRepGraph_VertexId anOldVtx = aRef.VertexDefId; - const BRepGraph_NodeId aParent = aRef.ParentId; - aRef.VertexDefId = theVertex; - rebindVertexRef(aStorage, aParent, anOldVtx, theVertex, theVertexRef); - myGraph->markRefModified(theVertexRef); -} - -void BRepGraph::EditorView::VertexOps::SetRefVertexDefId( - BRepGraph_MutGuard& theMut, - const BRepGraph_VertexId theVertex) -{ - if (theMut->VertexDefId == theVertex) - { - return; - } - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_VertexId anOldVtx = theMut->VertexDefId; - const BRepGraph_NodeId aParent = theMut->ParentId; - theMut.Internal().VertexDefId = theVertex; - rebindVertexRef(aStorage, aParent, anOldVtx, theVertex, theMut.Id()); -} - -//================================================================================================= - -namespace -{ -bool isLastWireUsageOnFace(const BRepGraphInc_Storage& theStorage, - const BRepGraph_FaceId theFace, - const BRepGraph_WireId theWire, - const BRepGraph_WireRefId theExcludingRef) -{ - if (!theFace.IsValid(theStorage.NbFaces()) || !theWire.IsValid()) - { - return true; - } - const BRepGraphInc::FaceDef& aFaceDef = theStorage.Face(theFace); - for (const BRepGraph_WireRefId& aRefId : aFaceDef.WireRefIds) - { - if (!aRefId.IsValid(theStorage.NbWireRefs()) || aRefId == theExcludingRef) - { - continue; - } - const BRepGraphInc::WireRef& aRef = theStorage.WireRef(aRefId); - if (!aRef.IsRemoved && aRef.WireDefId == theWire) - { - return false; - } - } - return true; + theStorage.RebindCoEdgeEdge(theCoEdge, theOldEdge, theNewEdge); } void rebindWireRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParent, + const BRepGraph_WireRefId theWireRef, const BRepGraph_WireId theOldWire, - const BRepGraph_WireId theNewWire, - const BRepGraph_WireRefId theMutatedRef) + const BRepGraph_WireId theNewWire) { - if (theOldWire == theNewWire) - { - return; - } - if (theParent.NodeKind != BRepGraph_NodeId::Kind::Face) - { - return; - } - const BRepGraph_FaceId aFace(theParent); - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldWire.IsValid() && isLastWireUsageOnFace(theStorage, aFace, theOldWire, theMutatedRef)) - { - aRI.UnbindWireFromFace(theOldWire, aFace); - } - if (theNewWire.IsValid()) - { - aRI.BindWireToFace(theNewWire, aFace); - } -} -} // namespace - -void BRepGraph::EditorView::WireOps::SetRefWireDefId(const BRepGraph_WireRefId theWireRef, - const BRepGraph_WireId theWire) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::WireRef& aRef = aStorage.ChangeWireRef(theWireRef); - if (aRef.WireDefId == theWire) - { - return; - } - const BRepGraph_WireId anOldWire = aRef.WireDefId; - const BRepGraph_NodeId aParent = aRef.ParentId; - aRef.WireDefId = theWire; - rebindWireRef(aStorage, aParent, anOldWire, theWire, theWireRef); - myGraph->markRefModified(theWireRef); -} - -void BRepGraph::EditorView::WireOps::SetRefWireDefId( - BRepGraph_MutGuard& theMut, - const BRepGraph_WireId theWire) -{ - if (theMut->WireDefId == theWire) - { - return; - } - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_WireId anOldWire = theMut->WireDefId; - const BRepGraph_NodeId aParent = theMut->ParentId; - theMut.Internal().WireDefId = theWire; - rebindWireRef(aStorage, aParent, anOldWire, theWire, theMut.Id()); -} - -//================================================================================================= - -namespace -{ -bool isLastFaceUsageOnShell(const BRepGraphInc_Storage& theStorage, - const BRepGraph_ShellId theShell, - const BRepGraph_FaceId theFace, - const BRepGraph_FaceRefId theExcludingRef) -{ - if (!theShell.IsValid(theStorage.NbShells()) || !theFace.IsValid()) - { - return true; - } - const BRepGraphInc::ShellDef& aShellDef = theStorage.Shell(theShell); - for (const BRepGraph_FaceRefId& aRefId : aShellDef.FaceRefIds) - { - if (!aRefId.IsValid(theStorage.NbFaceRefs()) || aRefId == theExcludingRef) - { - continue; - } - const BRepGraphInc::FaceRef& aRef = theStorage.FaceRef(aRefId); - if (!aRef.IsRemoved && aRef.FaceDefId == theFace) - { - return false; - } - } - return true; + theStorage.RebindWireRef(theWireRef, theOldWire, theNewWire); } void rebindFaceRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParent, + const BRepGraph_FaceRefId theFaceRef, const BRepGraph_FaceId theOldFace, - const BRepGraph_FaceId theNewFace, - const BRepGraph_FaceRefId theMutatedRef) + const BRepGraph_FaceId theNewFace) { - if (theOldFace == theNewFace) - { - return; - } - if (theParent.NodeKind != BRepGraph_NodeId::Kind::Shell) - { - return; - } - const BRepGraph_ShellId aShell(theParent); - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldFace.IsValid() && isLastFaceUsageOnShell(theStorage, aShell, theOldFace, theMutatedRef)) - { - aRI.UnbindFaceFromShell(theOldFace, aShell); - } - if (theNewFace.IsValid()) - { - aRI.BindFaceToShell(theNewFace, aShell); - } + theStorage.RebindFaceRef(theFaceRef, theOldFace, theNewFace); } + +void rebindShellRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_ShellRefId theShellRef, + const BRepGraph_ShellId theOldShell, + const BRepGraph_ShellId theNewShell) +{ + theStorage.RebindShellRef(theShellRef, theOldShell, theNewShell); +} + +void rebindSolidRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_SolidRefId theSolidRef, + const BRepGraph_SolidId theOldSolid, + const BRepGraph_SolidId theNewSolid) +{ + theStorage.RebindSolidRef(theSolidRef, theOldSolid, theNewSolid); +} + +void rebindChildRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_ChildRefId theChildRef, + const BRepGraph_NodeId theOldChild, + const BRepGraph_NodeId theNewChild) +{ + theStorage.RebindChildRef(theChildRef, theOldChild, theNewChild); +} + } // namespace -void BRepGraph::EditorView::FaceOps::SetRefFaceDefId(const BRepGraph_FaceRefId theFaceRef, - const BRepGraph_FaceId theFace) +void BRepGraph::EditorView::CoEdgeOps::SetChildEdgeId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_EdgeId theEdge) { - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::FaceRef& aRef = aStorage.ChangeFaceRef(theFaceRef); - if (aRef.FaceDefId == theFace) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc::CoEdgeDef& aDef = aStorage.ChangeCoEdge(theCoEdge); + if (aDef.ChildEdgeId == theEdge) { return; } - const BRepGraph_FaceId anOldFace = aRef.FaceDefId; - const BRepGraph_NodeId aParent = aRef.ParentId; - aRef.FaceDefId = theFace; - rebindFaceRef(aStorage, aParent, anOldFace, theFace, theFaceRef); - myGraph->markRefModified(theFaceRef); + const BRepGraph_EdgeId anOldEdge = aDef.ChildEdgeId; + aDef.ChildEdgeId = theEdge; + rebindCoEdgeEdge(aStorage, theCoEdge, anOldEdge, theEdge); + myGraph->markModified(theCoEdge); } -void BRepGraph::EditorView::FaceOps::SetRefFaceDefId( - BRepGraph_MutGuard& theMut, - const BRepGraph_FaceId theFace) +void BRepGraph::EditorView::CoEdgeOps::SetChildEdgeId( + BRepGraph_MutGuard& theMut, + const BRepGraph_EdgeId theEdge) { - if (theMut->FaceDefId == theFace) + if (theMut->ChildEdgeId == theEdge) { return; } BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_FaceId anOldFace = theMut->FaceDefId; - const BRepGraph_NodeId aParent = theMut->ParentId; - theMut.Internal().FaceDefId = theFace; - rebindFaceRef(aStorage, aParent, anOldFace, theFace, theMut.Id()); + const BRepGraph_EdgeId anOldEdge = theMut->ChildEdgeId; + theMut.Internal().ChildEdgeId = theEdge; + rebindCoEdgeEdge(aStorage, theMut.Id(), anOldEdge, theEdge); } //================================================================================================= -namespace +void BRepGraph::EditorView::CoEdgeOps::SetFaceId(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_FaceId theFace) { -bool isLastShellUsageOnSolid(const BRepGraphInc_Storage& theStorage, - const BRepGraph_SolidId theSolid, - const BRepGraph_ShellId theShell, - const BRepGraph_ShellRefId theExcludingRef) -{ - if (!theSolid.IsValid(theStorage.NbSolids()) || !theShell.IsValid()) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc::CoEdgeDef& aDef = aStorage.ChangeCoEdge(theCoEdge); + if (aDef.FaceId == theFace) { - return true; + return; } - const BRepGraphInc::SolidDef& aSolidDef = theStorage.Solid(theSolid); - for (const BRepGraph_ShellRefId& aRefId : aSolidDef.ShellRefIds) - { - if (!aRefId.IsValid(theStorage.NbShellRefs()) || aRefId == theExcludingRef) - { - continue; - } - const BRepGraphInc::ShellRef& aRef = theStorage.ShellRef(aRefId); - if (!aRef.IsRemoved && aRef.ShellDefId == theShell) - { - return false; - } - } - return true; + aDef.FaceId = theFace; + clearCoEdgeFaceScopedRepresentations(aStorage, aDef); + myGraph->markModified(theCoEdge); } -void rebindShellRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParent, - const BRepGraph_ShellId theOldShell, - const BRepGraph_ShellId theNewShell, - const BRepGraph_ShellRefId theMutatedRef) +void BRepGraph::EditorView::CoEdgeOps::SetFaceId( + BRepGraph_MutGuard& theMut, + const BRepGraph_FaceId theFace) { - if (theOldShell == theNewShell) + if (theMut->FaceId == theFace) { return; } - if (theParent.NodeKind != BRepGraph_NodeId::Kind::Solid) - { - return; - } - const BRepGraph_SolidId aSolid(theParent); - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldShell.IsValid() - && isLastShellUsageOnSolid(theStorage, aSolid, theOldShell, theMutatedRef)) - { - aRI.UnbindShellFromSolid(theOldShell, aSolid); - } - if (theNewShell.IsValid()) - { - aRI.BindShellToSolid(theNewShell, aSolid); - } + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aCoEdge = theMut.Internal(); + aCoEdge.FaceId = theFace; + clearCoEdgeFaceScopedRepresentations(aStorage, aCoEdge); } -} // namespace -void BRepGraph::EditorView::ShellOps::SetRefShellDefId(const BRepGraph_ShellRefId theShellRef, - const BRepGraph_ShellId theShell) +//================================================================================================= + +void BRepGraph::EditorView::VertexOps::SetRefChildVertexId(const BRepGraph_VertexRefId theVertexRef, + const BRepGraph_VertexId theVertex) { - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::ShellRef& aRef = aStorage.ChangeShellRef(theShellRef); - if (aRef.ShellDefId == theShell) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theVertexRef, "BRepGraph::EditorView: locked item"); + BRepGraphInc::VertexRef& aRef = aStorage.ChangeVertexRef(theVertexRef); + if (aRef.ChildVertexId == theVertex) { return; } - const BRepGraph_ShellId anOldShell = aRef.ShellDefId; - const BRepGraph_NodeId aParent = aRef.ParentId; - aRef.ShellDefId = theShell; - rebindShellRef(aStorage, aParent, anOldShell, theShell, theShellRef); + const BRepGraph_VertexId anOldVtx = aRef.ChildVertexId; + rebindVertexRef(aStorage, theVertexRef, anOldVtx, theVertex); + myGraph->markRefModified(theVertexRef); +} + +void BRepGraph::EditorView::VertexOps::SetRefChildVertexId( + BRepGraph_MutGuard& theMut, + const BRepGraph_VertexId theVertex) +{ + if (theMut->ChildVertexId == theVertex) + { + return; + } + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_VertexId anOldVtx = theMut->ChildVertexId; + rebindVertexRef(aStorage, theMut.Id(), anOldVtx, theVertex); +} + +//================================================================================================= + +void BRepGraph::EditorView::WireOps::SetRefChildWireId(const BRepGraph_WireRefId theWireRef, + const BRepGraph_WireId theWire) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theWireRef, "BRepGraph::EditorView: locked item"); + BRepGraphInc::WireRef& aRef = aStorage.ChangeWireRef(theWireRef); + if (aRef.ChildWireId == theWire) + { + return; + } + const BRepGraph_WireId anOldWire = aRef.ChildWireId; + aRef.ChildWireId = theWire; + rebindWireRef(aStorage, theWireRef, anOldWire, theWire); + myGraph->markRefModified(theWireRef); +} + +void BRepGraph::EditorView::WireOps::SetRefChildWireId( + BRepGraph_MutGuard& theMut, + const BRepGraph_WireId theWire) +{ + if (theMut->ChildWireId == theWire) + { + return; + } + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_WireId anOldWire = theMut->ChildWireId; + theMut.Internal().ChildWireId = theWire; + rebindWireRef(aStorage, theMut.Id(), anOldWire, theWire); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetRefFaceId(const BRepGraph_FaceRefId theFaceRef, + const BRepGraph_FaceId theFace) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theFaceRef, "BRepGraph::EditorView: locked item"); + BRepGraphInc::FaceRef& aRef = aStorage.ChangeFaceRef(theFaceRef); + if (aRef.ChildFaceId == theFace) + { + return; + } + const BRepGraph_FaceId anOldFace = aRef.ChildFaceId; + aRef.ChildFaceId = theFace; + rebindFaceRef(aStorage, theFaceRef, anOldFace, theFace); + myGraph->markRefModified(theFaceRef); +} + +void BRepGraph::EditorView::FaceOps::SetRefFaceId(BRepGraph_MutGuard& theMut, + const BRepGraph_FaceId theFace) +{ + if (theMut->ChildFaceId == theFace) + { + return; + } + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraph_FaceId anOldFace = theMut->ChildFaceId; + theMut.Internal().ChildFaceId = theFace; + rebindFaceRef(aStorage, theMut.Id(), anOldFace, theFace); +} + +//================================================================================================= + +void BRepGraph::EditorView::ShellOps::SetRefChildShellId(const BRepGraph_ShellRefId theShellRef, + const BRepGraph_ShellId theShell) +{ + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theShellRef, "BRepGraph::EditorView: locked item"); + BRepGraphInc::ShellRef& aRef = aStorage.ChangeShellRef(theShellRef); + if (aRef.ChildShellId == theShell) + { + return; + } + const BRepGraph_ShellId anOldShell = aRef.ChildShellId; + aRef.ChildShellId = theShell; + rebindShellRef(aStorage, theShellRef, anOldShell, theShell); myGraph->markRefModified(theShellRef); } -void BRepGraph::EditorView::ShellOps::SetRefShellDefId( +void BRepGraph::EditorView::ShellOps::SetRefChildShellId( BRepGraph_MutGuard& theMut, const BRepGraph_ShellId theShell) { - if (theMut->ShellDefId == theShell) + if (theMut->ChildShellId == theShell) { return; } BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_ShellId anOldShell = theMut->ShellDefId; - const BRepGraph_NodeId aParent = theMut->ParentId; - theMut.Internal().ShellDefId = theShell; - rebindShellRef(aStorage, aParent, anOldShell, theShell, theMut.Id()); + const BRepGraph_ShellId anOldShell = theMut->ChildShellId; + theMut.Internal().ChildShellId = theShell; + rebindShellRef(aStorage, theMut.Id(), anOldShell, theShell); } //================================================================================================= -namespace +void BRepGraph::EditorView::SolidOps::SetRefChildSolidId(const BRepGraph_SolidRefId theSolidRef, + const BRepGraph_SolidId theSolid) { -bool isLastSolidUsageOnCompSolid(const BRepGraphInc_Storage& theStorage, - const BRepGraph_CompSolidId theCompSolid, - const BRepGraph_SolidId theSolid, - const BRepGraph_SolidRefId theExcludingRef) -{ - if (!theCompSolid.IsValid(theStorage.NbCompSolids()) || !theSolid.IsValid()) - { - return true; - } - const BRepGraphInc::CompSolidDef& aCSDef = theStorage.CompSolid(theCompSolid); - for (const BRepGraph_SolidRefId& aRefId : aCSDef.SolidRefIds) - { - if (!aRefId.IsValid(theStorage.NbSolidRefs()) || aRefId == theExcludingRef) - { - continue; - } - const BRepGraphInc::SolidRef& aRef = theStorage.SolidRef(aRefId); - if (!aRef.IsRemoved && aRef.SolidDefId == theSolid) - { - return false; - } - } - return true; -} - -void rebindSolidRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParent, - const BRepGraph_SolidId theOldSolid, - const BRepGraph_SolidId theNewSolid, - const BRepGraph_SolidRefId theMutatedRef) -{ - if (theOldSolid == theNewSolid) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theSolidRef, "BRepGraph::EditorView: locked item"); + BRepGraphInc::SolidRef& aRef = aStorage.ChangeSolidRef(theSolidRef); + if (aRef.ChildSolidId == theSolid) { return; } - if (theParent.NodeKind != BRepGraph_NodeId::Kind::CompSolid) - { - return; - } - const BRepGraph_CompSolidId aCompSolid(theParent); - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldSolid.IsValid() - && isLastSolidUsageOnCompSolid(theStorage, aCompSolid, theOldSolid, theMutatedRef)) - { - aRI.UnbindSolidFromCompSolid(theOldSolid, aCompSolid); - } - if (theNewSolid.IsValid()) - { - aRI.BindSolidToCompSolid(theNewSolid, aCompSolid); - } -} -} // namespace - -void BRepGraph::EditorView::SolidOps::SetRefSolidDefId(const BRepGraph_SolidRefId theSolidRef, - const BRepGraph_SolidId theSolid) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::SolidRef& aRef = aStorage.ChangeSolidRef(theSolidRef); - if (aRef.SolidDefId == theSolid) - { - return; - } - const BRepGraph_SolidId anOldSolid = aRef.SolidDefId; - const BRepGraph_NodeId aParent = aRef.ParentId; - aRef.SolidDefId = theSolid; - rebindSolidRef(aStorage, aParent, anOldSolid, theSolid, theSolidRef); + const BRepGraph_SolidId anOldSolid = aRef.ChildSolidId; + aRef.ChildSolidId = theSolid; + rebindSolidRef(aStorage, theSolidRef, anOldSolid, theSolid); myGraph->markRefModified(theSolidRef); } -void BRepGraph::EditorView::SolidOps::SetRefSolidDefId( +void BRepGraph::EditorView::SolidOps::SetRefChildSolidId( BRepGraph_MutGuard& theMut, const BRepGraph_SolidId theSolid) { - if (theMut->SolidDefId == theSolid) + if (theMut->ChildSolidId == theSolid) { return; } BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_SolidId anOldSolid = theMut->SolidDefId; - const BRepGraph_NodeId aParent = theMut->ParentId; - theMut.Internal().SolidDefId = theSolid; - rebindSolidRef(aStorage, aParent, anOldSolid, theSolid, theMut.Id()); + const BRepGraph_SolidId anOldSolid = theMut->ChildSolidId; + theMut.Internal().ChildSolidId = theSolid; + rebindSolidRef(aStorage, theMut.Id(), anOldSolid, theSolid); } //================================================================================================= -namespace -{ -//! Resolve EdgeDefId reachable through a CoEdgeId (via CoEdgeDef.EdgeDefId). -BRepGraph_EdgeId edgeFromCoEdge(const BRepGraphInc_Storage& theStorage, - const BRepGraph_CoEdgeId theCoEdge) -{ - if (!theCoEdge.IsValid(theStorage.NbCoEdges())) - { - return BRepGraph_EdgeId(); - } - return theStorage.CoEdge(theCoEdge).EdgeDefId; -} - -//! True if no active CoEdgeRef in theWire references theEdge except theExcluding. -bool isLastCoEdgeRefOfEdgeInWire(const BRepGraphInc_Storage& theStorage, - const BRepGraph_EdgeId theEdge, - const BRepGraph_WireId theWire, - const BRepGraph_CoEdgeRefId theExcluding) -{ - if (!theWire.IsValid(theStorage.NbWires())) - { - return true; - } - const BRepGraphInc::WireDef& aWireDef = theStorage.Wire(theWire); - for (const BRepGraph_CoEdgeRefId& aRefId : aWireDef.CoEdgeRefIds) - { - if (aRefId == theExcluding) - { - continue; - } - if (!aRefId.IsValid(theStorage.NbCoEdgeRefs())) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef = theStorage.CoEdgeRef(aRefId); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aRef.CoEdgeDefId); - if (!aCoEdge.IsRemoved && aCoEdge.EdgeDefId == theEdge) - { - return false; - } - } - return true; -} - -void rebindCoEdgeRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_CoEdgeRefId theRefId, - const BRepGraph_NodeId theParent, - const BRepGraph_CoEdgeId theOldCoEdge, - const BRepGraph_CoEdgeId theNewCoEdge) -{ - if (theOldCoEdge == theNewCoEdge) - { - return; - } - if (theParent.NodeKind != BRepGraph_NodeId::Kind::Wire) - { - return; - } - const BRepGraph_WireId aWire(theParent); - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldCoEdge.IsValid()) - { - aRI.UnbindCoEdgeFromWire(theOldCoEdge, aWire); - } - if (theNewCoEdge.IsValid()) - { - aRI.BindCoEdgeToWire(theNewCoEdge, aWire); - } - // EdgeToWires is derived from coedge.EdgeDefId x wire (deduplicated). Unbind only - // when no other CoEdgeRef in the wire still references the old edge. - const BRepGraph_EdgeId anOldEdge = edgeFromCoEdge(theStorage, theOldCoEdge); - const BRepGraph_EdgeId aNewEdge = edgeFromCoEdge(theStorage, theNewCoEdge); - if (anOldEdge != aNewEdge) - { - if (anOldEdge.IsValid() && isLastCoEdgeRefOfEdgeInWire(theStorage, anOldEdge, aWire, theRefId)) - { - aRI.UnbindEdgeFromWire(anOldEdge, aWire); - } - if (aNewEdge.IsValid()) - { - aRI.BindEdgeToWire(aNewEdge, aWire); - } - } -} -} // namespace - -void BRepGraph::EditorView::CoEdgeOps::SetRefCoEdgeDefId(const BRepGraph_CoEdgeRefId theCoEdgeRef, - const BRepGraph_CoEdgeId theCoEdge) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::CoEdgeRef& aRef = aStorage.ChangeCoEdgeRef(theCoEdgeRef); - if (aRef.CoEdgeDefId == theCoEdge) - { - return; - } - const BRepGraph_CoEdgeId anOldCoEdge = aRef.CoEdgeDefId; - const BRepGraph_NodeId aParent = aRef.ParentId; - aRef.CoEdgeDefId = theCoEdge; - rebindCoEdgeRef(aStorage, theCoEdgeRef, aParent, anOldCoEdge, theCoEdge); - myGraph->markRefModified(theCoEdgeRef); -} - -void BRepGraph::EditorView::CoEdgeOps::SetRefCoEdgeDefId( - BRepGraph_MutGuard& theMut, - const BRepGraph_CoEdgeId theCoEdge) -{ - if (theMut->CoEdgeDefId == theCoEdge) - { - return; - } - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_CoEdgeId anOldCoEdge = theMut->CoEdgeDefId; - const BRepGraph_NodeId aParent = theMut->ParentId; - theMut.Internal().CoEdgeDefId = theCoEdge; - rebindCoEdgeRef(aStorage, theMut.Id(), aParent, anOldCoEdge, theCoEdge); -} - //================================================================================================= -namespace +void BRepGraph::EditorView::GenOps::SetChildRefChildNodeId(const BRepGraph_ChildRefId theChildRef, + const BRepGraph_NodeId theChild) { -bool isLastChildUsageOnCompound(const BRepGraphInc_Storage& theStorage, - const BRepGraph_CompoundId theCompound, - const BRepGraph_NodeId theChild, - const BRepGraph_ChildRefId theExcludingRef) -{ - if (!theCompound.IsValid(theStorage.NbCompounds()) || !theChild.IsValid()) - { - return true; - } - const BRepGraphInc::CompoundDef& aCompDef = theStorage.Compound(theCompound); - for (const BRepGraph_ChildRefId& aRefId : aCompDef.ChildRefIds) - { - if (!aRefId.IsValid(theStorage.NbChildRefs()) || aRefId == theExcludingRef) - { - continue; - } - const BRepGraphInc::ChildRef& aRef = theStorage.ChildRef(aRefId); - if (!aRef.IsRemoved && aRef.ChildDefId == theChild) - { - return false; - } - } - return true; -} - -void rebindChildRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParent, - const BRepGraph_NodeId theOldChild, - const BRepGraph_NodeId theNewChild, - const BRepGraph_ChildRefId theMutatedRef) -{ - if (theOldChild == theNewChild) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theChildRef, "BRepGraph::EditorView: locked item"); + BRepGraphInc::ChildRef& aRef = aStorage.ChangeChildRef(theChildRef); + if (aRef.ChildNodeId == theChild) { return; } - if (theParent.NodeKind != BRepGraph_NodeId::Kind::Compound) - { - return; - } - const BRepGraph_CompoundId aCompound(theParent); - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldChild.IsValid() - && isLastChildUsageOnCompound(theStorage, aCompound, theOldChild, theMutatedRef)) - { - aRI.UnbindCompoundChild(theOldChild, aCompound); - } - if (theNewChild.IsValid()) - { - aRI.BindCompoundChild(theNewChild, aCompound); - } -} -} // namespace - -void BRepGraph::EditorView::GenOps::SetChildRefChildDefId(const BRepGraph_ChildRefId theChildRef, - const BRepGraph_NodeId theChild) -{ - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::ChildRef& aRef = aStorage.ChangeChildRef(theChildRef); - if (aRef.ChildDefId == theChild) - { - return; - } - const BRepGraph_NodeId anOldChild = aRef.ChildDefId; - const BRepGraph_NodeId aParent = aRef.ParentId; - aRef.ChildDefId = theChild; - rebindChildRef(aStorage, aParent, anOldChild, theChild, theChildRef); + const BRepGraph_NodeId anOldChild = aRef.ChildNodeId; + aRef.ChildNodeId = theChild; + rebindChildRef(aStorage, theChildRef, anOldChild, theChild); myGraph->markRefModified(theChildRef); } -void BRepGraph::EditorView::GenOps::SetChildRefChildDefId( +void BRepGraph::EditorView::GenOps::SetChildRefChildNodeId( BRepGraph_MutGuard& theMut, const BRepGraph_NodeId theChild) { - if (theMut->ChildDefId == theChild) + if (theMut->ChildNodeId == theChild) { return; } BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_NodeId anOldChild = theMut->ChildDefId; - const BRepGraph_NodeId aParent = theMut->ParentId; - theMut.Internal().ChildDefId = theChild; - rebindChildRef(aStorage, aParent, anOldChild, theChild, theMut.Id()); + const BRepGraph_NodeId anOldChild = theMut->ChildNodeId; + theMut.Internal().ChildNodeId = theChild; + rebindChildRef(aStorage, theMut.Id(), anOldChild, theChild); } //================================================================================================= -namespace -{ -void rebindOccurrenceRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParent, - const BRepGraph_OccurrenceId theOldOcc, - const BRepGraph_OccurrenceId theNewOcc) -{ - if (theOldOcc == theNewOcc) - { - return; - } - if (theParent.NodeKind != BRepGraph_NodeId::Kind::Product) - { - return; - } - const BRepGraph_ProductId aProduct(theParent); - BRepGraphInc_ReverseIndex& aRI = theStorage.ChangeReverseIndex(); - if (theOldOcc.IsValid()) - { - aRI.UnbindProductOccurrence(theOldOcc, aProduct); - } - if (theNewOcc.IsValid()) - { - aRI.BindProductOccurrence(theNewOcc, aProduct); - } -} -} // namespace - -void BRepGraph::EditorView::OccurrenceOps::SetRefOccurrenceDefId( +void BRepGraph::EditorView::OccurrenceOps::SetRefChildOccurrenceId( const BRepGraph_OccurrenceRefId theOccurrenceRef, const BRepGraph_OccurrenceId theOccurrence) { - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - BRepGraphInc::OccurrenceRef& aRef = aStorage.ChangeOccurrenceRef(theOccurrenceRef); - if (aRef.OccurrenceDefId == theOccurrence) + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + myGraph->Editor().requireUnlocked(theOccurrenceRef, "BRepGraph::EditorView: locked item"); + BRepGraphInc::OccurrenceRef& aRef = aStorage.ChangeOccurrenceRef(theOccurrenceRef); + if (aRef.ChildOccurrenceId == theOccurrence) { return; } - const BRepGraph_OccurrenceId anOldOcc = aRef.OccurrenceDefId; - const BRepGraph_NodeId aParent = aRef.ParentId; - aRef.OccurrenceDefId = theOccurrence; - rebindOccurrenceRef(aStorage, aParent, anOldOcc, theOccurrence); + Standard_ASSERT_RETURN(!occurrenceHasOtherActiveRef(aStorage, theOccurrence, theOccurrenceRef), + "SetRefChildOccurrenceId: occurrence definition is already owned", + Standard_VOID_RETURN); + const BRepGraph_OccurrenceId anOldOccurrence = aRef.ChildOccurrenceId; + aRef.ChildOccurrenceId = theOccurrence; + rebindOccurrenceRefParentProduct(aStorage, theOccurrenceRef, anOldOccurrence, theOccurrence); myGraph->markRefModified(theOccurrenceRef); } -void BRepGraph::EditorView::OccurrenceOps::SetRefOccurrenceDefId( +void BRepGraph::EditorView::OccurrenceOps::SetRefChildOccurrenceId( BRepGraph_MutGuard& theMut, const BRepGraph_OccurrenceId theOccurrence) { - if (theMut->OccurrenceDefId == theOccurrence) + if (theMut->ChildOccurrenceId == theOccurrence) { return; } - BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_OccurrenceId anOldOcc = theMut->OccurrenceDefId; - const BRepGraph_NodeId aParent = theMut->ParentId; - theMut.Internal().OccurrenceDefId = theOccurrence; - rebindOccurrenceRef(aStorage, aParent, anOldOcc, theOccurrence); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + Standard_ASSERT_RETURN(!occurrenceHasOtherActiveRef(aStorage, theOccurrence, theMut.Id()), + "SetRefChildOccurrenceId: occurrence definition is already owned", + Standard_VOID_RETURN); + const BRepGraph_OccurrenceId anOldOccurrence = theMut->ChildOccurrenceId; + theMut.Internal().ChildOccurrenceId = theOccurrence; + rebindOccurrenceRefParentProduct(aStorage, theMut.Id(), anOldOccurrence, theOccurrence); +} + +//================================================================================================= +// Owner-scoped EdgeOps setters (Use-based API) +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetCurve(const BRepGraph_EdgeId theEdge, + const occ::handle& theCurve, + const double theFirst, + const double theLast) +{ + myGraph->Editor().requireUnlocked(theEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + + // Create or update the EdgeCurve3DRep (new owned use record) + if (!theCurve.IsNull()) + { + if (anEdge.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D())) + { + aStorage.SetRemoved(anEdge.Curve3DRepId, false); + BRepGraphInc::EdgeCurve3DRep& aUse = aStorage.ChangeEdgeCurve3DRep(anEdge.Curve3DRepId); + aUse.ParentEdgeId = theEdge; + aUse.Curve = theCurve; + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + } + else + { + const BRepGraph_EdgeCurve3DRepId aRepId = aStorage.AppendEdgeCurve3DRep(); + BRepGraphInc::EdgeCurve3DRep& aUse = aStorage.ChangeEdgeCurve3DRep(aRepId); + aUse.ParentEdgeId = theEdge; + aUse.Curve = theCurve; + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + anEdge.Curve3DRepId = aRepId; + } + } + else + { + aStorage.MarkRemoved(anEdge.Curve3DRepId); + } + + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::ClearCurve(const BRepGraph_EdgeId theEdge) +{ + myGraph->Editor().requireUnlocked(theEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + + aStorage.MarkRemoved(anEdge.Curve3DRepId); + + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::SetPersistentPolygon3D( + const BRepGraph_EdgeId theEdge, + const occ::handle& thePolygon) +{ + myGraph->Editor().requireUnlocked(theEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + + // Create or update the EdgePolygon3DRep (new owned use record) + if (!thePolygon.IsNull()) + { + if (anEdge.Polygon3DRepId.IsValid(aStorage.NbEdgePolygons3D())) + { + aStorage.SetRemoved(anEdge.Polygon3DRepId, false); + BRepGraphInc::EdgePolygon3DRep& aUse = aStorage.ChangeEdgePolygon3DRep(anEdge.Polygon3DRepId); + aUse.ParentEdgeId = theEdge; + aUse.Polygon = thePolygon; + } + else + { + const BRepGraph_EdgePolygon3DRepId aRepId = aStorage.AppendEdgePolygon3DRep(); + BRepGraphInc::EdgePolygon3DRep& aUse = aStorage.ChangeEdgePolygon3DRep(aRepId); + aUse.ParentEdgeId = theEdge; + aUse.Polygon = thePolygon; + anEdge.Polygon3DRepId = aRepId; + } + } + else + { + aStorage.MarkRemoved(anEdge.Polygon3DRepId); + } + + myGraph->markModified(theEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::EdgeOps::ClearPersistentPolygon3D(const BRepGraph_EdgeId theEdge) +{ + myGraph->Editor().requireUnlocked(theEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::EdgeDef& anEdge = aStorage.ChangeEdge(theEdge); + + aStorage.MarkRemoved(anEdge.Polygon3DRepId); + + myGraph->markModified(theEdge); +} + +//================================================================================================= +// Owner-scoped CoEdgeOps setters (Use-based API) +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetPCurve(const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& theCurve2d, + const double theFirst, + const double theLast) +{ + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(theCoEdge); + + // Create or update the CoEdgeCurve2DRep (new owned use record) + if (!theCurve2d.IsNull()) + { + if (aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D())) + { + aStorage.SetRemoved(aCoEdge.Curve2DRepId, false); + BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.ChangeCoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + aUse.ParentCoEdgeId = theCoEdge; + aUse.Curve = theCurve2d; + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + } + else + { + const BRepGraph_CoEdgeCurve2DRepId aRepId = aStorage.AppendCoEdgeCurve2DRep(); + BRepGraphInc::CoEdgeCurve2DRep& aUse = aStorage.ChangeCoEdgeCurve2DRep(aRepId); + aUse.ParentCoEdgeId = theCoEdge; + aUse.Curve = theCurve2d; + aUse.ParamFirst = theFirst; + aUse.ParamLast = theLast; + aCoEdge.Curve2DRepId = aRepId; + } + } + else + { + aStorage.MarkRemoved(aCoEdge.Curve2DRepId); + } + + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::ClearPCurve(const BRepGraph_CoEdgeId theCoEdge) +{ + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(theCoEdge); + + aStorage.MarkRemoved(aCoEdge.Curve2DRepId); + + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetPersistentPolygon2D( + const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& thePolygon) +{ + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(theCoEdge); + + // Create or update the CoEdgePolygon2DRep (new owned use record) + if (!thePolygon.IsNull()) + { + if (aCoEdge.Polygon2DRepId.IsValid(aStorage.NbCoEdgePolygons2D())) + { + aStorage.SetRemoved(aCoEdge.Polygon2DRepId, false); + BRepGraphInc::CoEdgePolygon2DRep& aUse = + aStorage.ChangeCoEdgePolygon2DRep(aCoEdge.Polygon2DRepId); + aUse.ParentCoEdgeId = theCoEdge; + aUse.Polygon = thePolygon; + } + else + { + const BRepGraph_CoEdgePolygon2DRepId aRepId = aStorage.AppendCoEdgePolygon2DRep(); + BRepGraphInc::CoEdgePolygon2DRep& aUse = aStorage.ChangeCoEdgePolygon2DRep(aRepId); + aUse.ParentCoEdgeId = theCoEdge; + aUse.Polygon = thePolygon; + aCoEdge.Polygon2DRepId = aRepId; + } + } + else + { + aStorage.MarkRemoved(aCoEdge.Polygon2DRepId); + } + + myGraph->markModified(theCoEdge); +} + +//================================================================================================= + +void BRepGraph::EditorView::CoEdgeOps::SetPersistentPolygonOnTri( + const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& thePolygon) +{ + myGraph->Editor().requireUnlocked(theCoEdge, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.ChangeCoEdge(theCoEdge); + + // Create or update the CoEdgePolygonOnTriRep (new owned use record) + if (!thePolygon.IsNull()) + { + if (aCoEdge.PolygonOnTriRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri())) + { + aStorage.SetRemoved(aCoEdge.PolygonOnTriRepId, false); + BRepGraphInc::CoEdgePolygonOnTriRep& aUse = + aStorage.ChangeCoEdgePolygonOnTriRep(aCoEdge.PolygonOnTriRepId); + aUse.ParentCoEdgeId = theCoEdge; + aUse.Polygon = thePolygon; + } + else + { + const BRepGraph_CoEdgePolygonOnTriRepId aRepId = aStorage.AppendCoEdgePolygonOnTriRep(); + BRepGraphInc::CoEdgePolygonOnTriRep& aUse = aStorage.ChangeCoEdgePolygonOnTriRep(aRepId); + aUse.ParentCoEdgeId = theCoEdge; + aUse.Polygon = thePolygon; + aCoEdge.PolygonOnTriRepId = aRepId; + } + } + else + { + aStorage.MarkRemoved(aCoEdge.PolygonOnTriRepId); + } + + myGraph->markModified(theCoEdge); +} + +//================================================================================================= +// Owner-scoped FaceOps setters (Use-based API) +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetSurface(const BRepGraph_FaceId theFace, + const occ::handle& theSurface) +{ + myGraph->Editor().requireUnlocked(theFace, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::FaceDef& aFace = aStorage.ChangeFace(theFace); + + // Create or update the FaceSurfaceRep (new owned use record) + if (!theSurface.IsNull()) + { + if (aFace.SurfaceRepId.IsValid(aStorage.NbFaceSurfaces())) + { + aStorage.SetRemoved(aFace.SurfaceRepId, false); + BRepGraphInc::FaceSurfaceRep& aUse = aStorage.ChangeFaceSurfaceRep(aFace.SurfaceRepId); + aUse.ParentFaceId = theFace; + aUse.Surface = theSurface; + } + else + { + const BRepGraph_FaceSurfaceRepId aRepId = aStorage.AppendFaceSurfaceRep(); + BRepGraphInc::FaceSurfaceRep& aUse = aStorage.ChangeFaceSurfaceRep(aRepId); + aUse.ParentFaceId = theFace; + aUse.Surface = theSurface; + aFace.SurfaceRepId = aRepId; + } + } + else + { + aStorage.MarkRemoved(aFace.SurfaceRepId); + } + + myGraph->markModified(theFace); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::ClearSurface(const BRepGraph_FaceId theFace) +{ + myGraph->Editor().requireUnlocked(theFace, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::FaceDef& aFace = aStorage.ChangeFace(theFace); + + aStorage.MarkRemoved(aFace.SurfaceRepId); + + myGraph->markModified(theFace); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::SetPersistentTriangulation( + const BRepGraph_FaceId theFace, + const occ::handle& theTriangulation) +{ + myGraph->Editor().requireUnlocked(theFace, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::FaceDef& aFace = aStorage.ChangeFace(theFace); + + // Create or update the FaceTriangulationRep (new owned use record) + if (!theTriangulation.IsNull()) + { + if (aFace.TriangulationRepId.IsValid(aStorage.NbFaceTriangulations())) + { + aStorage.SetRemoved(aFace.TriangulationRepId, false); + BRepGraphInc::FaceTriangulationRep& aUse = + aStorage.ChangeFaceTriangulationRep(aFace.TriangulationRepId); + aUse.ParentFaceId = theFace; + aUse.Triangulation = theTriangulation; + } + else + { + const BRepGraph_FaceTriangulationRepId aRepId = aStorage.AppendFaceTriangulationRep(); + BRepGraphInc::FaceTriangulationRep& aUse = aStorage.ChangeFaceTriangulationRep(aRepId); + aUse.ParentFaceId = theFace; + aUse.Triangulation = theTriangulation; + aFace.TriangulationRepId = aRepId; + } + } + else + { + aStorage.MarkRemoved(aFace.TriangulationRepId); + } + + myGraph->markModified(theFace); +} + +//================================================================================================= + +void BRepGraph::EditorView::FaceOps::ClearPersistentTriangulation(const BRepGraph_FaceId theFace) +{ + myGraph->Editor().requireUnlocked(theFace, "BRepGraph::EditorView: locked item"); + BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraphInc::FaceDef& aFace = aStorage.ChangeFace(theFace); + + aStorage.MarkRemoved(aFace.TriangulationRepId); + + myGraph->markModified(theFace); } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_History.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_History.cxx deleted file mode 100644 index 65b0f974bb..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_History.cxx +++ /dev/null @@ -1,298 +0,0 @@ -// 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 - -#include - -namespace -{ -constexpr int THE_HISTORY_RECORD_BLOCK_SIZE = 32; -constexpr int THE_HISTORY_REPLACEMENT_BLOCK_SIZE = 4; -constexpr int THE_HISTORY_FILTERED_BLOCK_SIZE = 4; -constexpr int THE_HISTORY_DERIVED_BLOCK_SIZE = 8; -constexpr int THE_HISTORY_QUEUE_BLOCK_SIZE = 32; -} // namespace - -//================================================================================================= - -void BRepGraph_History::SetAllocator(const occ::handle& theAlloc) -{ - Standard_ASSERT_VOID(myRecords.IsEmpty(), - "SetAllocator: must be called before any records are added"); - myAllocator = theAlloc; - // Reconstruct internal containers with the new allocator. - myRecords = - NCollection_DynamicArray(THE_HISTORY_RECORD_BLOCK_SIZE, myAllocator); - myDerivedToOriginal = NCollection_DataMap(1, myAllocator); - myOriginalToDerived = - NCollection_DataMap>(1, - myAllocator); -} - -//================================================================================================= - -void BRepGraph_History::Record(const TCollection_AsciiString& theOpLabel, - const BRepGraph_NodeId theOriginal, - const NCollection_DynamicArray& theReplacements) -{ - if (!myEnabled) - { - return; - } - - // Append a new history record. - BRepGraph_HistoryRecord aRecord; - aRecord.OperationName = theOpLabel; - aRecord.SequenceNumber = myRecords.Size(); - if (!myAllocator.IsNull()) - { - aRecord.Mapping = - NCollection_DataMap>( - 1, - myAllocator); - } - aRecord.Mapping.Bind(theOriginal, theReplacements); - myRecords.Append(std::move(aRecord)); - - // Populate the bidirectional lookup maps. - // Skip self-referencing entries (aDerived == theOriginal) to avoid overwriting - // prior chain links in the reverse map. - NCollection_DynamicArray aFilteredReplacements(THE_HISTORY_FILTERED_BLOCK_SIZE, - myAllocator); - for (const BRepGraph_NodeId& aDerived : theReplacements) - { - if (aDerived != theOriginal) - { - myDerivedToOriginal.Bind(aDerived, theOriginal); - aFilteredReplacements.Append(aDerived); - } - } - - if (aFilteredReplacements.IsEmpty()) - { - return; - } - - if (myOriginalToDerived.IsBound(theOriginal)) - { - NCollection_DynamicArray& aDerivedVec = - myOriginalToDerived.ChangeFind(theOriginal); - for (const BRepGraph_NodeId& aDerived : aFilteredReplacements) - { - aDerivedVec.Append(aDerived); - } - } - else - { - myOriginalToDerived.Bind(theOriginal, std::move(aFilteredReplacements)); - } -} - -//================================================================================================= - -void BRepGraph_History::RecordBatch( - const TCollection_AsciiString& theOpLabel, - const NCollection_DynamicArray& theOriginals, - const NCollection_DynamicArray& theReplacements, - const TCollection_AsciiString& theExtraInfo) -{ - Standard_ASSERT_VOID(theOriginals.Size() == theReplacements.Size(), - "RecordBatch: mismatched vector lengths"); - if (!myEnabled || theOriginals.IsEmpty()) - { - return; - } - - const size_t aNbPairs = theOriginals.Size(); - - // Create a single history record with all mappings. - // Pre-size the Mapping to avoid DataMap rehashing. - BRepGraph_HistoryRecord aRecord; - aRecord.OperationName = theOpLabel; - aRecord.SequenceNumber = myRecords.Size(); - if (!myAllocator.IsNull()) - { - aRecord.Mapping = - NCollection_DataMap>( - aNbPairs, - myAllocator); - } - else - { - aRecord.Mapping.ReSize(aNbPairs); - } - aRecord.ExtraInfo = theExtraInfo; - - // Build mapping: each pair creates a 1-element replacement vector. - { - NCollection_DynamicArray::Iterator anOrigIt(theOriginals); - NCollection_DynamicArray::Iterator aReplIt(theReplacements); - for (; anOrigIt.More(); anOrigIt.Next(), aReplIt.Next()) - { - const BRepGraph_NodeId& anOriginal = anOrigIt.Value(); - const BRepGraph_NodeId& aReplacement = aReplIt.Value(); - Standard_ASSERT_VOID(!aRecord.Mapping.IsBound(anOriginal), - "RecordBatch: duplicate original node"); - NCollection_DynamicArray aRepVec(THE_HISTORY_REPLACEMENT_BLOCK_SIZE, - myAllocator); - aRepVec.Append(aReplacement); - aRecord.Mapping.Bind(anOriginal, std::move(aRepVec)); - } - } - myRecords.Append(std::move(aRecord)); - - // Update bidirectional lookup maps in bulk. - // Pre-size to avoid rehashing during batch insert. - myDerivedToOriginal.ReSize(myDerivedToOriginal.Extent() + aNbPairs); - myOriginalToDerived.ReSize(myOriginalToDerived.Extent() + aNbPairs); - - { - NCollection_DynamicArray::Iterator anOrigIt(theOriginals); - NCollection_DynamicArray::Iterator aReplIt(theReplacements); - for (; anOrigIt.More(); anOrigIt.Next(), aReplIt.Next()) - { - const BRepGraph_NodeId& anOriginal = anOrigIt.Value(); - const BRepGraph_NodeId& aReplacement = aReplIt.Value(); - if (aReplacement == anOriginal) - { - continue; - } - - myDerivedToOriginal.Bind(aReplacement, anOriginal); - - if (myOriginalToDerived.IsBound(anOriginal)) - { - myOriginalToDerived.ChangeFind(anOriginal).Append(aReplacement); - } - else - { - NCollection_DynamicArray aDerVec(THE_HISTORY_REPLACEMENT_BLOCK_SIZE, - myAllocator); - aDerVec.Append(aReplacement); - myOriginalToDerived.Bind(anOriginal, std::move(aDerVec)); - } - } - } -} - -//================================================================================================= - -BRepGraph_NodeId BRepGraph_History::FindOriginal(const BRepGraph_NodeId theModified) const -{ - // Walk the reverse map iteratively until a root node is reached. - // Limit iterations to the map extent to protect against cycles. - BRepGraph_NodeId aCurrent = theModified; - int aMaxIter = myDerivedToOriginal.Extent(); - while (myDerivedToOriginal.IsBound(aCurrent) && aMaxIter-- > 0) - { - const BRepGraph_NodeId& anOriginal = myDerivedToOriginal.Find(aCurrent); - if (anOriginal == aCurrent) - { - break; - } - aCurrent = anOriginal; - } - return aCurrent; -} - -//================================================================================================= - -NCollection_DynamicArray BRepGraph_History::FindDerived( - const BRepGraph_NodeId theOriginal) const -{ - // Collect all transitively derived nodes using iterative BFS. - // A visited set guards against infinite loops if cycles exist in the forward map. - NCollection_DynamicArray aResult(THE_HISTORY_DERIVED_BLOCK_SIZE); - NCollection_DynamicArray aQueue(THE_HISTORY_QUEUE_BLOCK_SIZE); - NCollection_Map aVisited; - - aQueue.Append(theOriginal); - aVisited.Add(theOriginal); - - size_t aFront = 0; - while (aFront < aQueue.Size()) - { - const BRepGraph_NodeId aNode = aQueue.Value(aFront++); - if (!myOriginalToDerived.IsBound(aNode)) - { - // Leaf node: only add if it is not the initial query node itself. - if (aNode != theOriginal) - { - aResult.Append(aNode); - } - continue; - } - - const NCollection_DynamicArray& aDirectDerived = - myOriginalToDerived.Find(aNode); - for (const BRepGraph_NodeId& aDerived : aDirectDerived) - { - if (aVisited.Add(aDerived)) - { - aQueue.Append(aDerived); - } - } - } - - // If there were no transitive leaves but there are direct derived nodes, - // return the direct derived for the non-recursive case. - if (aResult.IsEmpty() && myOriginalToDerived.IsBound(theOriginal)) - { - const NCollection_DynamicArray& aDirectDerived = - myOriginalToDerived.Find(theOriginal); - for (const BRepGraph_NodeId& aDerived : aDirectDerived) - { - aResult.Append(aDerived); - } - } - - return aResult; -} - -//================================================================================================= - -size_t BRepGraph_History::NbRecords() const -{ - return myRecords.Size(); -} - -//================================================================================================= - -const BRepGraph_HistoryRecord& BRepGraph_History::Record(const size_t theRecordIdx) const -{ - return myRecords.Value(theRecordIdx); -} - -//================================================================================================= - -void BRepGraph_History::SetEnabled(const bool theVal) -{ - myEnabled = theVal; -} - -//================================================================================================= - -bool BRepGraph_History::IsEnabled() const -{ - return myEnabled; -} - -//================================================================================================= - -void BRepGraph_History::Clear() -{ - myRecords.Clear(); - myDerivedToOriginal.Clear(); - myOriginalToDerived.Clear(); -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_History.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_History.hxx deleted file mode 100644 index 931992fbb7..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_History.hxx +++ /dev/null @@ -1,116 +0,0 @@ -// 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. - -#ifndef _BRepGraph_History_HeaderFile -#define _BRepGraph_History_HeaderFile - -#include -#include -#include -#include -#include -#include -#include - -class BRepGraph; - -//! Extracted history subsystem for BRepGraph. -//! -//! BRepGraph_History maintains an append-only log of modification events -//! and bidirectional lookup maps (original <-> derived) for efficient -//! history queries. Recording can be toggled on/off at runtime. -class BRepGraph_History -{ - friend class BRepGraph; - -public: - DEFINE_STANDARD_ALLOC - - //! Record a modification: theOriginal was replaced by theReplacements. - //! @param[in] theOpLabel human-readable operation name - //! @param[in] theOriginal node id before the operation - //! @param[in] theReplacements node ids after the operation - Standard_EXPORT void Record(const TCollection_AsciiString& theOpLabel, - const BRepGraph_NodeId theOriginal, - const NCollection_DynamicArray& theReplacements); - - //! Record a batch of 1-to-1 modifications in a single history event. - //! theOriginals[i] was replaced by theReplacements[i]. - //! More efficient than calling Record() in a loop: creates one HistoryRecord - //! and updates the bidirectional maps with minimal overhead. - //! @param[in] theOpLabel human-readable operation name - //! @param[in] theOriginals node ids before the operation - //! @param[in] theReplacements node ids after the operation (same length) - //! @param[in] theExtraInfo optional diagnostic info stored on the record - Standard_EXPORT void RecordBatch( - const TCollection_AsciiString& theOpLabel, - const NCollection_DynamicArray& theOriginals, - const NCollection_DynamicArray& theReplacements, - const TCollection_AsciiString& theExtraInfo = TCollection_AsciiString()); - - //! Walk backwards from a modified node to its original. - //! Follows the reverse map recursively until a root is reached. - //! @param[in] theModified node id to trace back - //! @return the root original node id, or theModified itself if not found - [[nodiscard]] Standard_EXPORT BRepGraph_NodeId - FindOriginal(const BRepGraph_NodeId theModified) const; - - //! Walk forwards from an original node to all derived nodes. - //! Follows the forward map recursively, collecting all leaves. - //! @param[in] theOriginal node id to trace forward - //! @return all transitively derived node ids - [[nodiscard]] Standard_EXPORT NCollection_DynamicArray FindDerived( - const BRepGraph_NodeId theOriginal) const; - - //! Number of recorded history events. - //! @return record count - [[nodiscard]] Standard_EXPORT size_t NbRecords() const; - - //! Access a record by index (0-based). - //! @param[in] theRecordIdx zero-based index into the records vector - //! @return the history record at the given index - [[nodiscard]] Standard_EXPORT const BRepGraph_HistoryRecord& Record( - const size_t theRecordIdx) const; - - //! Enable or disable history recording. - //! @param[in] theVal true to enable, false to disable - Standard_EXPORT void SetEnabled(const bool theVal); - - //! Query whether history recording is enabled. - //! @return true if recording is active - [[nodiscard]] Standard_EXPORT bool IsEnabled() const; - - //! Clear all records and lookup maps. - Standard_EXPORT void Clear(); - - //! Set the allocator for internal containers. - //! Must be called before any Record/RecordBatch calls. - //! @param[in] theAlloc allocator to use for internal maps - Standard_EXPORT void SetAllocator(const occ::handle& theAlloc); - -private: - occ::handle myAllocator; - - NCollection_DynamicArray myRecords; - - //! Reverse map: derived node -> original node. - NCollection_DataMap myDerivedToOriginal; - - //! Forward map: original node -> vector of derived nodes. - NCollection_DataMap> - myOriginalToDerived; - - bool myEnabled = true; -}; - -#endif // _BRepGraph_History_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_HistoryRecord.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_HistoryRecord.hxx deleted file mode 100644 index 3724eab98b..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_HistoryRecord.hxx +++ /dev/null @@ -1,49 +0,0 @@ -// 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. - -#ifndef _BRepGraph_HistoryRecord_HeaderFile -#define _BRepGraph_HistoryRecord_HeaderFile - -#include - -#include -#include -#include - -//! One atomic modification event recorded in the graph's history log. -//! -//! A HistoryRecord captures what happened during a single call to -//! BRepGraph::ApplyModification(): -//! - OperationName identifies the algorithm ("Sewing", "FilletEdge", ...). -//! - SequenceNumber provides total ordering of events. -//! - Mapping records the topological fate of each affected node: -//! original -> [replacement1, replacement2, ...] (split) -//! original -> [same_node] (modified in place) -//! original -> [] (deleted) -//! -//! The history log is append-only within a graph's lifetime. -struct BRepGraph_HistoryRecord -{ - TCollection_AsciiString OperationName; - size_t SequenceNumber = 0; - - //! Key: original node id before the operation. - //! Value: sequence of replacement node ids after the operation. - NCollection_DataMap> Mapping; - - //! Optional extra info for diagnostic/debugging purposes. - //! E.g., merge tolerance, canonical source index. - TCollection_AsciiString ExtraInfo; -}; - -#endif // _BRepGraph_HistoryRecord_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ItemId.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ItemId.hxx new file mode 100644 index 0000000000..53a358619f --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ItemId.hxx @@ -0,0 +1,172 @@ +// 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. + +#ifndef _BRepGraph_ItemId_HeaderFile +#define _BRepGraph_ItemId_HeaderFile + +#include +#include +#include +#include +#include + +#include +#include +#include + +//! Generic BRepGraph item identifier covering definitions and references. +//! Use-records are NOT included - they are session-local, not graph identity. +class BRepGraph_ItemId +{ +public: + //! Addressed graph item domain. + enum class Domain : uint8_t + { + None, + Node, + Reference + }; + + //! Construct an invalid item id. + BRepGraph_ItemId() = default; + + //! Construct a node item id. + BRepGraph_ItemId(const BRepGraph_NodeId theNode) + { + if (theNode.IsValid()) + { + myDomain = Domain::Node; + myIndex = theNode.Index; + myKind = static_cast(theNode.NodeKind); + } + } + + //! Construct a reference item id. + BRepGraph_ItemId(const BRepGraph_RefId theRef) + { + if (theRef.IsValid()) + { + myDomain = Domain::Reference; + myIndex = theRef.Index; + myKind = static_cast(theRef.RefKind); + } + } + + //! Return true if this item addresses a graph object. + [[nodiscard]] bool IsValid() const noexcept + { + if (myIndex == THE_INVALID_INDEX) + { + return false; + } + + switch (myDomain) + { + case Domain::Node: + return BRepGraph_NodeId::IsValidKind(static_cast(myKind)); + case Domain::Reference: + return BRepGraph_RefId::IsValidKind(static_cast(myKind)); + case Domain::None: + return false; + } + return false; + } + + //! Return the addressed domain. + [[nodiscard]] Domain ItemDomain() const noexcept { return myDomain; } + + //! Return true if this item addresses a definition node. + [[nodiscard]] bool IsNode() const noexcept { return myDomain == Domain::Node; } + + //! Return true if this item addresses a reference entry. + [[nodiscard]] bool IsReference() const noexcept { return myDomain == Domain::Reference; } + + //! Convert to node id. Returns invalid id for non-node items. + [[nodiscard]] BRepGraph_NodeId NodeId() const noexcept + { + return IsNode() && BRepGraph_NodeId::IsValidKind(static_cast(myKind)) + ? BRepGraph_NodeId(static_cast(myKind), myIndex) + : BRepGraph_NodeId(); + } + + //! Convert to reference id. Returns invalid id for non-reference items. + [[nodiscard]] BRepGraph_RefId RefId() const noexcept + { + return IsReference() && BRepGraph_RefId::IsValidKind(static_cast(myKind)) + ? BRepGraph_RefId(static_cast(myKind), myIndex) + : BRepGraph_RefId(); + } + + //! Return node kind. Valid only when IsNode() is true. + [[nodiscard]] BRepGraph_NodeId::Kind NodeKind() const noexcept + { + Standard_ASSERT_RETURN( + IsNode() && BRepGraph_NodeId::IsValidKind(static_cast(myKind)), + "BRepGraph_ItemId::NodeKind(): item is not a valid node", + BRepGraph_NodeId::Kind::Solid); + return static_cast(myKind); + } + + //! Return reference kind. Valid only when IsReference() is true. + [[nodiscard]] BRepGraph_RefId::Kind RefKind() const noexcept + { + Standard_ASSERT_RETURN( + IsReference() && BRepGraph_RefId::IsValidKind(static_cast(myKind)), + "BRepGraph_ItemId::RefKind(): item is not a valid reference", + BRepGraph_RefId::Kind::Shell); + return static_cast(myKind); + } + + //! Return item kind encoded in its own domain enum space. + [[nodiscard]] uint8_t RawKind() const noexcept { return myKind; } + + //! Return item kind encoded in its own domain enum space. + [[nodiscard]] uint8_t Kind() const noexcept { return RawKind(); } + + //! Return item per-kind index. + [[nodiscard]] uint32_t Index() const noexcept { return myIndex; } + + friend bool operator==(const BRepGraph_ItemId& theLeft, const BRepGraph_ItemId& theRight) noexcept + { + return theLeft.myDomain == theRight.myDomain && theLeft.myKind == theRight.myKind + && theLeft.myIndex == theRight.myIndex; + } + + friend bool operator!=(const BRepGraph_ItemId& theLeft, const BRepGraph_ItemId& theRight) noexcept + { + return !(theLeft == theRight); + } + +private: + static constexpr uint32_t THE_INVALID_INDEX = std::numeric_limits::max(); + + Domain myDomain = Domain::None; + uint32_t myIndex = THE_INVALID_INDEX; + uint8_t myKind = 0; +}; + +//! std::hash specialization for BRepGraph_ItemId. +template <> +struct std::hash +{ + size_t operator()(const BRepGraph_ItemId& theId) const noexcept + { + size_t aCombination[3]; + aCombination[0] = opencascade::hash(static_cast(theId.ItemDomain())); + aCombination[1] = opencascade::hash(static_cast(theId.RawKind())); + aCombination[2] = opencascade::hash(theId.Index()); + return opencascade::hashBytes(aCombination, sizeof(aCombination)); + } +}; + +#endif // _BRepGraph_ItemId_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ItemUID.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ItemUID.hxx new file mode 100644 index 0000000000..51678b2a24 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ItemUID.hxx @@ -0,0 +1,189 @@ +// 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. + +#ifndef _BRepGraph_ItemUID_HeaderFile +#define _BRepGraph_ItemUID_HeaderFile + +#include +#include +#include +#include + +#include +#include +#include +#include + +//! Durable BRepGraph item identity covering definition nodes and reference entries. +//! +//! BRepGraph_ItemId is a transient structural address. BRepGraph_ItemUID is the persistent +//! identity assigned at item creation and kept stable across compaction and vector reordering. +//! Representation/use records are not addressed here because they do not have persisted identity. +class BRepGraph_ItemUID +{ +public: + //! Addressed persistent identity domain. + enum class Domain : uint8_t + { + None, + Node, + Reference + }; + + //! Construct an invalid UID. + BRepGraph_ItemUID() = default; + + //! Construct a node UID. + static BRepGraph_ItemUID Node(const BRepGraph_NodeId::Kind theKind, const size_t theCounter) + { + return BRepGraph_ItemUID(Domain::Node, static_cast(theKind), theCounter); + } + + //! Construct a reference UID. + static BRepGraph_ItemUID Reference(const BRepGraph_RefId::Kind theKind, const size_t theCounter) + { + return BRepGraph_ItemUID(Domain::Reference, static_cast(theKind), theCounter); + } + + //! Return an invalid sentinel UID. + static BRepGraph_ItemUID Invalid() { return BRepGraph_ItemUID(); } + + //! Return true if this UID has a non-sentinel counter and a valid domain/kind pair. + [[nodiscard]] bool IsValid() const noexcept + { + if (myCounter == 0) + { + return false; + } + + switch (myDomain) + { + case Domain::Node: + return BRepGraph_NodeId::IsValidKind(static_cast(myKind)); + case Domain::Reference: + return BRepGraph_RefId::IsValidKind(static_cast(myKind)); + case Domain::None: + return false; + } + return false; + } + + //! Return the addressed identity domain. + [[nodiscard]] Domain ItemDomain() const noexcept { return myDomain; } + + [[nodiscard]] bool IsNode() const noexcept { return myDomain == Domain::Node; } + + [[nodiscard]] bool IsReference() const noexcept { return myDomain == Domain::Reference; } + + //! Return node kind. Valid only for node UIDs. + [[nodiscard]] BRepGraph_NodeId::Kind NodeKind() const noexcept + { + Standard_ASSERT_RETURN( + IsNode() && BRepGraph_NodeId::IsValidKind(static_cast(myKind)), + "BRepGraph_ItemUID::NodeKind(): UID is not a valid node UID", + BRepGraph_NodeId::Kind::Solid); + return static_cast(myKind); + } + + //! Return reference kind. Valid only for reference UIDs. + [[nodiscard]] BRepGraph_RefId::Kind RefKind() const noexcept + { + Standard_ASSERT_RETURN( + IsReference() && BRepGraph_RefId::IsValidKind(static_cast(myKind)), + "BRepGraph_ItemUID::RefKind(): UID is not a valid reference UID", + BRepGraph_RefId::Kind::Shell); + return static_cast(myKind); + } + + //! Return item kind encoded in its own domain enum space. + [[nodiscard]] uint8_t RawKind() const noexcept { return myKind; } + + //! Return the graph-wide monotonic UID counter. + [[nodiscard]] size_t Counter() const noexcept { return myCounter; } + + friend bool operator==(const BRepGraph_ItemUID& theLeft, + const BRepGraph_ItemUID& theRight) noexcept + { + if (theLeft.myCounter == 0 || theRight.myCounter == 0) + { + return (theLeft.myCounter == 0) == (theRight.myCounter == 0); + } + return theLeft.myDomain == theRight.myDomain && theLeft.myKind == theRight.myKind + && theLeft.myCounter == theRight.myCounter; + } + + friend bool operator!=(const BRepGraph_ItemUID& theLeft, + const BRepGraph_ItemUID& theRight) noexcept + { + return !(theLeft == theRight); + } + + friend bool operator<(const BRepGraph_ItemUID& theLeft, + const BRepGraph_ItemUID& theRight) noexcept + { + if (theLeft.myDomain != theRight.myDomain) + { + return static_cast(theLeft.myDomain) < static_cast(theRight.myDomain); + } + if (theLeft.myKind != theRight.myKind) + { + return theLeft.myKind < theRight.myKind; + } + return theLeft.myCounter < theRight.myCounter; + } + + //! Compute a hash value compatible with operator==. + [[nodiscard]] size_t HashValue() const noexcept + { + if (myCounter == 0) + { + return opencascade::hash(0); + } + + size_t aCombination[3]; + aCombination[0] = opencascade::hash(static_cast(myDomain)); + aCombination[1] = opencascade::hash(static_cast(myKind)); + aCombination[2] = opencascade::hash(myCounter); + return opencascade::hashBytes(aCombination, sizeof(aCombination)); + } + +private: + BRepGraph_ItemUID(const Domain theDomain, const uint8_t theKind, const size_t theCounter) + : myCounter(0), + myDomain(theDomain), + myKind(theKind) + { + Standard_ASSERT_VOID(theCounter > 0, "BRepGraph_ItemUID: counter must be > 0 for valid UIDs"); + Standard_ASSERT_VOID(theCounter <= std::numeric_limits::max(), + "BRepGraph_ItemUID: counter exceeds 32-bit storage"); + if (theCounter > 0 && theCounter <= std::numeric_limits::max()) + { + myCounter = static_cast(theCounter); + } + } + + uint32_t myCounter = 0; //!< 0 = invalid sentinel; valid counters start at 1. + Domain myDomain = Domain::None; //!< Identity domain. + uint8_t myKind = 0; //!< Kind encoded in the selected domain enum space. +}; + +static_assert(sizeof(BRepGraph_ItemUID) <= 8, "BRepGraph_ItemUID must stay compact"); + +//! std::hash specialization for NCollection_DefaultHasher support. +template <> +struct std::hash +{ + size_t operator()(const BRepGraph_ItemUID& theUID) const noexcept { return theUID.HashValue(); } +}; + +#endif // _BRepGraph_ItemUID_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Iterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Iterator.hxx index c356dd90ee..72acda7579 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Iterator.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Iterator.hxx @@ -16,10 +16,8 @@ #include #include - #include -#include #include //! @brief Type-safe, allocation-free iterator over BRepGraph definition nodes. @@ -39,16 +37,6 @@ //! @endcode namespace BRepGraph_IteratorDetail { -//! SFINAE helper: detect whether NodeType has an IsRemoved member (BaseDef types do). -template -struct HasIsRemoved : std::false_type -{ -}; - -template -struct HasIsRemoved().IsRemoved)>> : std::true_type -{ -}; //! Compile-time traits mapping from definition type to typed NodeId, //! count accessor, and definition accessor. @@ -60,7 +48,7 @@ struct NodeTraits { using TypedId = BRepGraph_SolidId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Solids().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Solids().Nb(); } static const BRepGraphInc::SolidDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -73,7 +61,7 @@ struct NodeTraits { using TypedId = BRepGraph_ShellId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Shells().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Shells().Nb(); } static const BRepGraphInc::ShellDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -86,7 +74,7 @@ struct NodeTraits { using TypedId = BRepGraph_FaceId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Faces().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Faces().Nb(); } static const BRepGraphInc::FaceDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -99,7 +87,7 @@ struct NodeTraits { using TypedId = BRepGraph_WireId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Wires().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Wires().Nb(); } static const BRepGraphInc::WireDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -112,7 +100,7 @@ struct NodeTraits { using TypedId = BRepGraph_EdgeId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Edges().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Edges().Nb(); } static const BRepGraphInc::EdgeDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -125,7 +113,7 @@ struct NodeTraits { using TypedId = BRepGraph_VertexId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Vertices().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Vertices().Nb(); } static const BRepGraphInc::VertexDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -138,7 +126,7 @@ struct NodeTraits { using TypedId = BRepGraph_ProductId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Products().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Products().Nb(); } static const BRepGraphInc::ProductDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -151,7 +139,7 @@ struct NodeTraits { using TypedId = BRepGraph_OccurrenceId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Occurrences().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Occurrences().Nb(); } static const BRepGraphInc::OccurrenceDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -164,7 +152,7 @@ struct NodeTraits { using TypedId = BRepGraph_CoEdgeId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().CoEdges().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().CoEdges().Nb(); } static const BRepGraphInc::CoEdgeDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -177,7 +165,7 @@ struct NodeTraits { using TypedId = BRepGraph_CompoundId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().Compounds().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().Compounds().Nb(); } static const BRepGraphInc::CompoundDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -190,7 +178,7 @@ struct NodeTraits { using TypedId = BRepGraph_CompSolidId; - static int Count(const BRepGraph& theGraph) { return theGraph.Topo().CompSolids().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Topo().CompSolids().Nb(); } static const BRepGraphInc::CompSolidDef& Get(const BRepGraph& theGraph, const TypedId theId) { @@ -255,10 +243,12 @@ private: //! Advance past any nodes marked as removed. void skipRemoved() { - if constexpr (!TheFullTraverse && BRepGraph_IteratorDetail::HasIsRemoved::value) + if constexpr (!TheFullTraverse) { - while (myCurrent < myLength && Current().IsRemoved) + while (myCurrent < myLength && myCurrent.IsRemoved(myGraph)) + { ++myCurrent; + } } } @@ -312,7 +302,7 @@ public: { } - [[nodiscard]] bool More() const { return myIndex < myRoots.Length(); } + [[nodiscard]] bool More() const { return myIndex < myRoots.Size(); } void Next() { ++myIndex; } @@ -326,8 +316,8 @@ public: NCollection_ForwardRangeSentinel end() const { return NCollection_ForwardRangeSentinel{}; } private: - const NCollection_DynamicArray& myRoots; - int myIndex = 0; + const NCollection_LinearVector& myRoots; + size_t myIndex = 0; }; #endif // _BRepGraph_Iterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.cxx index a26704fc8b..89d5834cf9 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.cxx @@ -13,10 +13,63 @@ #include +#include +#include + IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_Layer, Standard_Transient) //================================================================================================= +BRepGraph_Layer::BRepGraph_Layer() = default; + +//================================================================================================= + +const BRepGraph& BRepGraph_Layer::Graph() const +{ + if (myGraph == nullptr) + { + throw Standard_ProgramError("BRepGraph_Layer: layer is detached from graph"); + } + return *myGraph; +} + +//================================================================================================= + +void BRepGraph_Layer::OnAttached() noexcept {} + +//================================================================================================= + +void BRepGraph_Layer::OnDetached() noexcept {} + +//================================================================================================= + +void BRepGraph_Layer::attachGraph(BRepGraph* theGraph) noexcept +{ + if (myGraph != nullptr) + { + detachContext(); + } + myGraph = theGraph; + if (myGraph != nullptr) + { + OnAttached(); + } +} + +//================================================================================================= + +void BRepGraph_Layer::detachContext() noexcept +{ + if (myGraph == nullptr) + { + return; + } + OnDetached(); + myGraph = nullptr; +} + +//================================================================================================= + int BRepGraph_Layer::SubscribedKinds() const { return 0; @@ -24,12 +77,67 @@ int BRepGraph_Layer::SubscribedKinds() const //================================================================================================= +void BRepGraph_Layer::OnNodeRemoved(const BRepGraph_NodeId /*theNode*/) noexcept {} + +//================================================================================================= + +void BRepGraph_Layer::OnItemRemoved(const BRepGraph_ItemId theItem) noexcept +{ + if (!theItem.IsValid()) + { + return; + } + + switch (theItem.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: + OnNodeRemoved(theItem.NodeId()); + return; + case BRepGraph_ItemId::Domain::Reference: + OnRefRemoved(theItem.RefId()); + return; + case BRepGraph_ItemId::Domain::None: + return; + } +} + +//================================================================================================= + void BRepGraph_Layer::OnNodeModified(const BRepGraph_NodeId /*theNode*/) noexcept {} //================================================================================================= +void BRepGraph_Layer::OnItemModified(const BRepGraph_ItemId theItem) noexcept +{ + if (!theItem.IsValid()) + { + return; + } + + switch (theItem.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: + OnNodeModified(theItem.NodeId()); + return; + case BRepGraph_ItemId::Domain::Reference: + OnRefModified(theItem.RefId()); + return; + case BRepGraph_ItemId::Domain::None: + return; + } +} + +//================================================================================================= + +void BRepGraph_Layer::OnNodeReplaced(const BRepGraph_NodeId /*theOldNode*/, + const BRepGraph_NodeId /*theNewNode*/) noexcept +{ +} + +//================================================================================================= + void BRepGraph_Layer::OnNodesModified( - const NCollection_DynamicArray& /*theModifiedNodes*/) noexcept + const NCollection_Array1& /*theModifiedNodes*/) noexcept { } @@ -51,7 +159,6 @@ void BRepGraph_Layer::OnRefModified(const BRepGraph_RefId /*theRef*/) noexcept { //================================================================================================= void BRepGraph_Layer::OnRefsModified( - const NCollection_DynamicArray& /*theModifiedRefs*/, - const int /*theModifiedRefKindsMask*/) noexcept + const NCollection_Array1& /*theModifiedRefs*/) noexcept { } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.hxx index e5db1c5aa1..6b6938aceb 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Layer.hxx @@ -14,17 +14,21 @@ #ifndef _BRepGraph_Layer_HeaderFile #define _BRepGraph_Layer_HeaderFile +#include +#include +#include #include #include +#include #include -#include +#include #include #include #include #include +#include -class BRepGraph; class BRepGraph_LayerRegistry; //! @brief Abstract base class for named attribute layers. @@ -67,27 +71,40 @@ public: //! Layer identity (unique within a graph). [[nodiscard]] virtual const TCollection_AsciiString& Name() const = 0; - //! Called when a node is soft-removed. - //! @param[in] theNode the removed node - //! @param[in] theReplacement if valid, the node that replaces theNode - //! (e.g., sewing edge merge, deduplicate). If invalid, pure deletion. - //! Layers should migrate data from theNode to theReplacement when valid, - //! otherwise discard or archive removed-node data. - //! Implementations must validate theReplacement before dereferencing - //! graph data through it. + //! Called when a node is soft-removed without a replacement. + //! @param[in] theNode the removed node + //! Layers should discard or archive data associated with it. //! @warning Layer callbacks must not throw. They are called from noexcept //! notification paths (MutGuard destructors, deferred invalidation flush). - virtual void OnNodeRemoved(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) noexcept = 0; + Standard_EXPORT virtual void OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept; - //! Called after Compact with a unified old->new remap map. - //! Layer must remap all internal NodeId references using this map. - //! The map covers all node kinds (Vertex through CompSolid and future extensions). - //! Nodes absent from the map were removed during compaction - layers should - //! drop data associated with those nodes. - //! @param[in] theRemapMap maps old NodeId to new NodeId for all surviving nodes - virtual void OnCompact( - const NCollection_DataMap& theRemapMap) noexcept = 0; + //! Dispatch a generic item removal to the matching typed removal callback. + //! This is a non-virtual convenience entry point; typed callbacks remain the + //! extension points for derived layers. + //! @param[in] theItem the removed definition or reference + Standard_EXPORT void OnItemRemoved(const BRepGraph_ItemId theItem) noexcept; + + //! Called when a node is soft-removed and replaced by another node. + //! @param[in] theOldNode the removed node + //! @param[in] theNewNode the node that replaces theOldNode + //! Layers that store node-keyed data should migrate from + //! theOldNode to theNewNode when the replacement kind is + //! compatible. This is a structural lifecycle event, not an + //! algorithmic history record. + //! @warning Layer callbacks must not throw. They are called from noexcept + //! notification paths (MutGuard destructors, deferred invalidation flush). + Standard_EXPORT virtual void OnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept; + + //! Copy this source layer data into another graph. + //! The source graph is the graph this layer is attached to (Graph()). + //! @param[in] theCopy source graph, target graph, and source item id -> target item id remap + //! @note Missing source items were not copied; persistent layers should skip dependent records. + //! @note For BRepGraph_CopyRemap::Mode::Compact, the layer is being migrated in-place after + //! structural compaction. UID/ItemUID records and ref/rep entries should be remapped through + //! the item map. Stale entries (absent from the remap) should be dropped. + //! @warning This callback may allocate and is intentionally not noexcept. + Standard_EXPORT virtual void CopyTo(const BRepGraph_CopyRemap& theCopy) const = 0; //! Mark all cached values dirty (bulk invalidation). virtual void InvalidateAll() noexcept = 0; @@ -95,8 +112,6 @@ public: //! Clear all stored data. virtual void Clear() noexcept = 0; - // --- Modification event subscription --- - //! Return a bitmask of BRepGraph_NodeId::Kind values this layer subscribes to. //! Only modification events matching subscribed kinds are dispatched. //! Default: 0 (no subscription - no modification events received). @@ -110,14 +125,20 @@ public: //! @param[in] theNode the modified node Standard_EXPORT virtual void OnNodeModified(const BRepGraph_NodeId theNode) noexcept; + //! Dispatch a generic item modification to the matching typed modification callback. + //! This is a non-virtual convenience entry point; typed callbacks remain the + //! extension points for derived layers. + //! @param[in] theItem the modified definition or reference + Standard_EXPORT void OnItemModified(const BRepGraph_ItemId theItem) noexcept; + //! Called after EndDeferredInvalidation() with all nodes modified during //! the deferred scope. Only dispatched if at least one modified node's kind - //! matches SubscribedKinds(). The vector may contain nodes of kinds not + //! matches SubscribedKinds(). The array may contain nodes of kinds not //! subscribed to - layers should filter internally if needed. //! Default: no-op. //! @param[in] theModifiedNodes all modified, non-removed nodes Standard_EXPORT virtual void OnNodesModified( - const NCollection_DynamicArray& theModifiedNodes) noexcept; + const NCollection_Array1& theModifiedNodes) noexcept; //! Convenience: return bitmask bit for a given Kind. static int KindBit(const BRepGraph_NodeId::Kind theKind) @@ -125,8 +146,6 @@ public: return 1 << static_cast(theKind); } - // --- Reference modification event subscription --- - //! Return a bitmask of BRepGraph_RefId::Kind values this layer subscribes to. //! Only modification events matching subscribed ref kinds are dispatched. //! Default: 0 (no subscription). Must be constant for the layer's lifetime. @@ -148,14 +167,12 @@ public: //! Called after EndDeferredInvalidation() with all refs modified during //! the deferred scope. Only dispatched if at least one modified ref's kind - //! matches SubscribedRefKinds(). The vector may contain refs of kinds not + //! matches SubscribedRefKinds(). The array may contain refs of kinds not //! subscribed to - layers should filter internally if needed. //! Default: no-op. - //! @param[in] theModifiedRefs all modified, non-removed refs - //! @param[in] theModifiedRefKindsMask bitwise OR of all modified ref kinds + //! @param[in] theModifiedRefs all modified, non-removed refs Standard_EXPORT virtual void OnRefsModified( - const NCollection_DynamicArray& theModifiedRefs, - const int theModifiedRefKindsMask) noexcept; + const NCollection_Array1& theModifiedRefs) noexcept; //! Convenience: return bitmask bit for a given RefId::Kind. static int RefKindBit(const BRepGraph_RefId::Kind theKind) @@ -163,34 +180,74 @@ public: return 1 << static_cast(theKind); } - // --- Revision + owning-graph access --- - //! Monotonic revision counter incremented by touch() on every observable //! state change. Consumers compare stored revisions to detect staleness in O(1). //! Derived layers MUST call touch() from their mutators. [[nodiscard]] uint64_t Revision() const noexcept { return myRevision; } - //! Owning graph, set by the registry on RegisterLayer() and cleared on Unregister(). - //! Nullptr before registration or after unregistration. - [[nodiscard]] const BRepGraph* OwningGraph() const noexcept { return myOwningGraph; } - - //! Mutable accessor for layers that drive graph mutations (e.g. meshing). - [[nodiscard]] BRepGraph* OwningMutableGraph() const noexcept - { - return const_cast(myOwningGraph); - } - protected: + Standard_EXPORT BRepGraph_Layer(); + //! Bump the revision counter. void touch() noexcept { ++myRevision; } + //! True while this layer is registered in a live graph registry. + [[nodiscard]] bool IsAttached() const noexcept { return myGraph != nullptr; } + + //! Attached graph for read-only layer services. Raises Standard_ProgramError if detached. + [[nodiscard]] Standard_EXPORT const BRepGraph& Graph() const; + + //! Attached mutable graph for graph-owned service layers. Returns null if detached. + [[nodiscard]] BRepGraph* AttachedGraph() const noexcept { return myGraph; } + + template + [[nodiscard]] static BRepGraph_NodeId::Typed RemappedItem( + const BRepGraph_CopyRemap& theCopy, + const BRepGraph_NodeId::Typed theId) + { + if (!theId.IsValid()) + { + return BRepGraph_NodeId::Typed(); + } + const BRepGraph_ItemId* aMapped = theCopy.TargetItem(BRepGraph_ItemId(theId)); + if (aMapped == nullptr || !aMapped->IsNode()) + { + return BRepGraph_NodeId::Typed(); + } + return BRepGraph_NodeId::Typed::FromNodeId(aMapped->NodeId()); + } + + template + [[nodiscard]] static BRepGraph_RefId::Typed RemappedItem( + const BRepGraph_CopyRemap& theCopy, + const BRepGraph_RefId::Typed theId) + { + if (!theId.IsValid()) + { + return BRepGraph_RefId::Typed(); + } + const BRepGraph_ItemId* aMapped = theCopy.TargetItem(BRepGraph_ItemId(theId)); + if (aMapped == nullptr || !aMapped->IsReference()) + { + return BRepGraph_RefId::Typed(); + } + return BRepGraph_RefId::Typed::FromRefId(aMapped->RefId()); + } + + //! Called after the layer is attached to a graph registry. + Standard_EXPORT virtual void OnAttached() noexcept; + + //! Called before the layer is detached from a graph registry. + Standard_EXPORT virtual void OnDetached() noexcept; + private: friend class ::BRepGraph_LayerRegistry; - void setOwningGraph(const BRepGraph* theGraph) noexcept { myOwningGraph = theGraph; } + Standard_EXPORT void attachGraph(BRepGraph* theGraph) noexcept; + Standard_EXPORT void detachContext() noexcept; - const BRepGraph* myOwningGraph = nullptr; - uint64_t myRevision = 0; + BRepGraph* myGraph = nullptr; + uint64_t myRevision = 0; public: DEFINE_STANDARD_RTTIEXT(BRepGraph_Layer, Standard_Transient) diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerDeferred.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerDeferred.cxx new file mode 100644 index 0000000000..c3caf48d70 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerDeferred.cxx @@ -0,0 +1,520 @@ +// 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 + +#include +#include +#include +#include + +#include + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerDeferred, BRepGraph_Layer) + +//================================================================================================= + +bool BRepGraph_LayerDeferred::Entry::RepresentationStorage::ContainsKind( + const RepresentationKind theKind) const +{ + for (size_t aRepresentationIdx = 0; aRepresentationIdx < mySize; ++aRepresentationIdx) + { + if (representationPtr(aRepresentationIdx)->Kind == theKind) + { + return true; + } + } + return false; +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::Entry::RepresentationStorage::Append( + const Representation& theRepresentation) +{ + Standard_ASSERT_RAISE(mySize < THE_MAX_REPRESENTATIONS_PER_ITEM, + "Too many deferred representations for one graph item"); + ::new (static_cast(representationPtr(mySize))) Representation(theRepresentation); + ++mySize; +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::Entry::RepresentationStorage::Clear() +{ + for (size_t aRepresentationIdx = 0; aRepresentationIdx < mySize; ++aRepresentationIdx) + { + representationPtr(aRepresentationIdx)->~Representation(); + } + mySize = 0; +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::Entry::RepresentationStorage::copyFrom( + const RepresentationStorage& theOther) +{ + for (size_t aRepresentationIdx = 0; aRepresentationIdx < theOther.mySize; ++aRepresentationIdx) + { + Append(theOther.Value(aRepresentationIdx)); + } +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::Entry::RepresentationStorage::moveFrom( + RepresentationStorage& theOther) +{ + for (size_t aRepresentationIdx = 0; aRepresentationIdx < theOther.mySize; ++aRepresentationIdx) + { + ::new (static_cast(representationPtr(mySize))) + Representation(std::move(*theOther.representationPtr(aRepresentationIdx))); + ++mySize; + } + theOther.Clear(); +} + +//================================================================================================= + +BRepGraph_LayerDeferred::Entry::RepresentationStorage& BRepGraph_LayerDeferred::Entry:: + RepresentationStorage::operator=(const RepresentationStorage& theOther) +{ + if (this != &theOther) + { + Clear(); + copyFrom(theOther); + } + return *this; +} + +//================================================================================================= + +BRepGraph_LayerDeferred::Entry::RepresentationStorage& BRepGraph_LayerDeferred::Entry:: + RepresentationStorage::operator=(RepresentationStorage&& theOther) noexcept +{ + if (this != &theOther) + { + Clear(); + moveFrom(theOther); + } + return *this; +} + +//================================================================================================= + +BRepGraph_LayerDeferred::BRepGraph_LayerDeferred() + : myEntries(1) +{ +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_LayerDeferred::GetID() +{ + static const Standard_GUID THE_ID("9b6555f8-4353-414c-a067-c96a5e47fef7"); + return THE_ID; +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_LayerDeferred::ID() const +{ + return GetID(); +} + +//================================================================================================= + +const BRepGraph_LayerDeferred::Entry* BRepGraph_LayerDeferred::FindDeferred( + const BRepGraph_ItemId theItem) const +{ + return myEntries.Seek(theItem); +} + +//================================================================================================= + +bool BRepGraph_LayerDeferred::HasDeferred(const BRepGraph_ItemId theItem) const +{ + return myEntries.IsBound(theItem); +} + +//================================================================================================= + +const BRepGraph_LayerDeferred::Entry* BRepGraph_LayerDeferred::FindFirstDeferred( + const RepresentationKind theKind, + BRepGraph_ItemId* theItem) const +{ + for (NCollection_DataMap::Iterator anIt(myEntries); anIt.More(); + anIt.Next()) + { + if (!anIt.Value().Representations.ContainsKind(theKind)) + { + continue; + } + if (theItem != nullptr) + { + *theItem = anIt.Key(); + } + return &anIt.Value(); + } + return nullptr; +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::RegisterDeferred(const BRepGraph_ItemId theItem, + const TCollection_AsciiString& theProvider, + const TCollection_AsciiString& theSourceKey, + const RepresentationKind theRepresentationKind, + const TCollection_AsciiString& theRepresentationName, + const uint32_t theSourceIndex) +{ + Representation aRepresentation; + aRepresentation.Kind = theRepresentationKind; + aRepresentation.Name = theRepresentationName; + aRepresentation.SourceIndex = theSourceIndex; + RegisterDeferredRepresentations(theItem, theProvider, theSourceKey, &aRepresentation, 1); +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::RegisterDeferredRepresentations( + const BRepGraph_ItemId theItem, + const TCollection_AsciiString& theProvider, + const TCollection_AsciiString& theSourceKey, + const Representation* theRepresentations, + const size_t theNbRepresentations) +{ + if (!theItem.IsValid()) + { + return; + } + + if (theRepresentations == nullptr || theNbRepresentations == 0) + { + return; + } + + bool isChanged = false; + Entry& anEntry = myEntries.TryEmplaced(theItem); + if (!(anEntry.Provider == theProvider)) + { + anEntry.Provider = theProvider; + isChanged = true; + } + if (!(anEntry.SourceKey == theSourceKey)) + { + anEntry.SourceKey = theSourceKey; + isChanged = true; + } + + for (size_t aSrcRepresentationIdx = 0; aSrcRepresentationIdx < theNbRepresentations; + ++aSrcRepresentationIdx) + { + const Representation& aSrcRepresentation = theRepresentations[aSrcRepresentationIdx]; + bool isKnown = false; + for (size_t aStoredRepresentationIdx = 0; + aStoredRepresentationIdx < anEntry.Representations.Size(); + ++aStoredRepresentationIdx) + { + const Representation& aRepresentation = + anEntry.Representations.Value(aStoredRepresentationIdx); + if (aRepresentation.Kind == aSrcRepresentation.Kind + && aRepresentation.Role == aSrcRepresentation.Role + && aRepresentation.Name == aSrcRepresentation.Name + && aRepresentation.SourceIndex == aSrcRepresentation.SourceIndex) + { + isKnown = true; + break; + } + } + if (isKnown) + { + continue; + } + + anEntry.Representations.Append(aSrcRepresentation); + isChanged = true; + } + + if (isChanged) + { + lockItem(theItem); + if (myBulkRegistrationDepth != 0) + { + myHasBulkChanges = true; + } + else + { + touch(); + } + } +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::RegisterDeferredRepresentationsDirect( + const BRepGraph_ItemId theItem, + const TCollection_AsciiString& theProvider, + const TCollection_AsciiString& theSourceKey, + const Representation* theRepresentations, + const size_t theNbRepresentations) +{ + if (!theItem.IsValid() || theRepresentations == nullptr || theNbRepresentations == 0) + { + return; + } + Standard_ASSERT_RETURN( + !myEntries.IsBound(theItem), + "RegisterDeferredRepresentationsDirect requires an item without deferred representations", + Standard_VOID_RETURN); + + Entry anEntry; + anEntry.Provider = theProvider; + anEntry.SourceKey = theSourceKey; + for (size_t aRepresentationIdx = 0; aRepresentationIdx < theNbRepresentations; + ++aRepresentationIdx) + { + anEntry.Representations.Append(theRepresentations[aRepresentationIdx]); + } + + myEntries.Bind(theItem, std::move(anEntry)); + lockItem(theItem); + if (myBulkRegistrationDepth != 0) + { + myHasBulkChanges = true; + } + else + { + touch(); + } +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::UnregisterDeferred(const BRepGraph_ItemId theItem) +{ + if (!theItem.IsValid()) + { + return; + } + + if (myEntries.UnBind(theItem)) + { + unlockItem(theItem); + touch(); + } +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::ReserveDeferredItems(const size_t theNbItems) +{ + myEntries.ReSize(myEntries.Extent() + theNbItems); + if (BRepGraph* aGraph = AttachedGraph()) + { + occ::handle aLockLayer = + aGraph->LayerRegistry().Ensure(); + aLockLayer->ReserveOwners(theNbItems); + } +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::BeginBulkRegistration() +{ + if (myBulkRegistrationDepth == 0) + { + myHasBulkChanges = false; + } + ++myBulkRegistrationDepth; +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::EndBulkRegistration() +{ + Standard_ASSERT_RAISE(myBulkRegistrationDepth > 0, + "BRepGraph_LayerDeferred::EndBulkRegistration without matching begin"); + --myBulkRegistrationDepth; + if (myBulkRegistrationDepth != 0) + { + return; + } + + const bool hasChanges = myHasBulkChanges; + myHasBulkChanges = false; + if (!hasChanges) + { + return; + } + + touch(); + if (BRepGraph* aGraph = AttachedGraph()) + { + if (occ::handle aLockLayer = + aGraph->LayerRegistry().FindLayer(); + !aLockLayer.IsNull()) + { + aLockLayer->TouchOwners(); + } + } +} + +//================================================================================================= + +const TCollection_AsciiString& BRepGraph_LayerDeferred::Name() const +{ + static const TCollection_AsciiString THE_NAME("Deferred"); + return THE_NAME; +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept +{ + removeItem(BRepGraph_ItemId(theNode)); +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::OnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept +{ + const BRepGraph_ItemId anOldItem(theOldNode); + const BRepGraph_ItemId aNewItem(theNewNode); + const Entry* anOldEntry = myEntries.Seek(anOldItem); + if (anOldEntry == nullptr) + { + return; + } + + Entry anEntry = *anOldEntry; + myEntries.UnBind(anOldItem); + if (Entry* aTarget = myEntries.ChangeSeek(aNewItem)) + { + for (size_t i = 0; i < anEntry.Representations.Size(); ++i) + { + aTarget->Representations.Append(anEntry.Representations.Value(i)); + } + } + else + { + myEntries.Bind(aNewItem, anEntry); + } + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::CopyTo(const BRepGraph_CopyRemap& theCopy) const +{ + if (myEntries.IsEmpty()) + { + return; + } + + occ::handle aTarget = + theCopy.TargetGraph().LayerRegistry().Ensure(); + aTarget->ReserveDeferredItems(static_cast(myEntries.Extent())); + aTarget->BeginBulkRegistration(); + for (NCollection_DataMap::Iterator anIt(myEntries); anIt.More(); + anIt.Next()) + { + const BRepGraph_ItemId* aTargetItem = theCopy.TargetItem(anIt.Key()); + if (aTargetItem == nullptr || !aTargetItem->IsValid()) + { + continue; + } + + const Entry& anEntry = anIt.Value(); + Representation aRepresentations[Entry::RepresentationStorage::THE_MAX_REPRESENTATIONS_PER_ITEM]; + for (size_t aRepresentationIdx = 0; aRepresentationIdx < anEntry.Representations.Size(); + ++aRepresentationIdx) + { + aRepresentations[aRepresentationIdx] = anEntry.Representations.Value(aRepresentationIdx); + } + aTarget->RegisterDeferredRepresentationsDirect(*aTargetItem, + anEntry.Provider, + anEntry.SourceKey, + aRepresentations, + anEntry.Representations.Size()); + } + aTarget->EndBulkRegistration(); +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::OnRefRemoved(const BRepGraph_RefId theRef) noexcept +{ + removeItem(BRepGraph_ItemId(theRef)); +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::InvalidateAll() noexcept +{ + Clear(); +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::Clear() noexcept +{ + for (NCollection_DataMap::Iterator anIt(myEntries); anIt.More(); + anIt.Next()) + { + unlockItem(anIt.Key()); + } + myEntries.Clear(true); + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::removeItem(const BRepGraph_ItemId theItem) noexcept +{ + if (myEntries.UnBind(theItem)) + { + unlockItem(theItem); + touch(); + } +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::lockItem(const BRepGraph_ItemId theItem) +{ + if (BRepGraph* aGraph = AttachedGraph()) + { + occ::handle aLockLayer = + aGraph->LayerRegistry().Ensure(); + const bool isBulk = myBulkRegistrationDepth != 0; + const bool isChanged = aLockLayer->SetOwner(theItem, ID(), !isBulk); + myHasBulkChanges = myHasBulkChanges || (isBulk && isChanged); + } +} + +//================================================================================================= + +void BRepGraph_LayerDeferred::unlockItem(const BRepGraph_ItemId theItem) +{ + if (BRepGraph* aGraph = AttachedGraph()) + { + if (occ::handle aLockLayer = + aGraph->LayerRegistry().FindLayer(); + !aLockLayer.IsNull()) + { + aLockLayer->UnsetOwner(theItem, ID()); + } + } +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerDeferred.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerDeferred.hxx new file mode 100644 index 0000000000..165b0abeb4 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerDeferred.hxx @@ -0,0 +1,324 @@ +// 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. + +#ifndef _BRepGraph_LayerDeferred_HeaderFile +#define _BRepGraph_LayerDeferred_HeaderFile + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//! Base layer for postponed graph item loading. +//! +//! The layer stores provider-neutral deferred representation records and owns the lock +//! state through BRepGraph_LayerLock. Format-specific loaders, such as ODE or +//! STEP, should derive from this class or use the same representation contract rather +//! than storing deferred ownership in topology definitions. +class BRepGraph_LayerDeferred : public BRepGraph_Layer +{ +public: + //! Constructor for generic deferred layers. + Standard_EXPORT BRepGraph_LayerDeferred(); + + //! Representation category. + enum class RepresentationKind + { + Unknown, + Geometry, + Mesh, + Topology, + Assembly, + Parametric + }; + + //! One postponed representation attached to a graph item. + struct Representation + { + static constexpr uint32_t THE_INVALID_SOURCE_INDEX = std::numeric_limits::max(); + + RepresentationKind Kind = RepresentationKind::Unknown; + uint32_t Role = 0; + TCollection_AsciiString Name; + uint32_t SourceIndex = THE_INVALID_SOURCE_INDEX; + }; + + //! Deferred ownership entry for one graph item. + struct Entry + { + //! Small fixed representation list. Deferred graph items have a bounded number of + //! persisted representations, so avoid one heap allocation per item. + class RepresentationStorage + { + public: + static constexpr size_t THE_MAX_REPRESENTATIONS_PER_ITEM = 8; + + RepresentationStorage() = default; + + RepresentationStorage(const RepresentationStorage& theOther) { copyFrom(theOther); } + + RepresentationStorage(RepresentationStorage&& theOther) noexcept { moveFrom(theOther); } + + Standard_EXPORT RepresentationStorage& operator=(const RepresentationStorage& theOther); + Standard_EXPORT RepresentationStorage& operator=(RepresentationStorage&& theOther) noexcept; + + ~RepresentationStorage() { Clear(); } + + [[nodiscard]] size_t Size() const { return mySize; } + + [[nodiscard]] bool IsEmpty() const { return mySize == 0; } + + [[nodiscard]] Standard_EXPORT bool ContainsKind(const RepresentationKind theKind) const; + + [[nodiscard]] const Representation& Value(const size_t theIndex) const + { + Standard_ASSERT_RAISE(theIndex < mySize, "Deferred representation index is out of range"); + return *representationPtr(theIndex); + } + + [[nodiscard]] Representation& ChangeValue(const size_t theIndex) + { + Standard_ASSERT_RAISE(theIndex < mySize, "Deferred representation index is out of range"); + return *representationPtr(theIndex); + } + + [[nodiscard]] const Representation& First() const + { + Standard_ASSERT_RAISE(mySize > 0, "Deferred representation list is empty"); + return *representationPtr(0); + } + + Standard_EXPORT void Append(const Representation& theRepresentation); + + Standard_EXPORT void Clear(); + + private: + [[nodiscard]] const Representation* representationPtr(const size_t theIndex) const + { + return reinterpret_cast(&myRepresentationStorage[theIndex]); + } + + [[nodiscard]] Representation* representationPtr(const size_t theIndex) + { + return reinterpret_cast(&myRepresentationStorage[theIndex]); + } + + Standard_EXPORT void copyFrom(const RepresentationStorage& theOther); + Standard_EXPORT void moveFrom(RepresentationStorage& theOther); + + using RepresentationSlot = + std::aligned_storage_t; + + RepresentationSlot myRepresentationStorage[THE_MAX_REPRESENTATIONS_PER_ITEM]; + size_t mySize = 0; + }; + + TCollection_AsciiString Provider; + TCollection_AsciiString SourceKey; + RepresentationStorage Representations; + }; + + //! Return fixed layer type GUID. + [[nodiscard]] Standard_EXPORT static const Standard_GUID& GetID(); + + //! Return this layer type GUID. + [[nodiscard]] Standard_EXPORT const Standard_GUID& ID() const override; + + //! Return deferred entry for an item, or null if none exists. + [[nodiscard]] Standard_EXPORT const Entry* FindDeferred(const BRepGraph_ItemId theItem) const; + + //! Return deferred entry for a node, or null if none exists. + [[nodiscard]] const Entry* FindDeferred(const BRepGraph_NodeId theNode) const + { + return FindDeferred(BRepGraph_ItemId(theNode)); + } + + //! Return deferred entry for a reference, or null if none exists. + [[nodiscard]] const Entry* FindDeferred(const BRepGraph_RefId theRef) const + { + return FindDeferred(BRepGraph_ItemId(theRef)); + } + + //! Return true if an item has deferred representations. + [[nodiscard]] Standard_EXPORT bool HasDeferred(const BRepGraph_ItemId theItem) const; + + //! Return true if a node has deferred representations. + [[nodiscard]] bool HasDeferred(const BRepGraph_NodeId theNode) const + { + return HasDeferred(BRepGraph_ItemId(theNode)); + } + + //! Return true if a reference has deferred representations. + [[nodiscard]] bool HasDeferred(const BRepGraph_RefId theRef) const + { + return HasDeferred(BRepGraph_ItemId(theRef)); + } + + //! Register one postponed representation and lock the item. + Standard_EXPORT void RegisterDeferred(const BRepGraph_ItemId theItem, + const TCollection_AsciiString& theProvider, + const TCollection_AsciiString& theSourceKey, + const RepresentationKind theRepresentationKind, + const TCollection_AsciiString& theRepresentationName, + const uint32_t theSourceIndex); + + //! Register postponed representations for one item and lock the item once. + Standard_EXPORT void RegisterDeferredRepresentations(const BRepGraph_ItemId theItem, + const TCollection_AsciiString& theProvider, + const TCollection_AsciiString& theSourceKey, + const Representation* theRepresentations, + const size_t theNbRepresentations); + + //! Register postponed representations for a new item and lock it once. + //! + //! This is a trusted bulk-load fast path: the caller must ensure the item is valid, + //! has no existing deferred entry, and `theRepresentations` contains no duplicates. + Standard_EXPORT void RegisterDeferredRepresentationsDirect( + const BRepGraph_ItemId theItem, + const TCollection_AsciiString& theProvider, + const TCollection_AsciiString& theSourceKey, + const Representation* theRepresentations, + const size_t theNbRepresentations); + + //! Register one postponed node representation and lock the node. + void RegisterDeferred(const BRepGraph_NodeId theNode, + const TCollection_AsciiString& theProvider, + const TCollection_AsciiString& theSourceKey, + const RepresentationKind theRepresentationKind, + const TCollection_AsciiString& theRepresentationName, + const uint32_t theSourceIndex) + { + RegisterDeferred(BRepGraph_ItemId(theNode), + theProvider, + theSourceKey, + theRepresentationKind, + theRepresentationName, + theSourceIndex); + } + + //! Register one postponed reference representation and lock the reference. + void RegisterDeferred(const BRepGraph_RefId theRef, + const TCollection_AsciiString& theProvider, + const TCollection_AsciiString& theSourceKey, + const RepresentationKind theRepresentationKind, + const TCollection_AsciiString& theRepresentationName, + const uint32_t theSourceIndex) + { + RegisterDeferred(BRepGraph_ItemId(theRef), + theProvider, + theSourceKey, + theRepresentationKind, + theRepresentationName, + theSourceIndex); + } + + //! Remove all deferred representations for an item and unlock it. + Standard_EXPORT void UnregisterDeferred(const BRepGraph_ItemId theItem); + + //! Remove all deferred representations for a node and unlock it. + void UnregisterDeferred(const BRepGraph_NodeId theNode) + { + UnregisterDeferred(BRepGraph_ItemId(theNode)); + } + + //! Remove all deferred representations for a reference and unlock it. + void UnregisterDeferred(const BRepGraph_RefId theRef) + { + UnregisterDeferred(BRepGraph_ItemId(theRef)); + } + + //! Return true if at least one item has deferred representations. + [[nodiscard]] bool HasDeferredItems() const { return myEntries.Extent() != 0; } + + //! Return first deferred entry with at least one representation of the requested kind, or null. + [[nodiscard]] Standard_EXPORT const Entry* FindFirstDeferred( + const RepresentationKind theKind, + BRepGraph_ItemId* theItem = nullptr) const; + + //! Reserve deferred and lock layer buckets for bulk registration. + Standard_EXPORT void ReserveDeferredItems(const size_t theNbItems); + + //! Begin bulk deferred registration. Revision updates are postponed until EndBulkRegistration(). + Standard_EXPORT void BeginBulkRegistration(); + + //! Finish bulk deferred registration and publish one revision update if anything changed. + Standard_EXPORT void EndBulkRegistration(); + + //! Visit deferred entries. Callback receives item id and an entry copy; returning false stops. + template + void ForEachDeferred(VisitorT&& theVisitor) const + { + for (NCollection_DataMap::Iterator anIt(myEntries); anIt.More();) + { + const BRepGraph_ItemId anItem = anIt.Key(); + const Entry anEntry = anIt.Value(); + anIt.Next(); + if (!theVisitor(anItem, anEntry)) + { + break; + } + } + } + + //! Visit deferred entries with at least one representation of the requested kind. + //! Callback receives item id and an entry copy; returning false stops. + template + void ForEachDeferred(const RepresentationKind theKind, VisitorT&& theVisitor) const + { + for (NCollection_DataMap::Iterator anIt(myEntries); anIt.More();) + { + if (!anIt.Value().Representations.ContainsKind(theKind)) + { + anIt.Next(); + continue; + } + + const BRepGraph_ItemId anItem = anIt.Key(); + const Entry anEntry = anIt.Value(); + anIt.Next(); + if (!theVisitor(anItem, anEntry)) + { + break; + } + } + } + + Standard_EXPORT const TCollection_AsciiString& Name() const override; + Standard_EXPORT void OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept override; + Standard_EXPORT void OnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept override; + Standard_EXPORT void CopyTo(const BRepGraph_CopyRemap& theCopy) const override; + Standard_EXPORT void OnRefRemoved(const BRepGraph_RefId theRef) noexcept override; + Standard_EXPORT void InvalidateAll() noexcept override; + Standard_EXPORT void Clear() noexcept override; + + DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerDeferred, BRepGraph_Layer) + +private: + void removeItem(const BRepGraph_ItemId theItem) noexcept; + void lockItem(const BRepGraph_ItemId theItem); + void unlockItem(const BRepGraph_ItemId theItem); + +private: + NCollection_DataMap myEntries; + uint32_t myBulkRegistrationDepth = 0; + bool myHasBulkChanges = false; +}; + +#endif // _BRepGraph_LayerDeferred_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerHistory.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerHistory.cxx new file mode 100644 index 0000000000..233f8e55aa --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerHistory.cxx @@ -0,0 +1,1829 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerHistory, BRepGraph_Layer) + +namespace +{ +constexpr int THE_HISTORY_REPLACEMENT_BLOCK_SIZE = 4; +constexpr int THE_HISTORY_FILTERED_BLOCK_SIZE = 4; +constexpr int THE_HISTORY_DERIVED_BLOCK_SIZE = 8; +constexpr int THE_HISTORY_QUEUE_BLOCK_SIZE = 32; + +//! Pick the forward map that matches a record kind. Deletion records do +//! not populate a forward map; the caller short-circuits for that kind. +inline NCollection_DataMap>& + selectForwardMap( + const BRepGraph_LayerHistory::Kind theKind, + NCollection_DataMap>& theModified, + NCollection_DataMap>& theGenerated) +{ + return theKind == BRepGraph_LayerHistory::Kind::Generated ? theGenerated : theModified; +} + +inline NCollection_DataMap>& + selectUidForwardMap( + const BRepGraph_LayerHistory::Kind theKind, + NCollection_DataMap>& theModified, + NCollection_DataMap>& theGenerated) +{ + return theKind == BRepGraph_LayerHistory::Kind::Generated ? theGenerated : theModified; +} + +inline NCollection_DataMap>& + selectItemUidForwardMap( + const BRepGraph_LayerHistory::Kind theKind, + NCollection_DataMap>& + theModified, + NCollection_DataMap>& + theGenerated) +{ + return theKind == BRepGraph_LayerHistory::Kind::Generated ? theGenerated : theModified; +} + +inline void appendUniqueUid(NCollection_LinearVector& theUids, + const BRepGraph_UID& theUid) +{ + for (const BRepGraph_UID& aSeen : theUids) + { + if (aSeen == theUid) + { + return; + } + } + theUids.Append(theUid); +} + +inline void appendUniqueItemUid(NCollection_LinearVector& theUids, + const BRepGraph_ItemUID& theUid) +{ + for (const BRepGraph_ItemUID& aSeen : theUids) + { + if (aSeen == theUid) + { + return; + } + } + theUids.Append(theUid); +} + +inline void appendUniqueNode(NCollection_LinearVector& theNodes, + const BRepGraph_NodeId theNode) +{ + for (const BRepGraph_NodeId& aSeen : theNodes) + { + if (aSeen == theNode) + { + return; + } + } + theNodes.Append(theNode); +} + +inline void appendDerivedOrigin( + NCollection_DataMap>& theMap, + const BRepGraph_NodeId theDerived, + const BRepGraph_NodeId theOriginal) +{ + if (theMap.IsBound(theDerived)) + { + appendUniqueNode(theMap.ChangeFind(theDerived), theOriginal); + return; + } + + NCollection_LinearVector anOrigins(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + anOrigins.Append(theOriginal); + theMap.Bind(theDerived, std::move(anOrigins)); +} + +template +inline NCollection_LinearVector copyNodes( + const ContainerT& theSource, + const int theBlockSize = THE_HISTORY_REPLACEMENT_BLOCK_SIZE) +{ + NCollection_LinearVector aCopy(theBlockSize); + for (const BRepGraph_NodeId& aNode : theSource) + { + aCopy.Append(aNode); + } + return aCopy; +} + +template +inline NCollection_LinearVector copyUids( + const ContainerT& theSource, + const int theBlockSize = THE_HISTORY_REPLACEMENT_BLOCK_SIZE) +{ + NCollection_LinearVector aCopy(theBlockSize); + for (const BRepGraph_UID& aUid : theSource) + { + aCopy.Append(aUid); + } + return aCopy; +} + +template +inline NCollection_LinearVector copyItemUids( + const ContainerT& theSource, + const int theBlockSize = THE_HISTORY_REPLACEMENT_BLOCK_SIZE) +{ + NCollection_LinearVector aCopy(theBlockSize); + for (const BRepGraph_ItemUID& aUid : theSource) + { + aCopy.Append(aUid); + } + return aCopy; +} + +inline BRepGraph_ItemUID itemUidOf(const BRepGraph* theGraph, const BRepGraph_ItemId theItem) +{ + return theGraph != nullptr ? theGraph->UIDs().Of(theItem) : BRepGraph_ItemUID(); +} + +inline NCollection_LinearVector itemUidsOfNodes( + const BRepGraph* theGraph, + const NCollection_Array1& theNodes) +{ + NCollection_LinearVector aResult(THE_HISTORY_FILTERED_BLOCK_SIZE); + if (theGraph == nullptr) + { + return aResult; + } + + for (const BRepGraph_NodeId& aNode : theNodes) + { + const BRepGraph_ItemUID aUid = theGraph->UIDs().Of(BRepGraph_ItemId(aNode)); + if (aUid.IsValid()) + { + appendUniqueItemUid(aResult, aUid); + } + } + return aResult; +} + +inline void appendItemUidForward( + NCollection_DataMap>& theMap, + const BRepGraph_ItemUID& theOriginal, + const BRepGraph_ItemUID& theDerived) +{ + if (theMap.IsBound(theOriginal)) + { + appendUniqueItemUid(theMap.ChangeFind(theOriginal), theDerived); + return; + } + + NCollection_LinearVector aFresh(THE_HISTORY_FILTERED_BLOCK_SIZE); + aFresh.Append(theDerived); + theMap.Bind(theOriginal, std::move(aFresh)); +} +} // namespace + +//================================================================================================= + +BRepGraph_LayerHistory::BRepGraph_LayerHistory() + : myRecords(32), + myDerivedToOriginals(1), + myOriginalToModified(1), + myOriginalToGenerated(1), + myDeleted(1), + myUidOriginalToModified(1), + myUidOriginalToGenerated(1), + myUidKnownInputs(1), + myUidDeleted(1), + myItemUidOriginalToModified(1), + myItemUidOriginalToGenerated(1), + myItemUidKnownInputs(1), + myItemUidDeleted(1) +{ +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_LayerHistory::GetID() +{ + static const Standard_GUID THE_ID("7b6756a1-0f98-48b0-9985-c0ab41e38012"); + return THE_ID; +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_LayerHistory::ID() const +{ + return GetID(); +} + +//================================================================================================= + +const TCollection_AsciiString& BRepGraph_LayerHistory::Name() const +{ + static const TCollection_AsciiString THE_NAME("History"); + return THE_NAME; +} + +//================================================================================================= + +void BRepGraph_LayerHistory::OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept +{ + if (!myEnabled || !theNode.IsValid()) + { + return; + } + + try + { + NCollection_LinearVector aDeleted(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aDeleted.Append(theNode); + RecordDeleted(TCollection_AsciiString("Graph:RemoveNode"), aDeleted.ToArray1()); + } + catch (...) + { + } +} + +//================================================================================================= + +void BRepGraph_LayerHistory::rebuildCaches() +{ + myDerivedToOriginals.Clear(); + myOriginalToModified.Clear(); + myOriginalToGenerated.Clear(); + myDeleted.Clear(); + myUidOriginalToModified.Clear(); + myUidOriginalToGenerated.Clear(); + myUidKnownInputs.Clear(); + myUidDeleted.Clear(); + myItemUidOriginalToModified.Clear(); + myItemUidOriginalToGenerated.Clear(); + myItemUidKnownInputs.Clear(); + myItemUidDeleted.Clear(); + + for (const Event& aRecord : myRecords) + { + for (NCollection_DataMap>::Iterator + anIt(aRecord.Mapping); + anIt.More(); + anIt.Next()) + { + const BRepGraph_NodeId& anOriginal = anIt.Key(); + const NCollection_LinearVector& aReplacements = anIt.Value(); + if (aRecord.RecordKind == BRepGraph_LayerHistory::Kind::Deleted || aReplacements.IsEmpty()) + { + myDeleted.Add(anOriginal); + continue; + } + + NCollection_DataMap>& aFwd = + selectForwardMap(aRecord.RecordKind, myOriginalToModified, myOriginalToGenerated); + for (const BRepGraph_NodeId& aDerived : aReplacements) + { + if (aDerived == anOriginal) + { + continue; + } + appendDerivedOrigin(myDerivedToOriginals, aDerived, anOriginal); + if (aFwd.IsBound(anOriginal)) + { + appendUniqueNode(aFwd.ChangeFind(anOriginal), aDerived); + } + else + { + NCollection_LinearVector aFresh(THE_HISTORY_FILTERED_BLOCK_SIZE); + aFresh.Append(aDerived); + aFwd.Bind(anOriginal, std::move(aFresh)); + } + } + + if (aRecord.RecordKind == BRepGraph_LayerHistory::Kind::Replaced) + { + myDeleted.Add(anOriginal); + } + } + + for (NCollection_DataMap>::Iterator anIt( + aRecord.UidMapping); + anIt.More(); + anIt.Next()) + { + const BRepGraph_UID& anOriginal = anIt.Key(); + myUidKnownInputs.Add(anOriginal); + const NCollection_LinearVector& aReplacements = anIt.Value(); + if (aRecord.RecordKind == BRepGraph_LayerHistory::Kind::Deleted || aReplacements.IsEmpty()) + { + myUidDeleted.Add(anOriginal); + continue; + } + + NCollection_DataMap>& aFwd = + selectUidForwardMap(aRecord.RecordKind, myUidOriginalToModified, myUidOriginalToGenerated); + for (const BRepGraph_UID& aDerived : aReplacements) + { + if (aDerived != anOriginal) + { + if (aFwd.IsBound(anOriginal)) + { + appendUniqueUid(aFwd.ChangeFind(anOriginal), aDerived); + } + else + { + NCollection_LinearVector aFresh(THE_HISTORY_FILTERED_BLOCK_SIZE); + aFresh.Append(aDerived); + aFwd.Bind(anOriginal, std::move(aFresh)); + } + } + } + + if (aRecord.RecordKind == BRepGraph_LayerHistory::Kind::Replaced) + { + myUidDeleted.Add(anOriginal); + } + } + + for (NCollection_DataMap>::Iterator + anIt(aRecord.ItemUidMapping); + anIt.More(); + anIt.Next()) + { + const BRepGraph_ItemUID& anOriginal = anIt.Key(); + myItemUidKnownInputs.Add(anOriginal); + const NCollection_LinearVector& aReplacements = anIt.Value(); + if (aRecord.RecordKind == BRepGraph_LayerHistory::Kind::Deleted || aReplacements.IsEmpty()) + { + myItemUidDeleted.Add(anOriginal); + continue; + } + + NCollection_DataMap>& aFwd = + selectItemUidForwardMap(aRecord.RecordKind, + myItemUidOriginalToModified, + myItemUidOriginalToGenerated); + for (const BRepGraph_ItemUID& aDerived : aReplacements) + { + if (aDerived != anOriginal) + { + appendItemUidForward(aFwd, anOriginal, aDerived); + } + } + + if (aRecord.RecordKind == BRepGraph_LayerHistory::Kind::Replaced) + { + myItemUidDeleted.Add(anOriginal); + } + } + } + + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::CopyTo(const BRepGraph_CopyRemap& theCopy) const +{ + if (myRecords.IsEmpty()) + { + return; + } + + if (theCopy.IsCompact()) + { + // Compact mode: remap node-domain keys through ItemId map, preserve durable UID/ItemUID, + // skip previous Compact:Remap records, drop stale node-only entries. + occ::handle aTarget = + theCopy.TargetGraph().LayerRegistry().Ensure(); + aTarget->SetEnabled(true); + + static const TCollection_AsciiString THE_COMPACT_REMAP_LABEL("Compact:Remap"); + NCollection_DynamicArray aNewRecords(32); + + for (const Event& aRecord : myRecords) + { + if (aRecord.OperationName == THE_COMPACT_REMAP_LABEL) + { + continue; + } + + Event aNewRecord; + aNewRecord.OperationName = aRecord.OperationName; + aNewRecord.SequenceNumber = aNewRecords.Size(); + aNewRecord.RecordKind = aRecord.RecordKind; + aNewRecord.ExtraInfo = aRecord.ExtraInfo; + + // Preserve durable UID mappings as-is. + for (NCollection_DataMap>::Iterator + anIt(aRecord.UidMapping); + anIt.More(); + anIt.Next()) + { + aNewRecord.UidMapping.Bind(anIt.Key(), copyUids(anIt.Value())); + } + for (NCollection_DataMap>::Iterator + anIt(aRecord.ItemUidMapping); + anIt.More(); + anIt.Next()) + { + aNewRecord.ItemUidMapping.Bind(anIt.Key(), copyItemUids(anIt.Value())); + } + + // Remap NodeId-based entries through the ItemId map. + for (NCollection_DataMap>::Iterator + anIt(aRecord.Mapping); + anIt.More(); + anIt.Next()) + { + const BRepGraph_ItemId* aNewOriginalItem = theCopy.TargetItem(BRepGraph_ItemId(anIt.Key())); + if (aNewOriginalItem == nullptr || !aNewOriginalItem->IsNode()) + { + continue; + } + + NCollection_LinearVector aNewImages(THE_HISTORY_FILTERED_BLOCK_SIZE); + for (const BRepGraph_NodeId& anOldImage : anIt.Value()) + { + const BRepGraph_ItemId* aNewImageItem = theCopy.TargetItem(BRepGraph_ItemId(anOldImage)); + if (aNewImageItem != nullptr && aNewImageItem->IsNode()) + { + appendUniqueNode(aNewImages, aNewImageItem->NodeId()); + } + } + aNewRecord.Mapping.Bind(aNewOriginalItem->NodeId(), std::move(aNewImages)); + } + + if (!aNewRecord.Mapping.IsEmpty() || !aNewRecord.UidMapping.IsEmpty() + || !aNewRecord.ItemUidMapping.IsEmpty()) + { + aNewRecords.Append(std::move(aNewRecord)); + } + } + + aTarget->myRecords = std::move(aNewRecords); + aTarget->rebuildCaches(); + aTarget->SetEnabled(myEnabled); + return; + } + + occ::handle aTarget = + theCopy.TargetGraph().LayerRegistry().Ensure(); + aTarget->SetEnabled(true); + bool hasRawRecordsAppended = false; + + auto toSourceItem = [&](const BRepGraph_ItemUID& theUID) { + return theCopy.SourceGraph().UIDs().ItemIdFrom(theUID); + }; + + auto replayItems = [&](const TCollection_AsciiString& theOperationName, + const BRepGraph_LayerHistory::Kind theKind, + const BRepGraph_ItemId theSourceOriginal, + const BRepGraph_ItemUID& theDurableOriginalUID, + const NCollection_LinearVector& theSourceImages) { + const BRepGraph_ItemId* aTargetOriginal = theCopy.TargetItem(theSourceOriginal); + if (aTargetOriginal == nullptr || !aTargetOriginal->IsValid()) + { + if (theKind == BRepGraph_LayerHistory::Kind::Deleted) + { + if (theSourceOriginal.IsNode() + && theCopy.TargetGraphConst().Topo().Gen().TopoEntity(theSourceOriginal.NodeId()) + != nullptr) + { + NCollection_LinearVector aDeleted(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aDeleted.Append(theSourceOriginal.NodeId()); + aTarget->RecordDeleted(theOperationName, aDeleted.ToArray1()); + } + + if (theDurableOriginalUID.IsValid()) + { + NCollection_LinearVector aDeleted(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aDeleted.Append(theDurableOriginalUID); + aTarget->RecordDeletedItemUid(theOperationName, aDeleted.ToArray1()); + } + } + return; + } + + NCollection_LinearVector aTargetImageUIDs(THE_HISTORY_FILTERED_BLOCK_SIZE); + NCollection_LinearVector aTargetImageNodes(THE_HISTORY_FILTERED_BLOCK_SIZE); + bool areAllItemsNodes = aTargetOriginal->IsNode(); + for (const BRepGraph_ItemId& aSourceImage : theSourceImages) + { + const BRepGraph_ItemId* aTargetImage = theCopy.TargetItem(aSourceImage); + if (aTargetImage == nullptr || !aTargetImage->IsValid()) + { + continue; + } + + const BRepGraph_ItemUID aTargetUID = theCopy.TargetUID(*aTargetImage); + if (aTargetUID.IsValid()) + { + appendUniqueItemUid(aTargetImageUIDs, aTargetUID); + } + + if (areAllItemsNodes && aTargetImage->IsNode()) + { + appendUniqueNode(aTargetImageNodes, aTargetImage->NodeId()); + } + else + { + areAllItemsNodes = false; + } + } + + if (theKind == BRepGraph_LayerHistory::Kind::Deleted) + { + if (aTargetOriginal->IsNode()) + { + NCollection_LinearVector aDeleted(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aDeleted.Append(aTargetOriginal->NodeId()); + aTarget->RecordDeleted(theOperationName, aDeleted.ToArray1()); + } + if (theDurableOriginalUID.IsValid()) + { + NCollection_LinearVector aDeleted(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aDeleted.Append(theDurableOriginalUID); + aTarget->RecordDeletedItemUid(theOperationName, aDeleted.ToArray1()); + } + return; + } + + if (aTargetImageUIDs.IsEmpty()) + { + return; + } + + if (areAllItemsNodes) + { + aTarget->Record(theOperationName, + aTargetOriginal->NodeId(), + aTargetImageNodes.ToArray1(), + theKind); + return; + } + + const BRepGraph_ItemUID aTargetOriginalUID = theCopy.TargetUID(*aTargetOriginal); + if (aTargetOriginalUID.IsValid()) + { + aTarget->RecordItemUid(theOperationName, + aTargetOriginalUID, + aTargetImageUIDs.ToArray1(), + theKind); + } + }; + + for (const Event& aRecord : myRecords) + { + if (aRecord.RecordKind == BRepGraph_LayerHistory::Kind::Deleted && !aRecord.Mapping.IsEmpty()) + { + Event aNewRecord; + aNewRecord.OperationName = aRecord.OperationName; + aNewRecord.SequenceNumber = aTarget->myRecords.Size(); + aNewRecord.RecordKind = aRecord.RecordKind; + aNewRecord.ExtraInfo = aRecord.ExtraInfo; + + for (NCollection_DataMap>::Iterator + anIt(aRecord.Mapping); + anIt.More(); + anIt.Next()) + { + BRepGraph_NodeId aTargetNode; + const BRepGraph_ItemId* aTargetOriginal = theCopy.TargetItem(BRepGraph_ItemId(anIt.Key())); + if (aTargetOriginal != nullptr && aTargetOriginal->IsNode()) + { + aTargetNode = aTargetOriginal->NodeId(); + } + else if (theCopy.TargetGraphConst().Topo().Gen().TopoEntity(anIt.Key()) != nullptr) + { + aTargetNode = anIt.Key(); + } + if (!aTargetNode.IsValid()) + { + continue; + } + + NCollection_LinearVector aTargetImages(THE_HISTORY_FILTERED_BLOCK_SIZE); + for (const BRepGraph_NodeId& aNode : anIt.Value()) + { + const BRepGraph_ItemId* aTargetImage = theCopy.TargetItem(BRepGraph_ItemId(aNode)); + if (aTargetImage != nullptr && aTargetImage->IsNode()) + { + appendUniqueNode(aTargetImages, aTargetImage->NodeId()); + } + } + aNewRecord.Mapping.Bind(aTargetNode, std::move(aTargetImages)); + } + + if (!aRecord.ItemUidMapping.IsEmpty()) + { + for (NCollection_DataMap>::Iterator + anIt(aRecord.ItemUidMapping); + anIt.More(); + anIt.Next()) + { + aNewRecord.ItemUidMapping.Bind(anIt.Key(), copyItemUids(anIt.Value())); + } + } + + if (!aNewRecord.Mapping.IsEmpty() || !aNewRecord.ItemUidMapping.IsEmpty()) + { + aTarget->myRecords.Append(std::move(aNewRecord)); + hasRawRecordsAppended = true; + } + continue; + } + + if (!aRecord.ItemUidMapping.IsEmpty()) + { + for (NCollection_DataMap>::Iterator + anIt(aRecord.ItemUidMapping); + anIt.More(); + anIt.Next()) + { + NCollection_LinearVector aSourceImages(THE_HISTORY_FILTERED_BLOCK_SIZE); + for (const BRepGraph_ItemUID& aUid : anIt.Value()) + { + const BRepGraph_ItemId aSourceImage = toSourceItem(aUid); + if (aSourceImage.IsValid()) + { + aSourceImages.Append(aSourceImage); + } + } + replayItems(aRecord.OperationName, + aRecord.RecordKind, + toSourceItem(anIt.Key()), + anIt.Key(), + aSourceImages); + } + continue; + } + + if (!aRecord.UidMapping.IsEmpty()) + { + for (NCollection_DataMap>::Iterator + anIt(aRecord.UidMapping); + anIt.More(); + anIt.Next()) + { + if (aRecord.RecordKind == BRepGraph_LayerHistory::Kind::Deleted) + { + NCollection_LinearVector aDeleted(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aDeleted.Append(anIt.Key()); + aTarget->RecordDeletedUid(aRecord.OperationName, aDeleted.ToArray1()); + continue; + } + + NCollection_LinearVector aSourceImages(THE_HISTORY_FILTERED_BLOCK_SIZE); + NCollection_LinearVector aTargetImageUIDs(THE_HISTORY_FILTERED_BLOCK_SIZE); + for (const BRepGraph_UID& aUid : anIt.Value()) + { + const BRepGraph_ItemId aSourceImage = + toSourceItem(BRepGraph_ItemUID::Node(aUid.Kind, aUid.Counter)); + if (aSourceImage.IsValid()) + { + aSourceImages.Append(aSourceImage); + const BRepGraph_ItemId* aTargetImage = theCopy.TargetItem(aSourceImage); + if (aTargetImage != nullptr && aTargetImage->IsNode()) + { + const BRepGraph_UID aTargetUID = + theCopy.TargetGraphConst().UIDs().Of(aTargetImage->NodeId()); + if (aTargetUID.IsValid()) + { + appendUniqueUid(aTargetImageUIDs, aTargetUID); + } + } + } + } + if (!aTargetImageUIDs.IsEmpty()) + { + aTarget->RecordUid(aRecord.OperationName, + anIt.Key(), + aTargetImageUIDs.ToArray1(), + aRecord.RecordKind); + } + } + continue; + } + + for (NCollection_DataMap>::Iterator + anIt(aRecord.Mapping); + anIt.More(); + anIt.Next()) + { + NCollection_LinearVector aSourceImages(THE_HISTORY_FILTERED_BLOCK_SIZE); + for (const BRepGraph_NodeId& aNode : anIt.Value()) + { + aSourceImages.Append(BRepGraph_ItemId(aNode)); + } + replayItems(aRecord.OperationName, + aRecord.RecordKind, + BRepGraph_ItemId(anIt.Key()), + BRepGraph_ItemUID(), + aSourceImages); + } + } + + if (hasRawRecordsAppended) + { + aTarget->rebuildCaches(); + } + aTarget->SetEnabled(myEnabled); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::InvalidateAll() noexcept +{ + Clear(); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::Record(const TCollection_AsciiString& theOpLabel, + const BRepGraph_NodeId theOriginal, + const NCollection_Array1& theReplacements, + const BRepGraph_LayerHistory::Kind theKind) +{ + if (!myEnabled) + { + return; + } + + // Append a new history record. Empty replacements collapse to a Deleted + // record regardless of the caller-supplied kind. + BRepGraph_LayerHistory::Event aRecord; + aRecord.OperationName = theOpLabel; + aRecord.SequenceNumber = myRecords.Size(); + aRecord.RecordKind = theReplacements.IsEmpty() ? BRepGraph_LayerHistory::Kind::Deleted : theKind; + aRecord.Mapping.Bind(theOriginal, copyNodes(theReplacements)); + const BRepGraph* aGraph = AttachedGraph(); + const BRepGraph_ItemUID anOriginalItemUID = itemUidOf(aGraph, BRepGraph_ItemId(theOriginal)); + const NCollection_LinearVector anItemReplacements = + itemUidsOfNodes(aGraph, theReplacements); + if (anOriginalItemUID.IsValid()) + { + myItemUidKnownInputs.Add(anOriginalItemUID); + aRecord.ItemUidMapping.Bind(anOriginalItemUID, copyItemUids(anItemReplacements)); + } + myRecords.Append(std::move(aRecord)); + + if (theReplacements.IsEmpty()) + { + myDeleted.Add(theOriginal); + if (anOriginalItemUID.IsValid()) + { + myItemUidDeleted.Add(anOriginalItemUID); + } + touch(); + return; + } + + Standard_ASSERT_VOID(theKind != BRepGraph_LayerHistory::Kind::Deleted, + "Record: use RecordDeleted() for deletions"); + + // Populate the per-kind forward map and the reverse map. Skip + // self-referencing entries (aDerived == theOriginal) to avoid overwriting + // prior chain links in the reverse map. + NCollection_DataMap>& aFwd = + selectForwardMap(theKind, myOriginalToModified, myOriginalToGenerated); + NCollection_LinearVector* aDerivedVec = + aFwd.IsBound(theOriginal) ? &aFwd.ChangeFind(theOriginal) : nullptr; + + for (const BRepGraph_NodeId& aDerived : theReplacements) + { + if (aDerived == theOriginal) + { + continue; + } + appendDerivedOrigin(myDerivedToOriginals, aDerived, theOriginal); + if (aDerivedVec == nullptr) + { + NCollection_LinearVector aFresh(THE_HISTORY_FILTERED_BLOCK_SIZE); + aFresh.Append(aDerived); + aDerivedVec = aFwd.Bound(theOriginal, std::move(aFresh)); + } + else + { + // De-dup: avoid appending the same derived id twice if the caller + // records the same edge across multiple Record() calls. + bool aSeen = false; + for (const BRepGraph_NodeId& aExisting : *aDerivedVec) + { + if (aExisting == aDerived) + { + aSeen = true; + break; + } + } + if (!aSeen) + { + aDerivedVec->Append(aDerived); + } + } + } + + if (anOriginalItemUID.IsValid()) + { + NCollection_DataMap>& anItemFwd = + selectItemUidForwardMap(theKind, myItemUidOriginalToModified, myItemUidOriginalToGenerated); + for (const BRepGraph_ItemUID& aDerivedUID : anItemReplacements) + { + if (aDerivedUID != anOriginalItemUID) + { + appendItemUidForward(anItemFwd, anOriginalItemUID, aDerivedUID); + } + } + } + + if (theKind == BRepGraph_LayerHistory::Kind::Replaced) + { + myDeleted.Add(theOriginal); + if (anOriginalItemUID.IsValid()) + { + myItemUidDeleted.Add(anOriginalItemUID); + } + } + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::RecordBatch( + const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theOriginals, + const NCollection_Array1& theReplacements, + const TCollection_AsciiString& theExtraInfo, + const BRepGraph_LayerHistory::Kind theKind) +{ + Standard_ASSERT_VOID(theOriginals.Size() == theReplacements.Size(), + "RecordBatch: mismatched array lengths"); + Standard_ASSERT_VOID(theKind != BRepGraph_LayerHistory::Kind::Deleted, + "RecordBatch: use RecordDeleted() for deletions"); + if (!myEnabled || theOriginals.IsEmpty()) + { + return; + } + + const size_t aNbPairs = theOriginals.Size(); + + // Create a single history record with all mappings. + // Pre-size the Mapping to avoid DataMap rehashing. + BRepGraph_LayerHistory::Event aRecord; + aRecord.OperationName = theOpLabel; + aRecord.SequenceNumber = myRecords.Size(); + aRecord.RecordKind = theKind; + aRecord.Mapping.ReSize(aNbPairs); + + aRecord.ExtraInfo = theExtraInfo; + + // Build mapping: each pair creates a 1-element replacement vector. + const BRepGraph* aGraph = AttachedGraph(); + { + for (size_t aPairIdx = 0; aPairIdx < aNbPairs; ++aPairIdx) + { + const BRepGraph_NodeId& anOriginal = theOriginals.At(aPairIdx); + const BRepGraph_NodeId& aReplacement = theReplacements.At(aPairIdx); + Standard_ASSERT_VOID(!aRecord.Mapping.IsBound(anOriginal), + "RecordBatch: duplicate original node"); + NCollection_LinearVector aRepVec(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aRepVec.Append(aReplacement); + aRecord.Mapping.Bind(anOriginal, std::move(aRepVec)); + + const BRepGraph_ItemUID anOriginalUID = itemUidOf(aGraph, BRepGraph_ItemId(anOriginal)); + const BRepGraph_ItemUID aReplacementUID = itemUidOf(aGraph, BRepGraph_ItemId(aReplacement)); + if (anOriginalUID.IsValid()) + { + myItemUidKnownInputs.Add(anOriginalUID); + NCollection_LinearVector anItemRepVec( + THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + if (aReplacementUID.IsValid()) + { + anItemRepVec.Append(aReplacementUID); + } + aRecord.ItemUidMapping.Bind(anOriginalUID, std::move(anItemRepVec)); + } + } + } + myRecords.Append(std::move(aRecord)); + + // Update per-kind forward map and reverse map in bulk. + NCollection_DataMap>& aFwd = + selectForwardMap(theKind, myOriginalToModified, myOriginalToGenerated); + NCollection_DataMap>& anItemFwd = + selectItemUidForwardMap(theKind, myItemUidOriginalToModified, myItemUidOriginalToGenerated); + aFwd.ReSize(aFwd.Extent() + aNbPairs); + + { + for (size_t aPairIdx = 0; aPairIdx < aNbPairs; ++aPairIdx) + { + const BRepGraph_NodeId& anOriginal = theOriginals.At(aPairIdx); + const BRepGraph_NodeId& aReplacement = theReplacements.At(aPairIdx); + const BRepGraph_ItemUID anOriginalUID = itemUidOf(aGraph, BRepGraph_ItemId(anOriginal)); + const BRepGraph_ItemUID aReplacementUID = itemUidOf(aGraph, BRepGraph_ItemId(aReplacement)); + if (aReplacement == anOriginal) + { + continue; + } + + appendDerivedOrigin(myDerivedToOriginals, aReplacement, anOriginal); + + if (aFwd.IsBound(anOriginal)) + { + NCollection_LinearVector& aDerVec = aFwd.ChangeFind(anOriginal); + bool aSeen = false; + for (const BRepGraph_NodeId& aExisting : aDerVec) + { + if (aExisting == aReplacement) + { + aSeen = true; + break; + } + } + if (!aSeen) + { + aDerVec.Append(aReplacement); + } + } + else + { + NCollection_LinearVector aDerVec(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aDerVec.Append(aReplacement); + aFwd.Bind(anOriginal, std::move(aDerVec)); + } + + if (theKind == BRepGraph_LayerHistory::Kind::Replaced) + { + myDeleted.Add(anOriginal); + } + + if (anOriginalUID.IsValid() && aReplacementUID.IsValid() && aReplacementUID != anOriginalUID) + { + appendItemUidForward(anItemFwd, anOriginalUID, aReplacementUID); + } + + if (theKind == BRepGraph_LayerHistory::Kind::Replaced && anOriginalUID.IsValid()) + { + myItemUidDeleted.Add(anOriginalUID); + } + } + } + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::RecordDeleted(const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theDeleted) +{ + if (!myEnabled || theDeleted.IsEmpty()) + { + return; + } + + BRepGraph_LayerHistory::Event aRecord; + aRecord.OperationName = theOpLabel; + aRecord.SequenceNumber = myRecords.Size(); + aRecord.RecordKind = BRepGraph_LayerHistory::Kind::Deleted; + aRecord.Mapping.ReSize(theDeleted.Size()); + aRecord.ItemUidMapping.ReSize(theDeleted.Size()); + const BRepGraph* aGraph = AttachedGraph(); + for (const BRepGraph_NodeId& aNode : theDeleted) + { + if (!aRecord.Mapping.IsBound(aNode)) + { + aRecord.Mapping.Bind( + aNode, + NCollection_LinearVector(THE_HISTORY_REPLACEMENT_BLOCK_SIZE)); + } + myDeleted.Add(aNode); + + const BRepGraph_ItemUID aUID = itemUidOf(aGraph, BRepGraph_ItemId(aNode)); + if (aUID.IsValid()) + { + myItemUidKnownInputs.Add(aUID); + myItemUidDeleted.Add(aUID); + if (!aRecord.ItemUidMapping.IsBound(aUID)) + { + aRecord.ItemUidMapping.Bind( + aUID, + NCollection_LinearVector(THE_HISTORY_REPLACEMENT_BLOCK_SIZE)); + } + } + } + myRecords.Append(std::move(aRecord)); + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::RecordReplaced(const TCollection_AsciiString& theOpLabel, + const BRepGraph_NodeId theOriginal, + const BRepGraph_NodeId theReplacement) +{ + if (!theOriginal.IsValid() || !theReplacement.IsValid()) + { + return; + } + + NCollection_LinearVector aReplacements(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + aReplacements.Append(theReplacement); + Record(theOpLabel, theOriginal, aReplacements.ToArray1(), BRepGraph_LayerHistory::Kind::Replaced); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::RecordReplacedBatch( + const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theOriginals, + const NCollection_Array1& theReplacements, + const TCollection_AsciiString& theExtraInfo) +{ + RecordBatch(theOpLabel, + theOriginals, + theReplacements, + theExtraInfo, + BRepGraph_LayerHistory::Kind::Replaced); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::RecordUid(const TCollection_AsciiString& theOpLabel, + const BRepGraph_UID& theOriginal, + const NCollection_Array1& theReplacements, + const BRepGraph_LayerHistory::Kind theKind) +{ + if (!myEnabled || !theOriginal.IsValid()) + { + return; + } + + myUidKnownInputs.Add(theOriginal); + myItemUidKnownInputs.Add(BRepGraph_ItemUID::Node(theOriginal.Kind, theOriginal.Counter)); + + if (theReplacements.IsEmpty()) + { + NCollection_LinearVector aDeleted(THE_HISTORY_DERIVED_BLOCK_SIZE); + aDeleted.Append(theOriginal); + RecordDeletedUid(theOpLabel, aDeleted.ToArray1()); + return; + } + + Standard_ASSERT_VOID(theKind != BRepGraph_LayerHistory::Kind::Deleted, + "RecordUid: use RecordDeletedUid() for deletions"); + + NCollection_LinearVector aValidReplacements(THE_HISTORY_FILTERED_BLOCK_SIZE); + NCollection_LinearVector aValidItemReplacements( + THE_HISTORY_FILTERED_BLOCK_SIZE); + for (const BRepGraph_UID& aDerived : theReplacements) + { + if (aDerived.IsValid()) + { + appendUniqueUid(aValidReplacements, aDerived); + appendUniqueItemUid(aValidItemReplacements, + BRepGraph_ItemUID::Node(aDerived.Kind, aDerived.Counter)); + } + } + if (aValidReplacements.IsEmpty()) + { + return; + } + + BRepGraph_LayerHistory::Event aRecord; + aRecord.OperationName = theOpLabel; + aRecord.SequenceNumber = myRecords.Size(); + aRecord.RecordKind = theKind; + aRecord.UidMapping.Bind(theOriginal, copyUids(aValidReplacements)); + aRecord.ItemUidMapping.Bind(BRepGraph_ItemUID::Node(theOriginal.Kind, theOriginal.Counter), + copyItemUids(aValidItemReplacements)); + myRecords.Append(std::move(aRecord)); + + NCollection_DataMap>& aFwd = + selectUidForwardMap(theKind, myUidOriginalToModified, myUidOriginalToGenerated); + NCollection_LinearVector* aDerivedVec = + aFwd.IsBound(theOriginal) ? &aFwd.ChangeFind(theOriginal) : nullptr; + + for (const BRepGraph_UID& aDerived : aValidReplacements) + { + if (aDerivedVec == nullptr) + { + NCollection_LinearVector aFresh(THE_HISTORY_FILTERED_BLOCK_SIZE); + aFresh.Append(aDerived); + aDerivedVec = aFwd.Bound(theOriginal, std::move(aFresh)); + } + else + { + appendUniqueUid(*aDerivedVec, aDerived); + } + } + + NCollection_DataMap>& anItemFwd = + selectItemUidForwardMap(theKind, myItemUidOriginalToModified, myItemUidOriginalToGenerated); + for (const BRepGraph_ItemUID& aDerived : aValidItemReplacements) + { + appendItemUidForward(anItemFwd, + BRepGraph_ItemUID::Node(theOriginal.Kind, theOriginal.Counter), + aDerived); + } + + if (theKind == BRepGraph_LayerHistory::Kind::Replaced) + { + myUidDeleted.Add(theOriginal); + myItemUidDeleted.Add(BRepGraph_ItemUID::Node(theOriginal.Kind, theOriginal.Counter)); + } + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::RecordDeletedUid(const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theDeleted) +{ + if (!myEnabled || theDeleted.IsEmpty()) + { + return; + } + + BRepGraph_LayerHistory::Event aRecord; + aRecord.OperationName = theOpLabel; + aRecord.SequenceNumber = myRecords.Size(); + aRecord.RecordKind = BRepGraph_LayerHistory::Kind::Deleted; + aRecord.UidMapping.ReSize(theDeleted.Size()); + aRecord.ItemUidMapping.ReSize(theDeleted.Size()); + + for (const BRepGraph_UID& aUid : theDeleted) + { + if (!aUid.IsValid()) + { + continue; + } + myUidKnownInputs.Add(aUid); + myUidDeleted.Add(aUid); + myItemUidKnownInputs.Add(BRepGraph_ItemUID::Node(aUid.Kind, aUid.Counter)); + myItemUidDeleted.Add(BRepGraph_ItemUID::Node(aUid.Kind, aUid.Counter)); + if (!aRecord.UidMapping.IsBound(aUid)) + { + aRecord.UidMapping.Bind( + aUid, + NCollection_LinearVector(THE_HISTORY_REPLACEMENT_BLOCK_SIZE)); + } + if (!aRecord.ItemUidMapping.IsBound(BRepGraph_ItemUID::Node(aUid.Kind, aUid.Counter))) + { + aRecord.ItemUidMapping.Bind( + BRepGraph_ItemUID::Node(aUid.Kind, aUid.Counter), + NCollection_LinearVector(THE_HISTORY_REPLACEMENT_BLOCK_SIZE)); + } + } + + if (!aRecord.UidMapping.IsEmpty() || !aRecord.ItemUidMapping.IsEmpty()) + { + myRecords.Append(std::move(aRecord)); + touch(); + } +} + +//================================================================================================= + +void BRepGraph_LayerHistory::RecordItemUid( + const TCollection_AsciiString& theOpLabel, + const BRepGraph_ItemUID& theOriginal, + const NCollection_Array1& theReplacements, + const BRepGraph_LayerHistory::Kind theKind) +{ + if (!myEnabled || !theOriginal.IsValid()) + { + return; + } + + myItemUidKnownInputs.Add(theOriginal); + + if (theReplacements.IsEmpty()) + { + NCollection_LinearVector aDeleted(THE_HISTORY_DERIVED_BLOCK_SIZE); + aDeleted.Append(theOriginal); + RecordDeletedItemUid(theOpLabel, aDeleted.ToArray1()); + return; + } + + Standard_ASSERT_VOID(theKind != BRepGraph_LayerHistory::Kind::Deleted, + "RecordItemUid: use RecordDeletedItemUid() for deletions"); + + NCollection_LinearVector aValidReplacements(THE_HISTORY_FILTERED_BLOCK_SIZE); + for (const BRepGraph_ItemUID& aDerived : theReplacements) + { + if (aDerived.IsValid()) + { + appendUniqueItemUid(aValidReplacements, aDerived); + } + } + if (aValidReplacements.IsEmpty()) + { + return; + } + + BRepGraph_LayerHistory::Event aRecord; + aRecord.OperationName = theOpLabel; + aRecord.SequenceNumber = myRecords.Size(); + aRecord.RecordKind = theKind; + aRecord.ItemUidMapping.Bind(theOriginal, copyItemUids(aValidReplacements)); + myRecords.Append(std::move(aRecord)); + + NCollection_DataMap>& aFwd = + selectItemUidForwardMap(theKind, myItemUidOriginalToModified, myItemUidOriginalToGenerated); + for (const BRepGraph_ItemUID& aDerived : aValidReplacements) + { + appendItemUidForward(aFwd, theOriginal, aDerived); + } + + if (theKind == BRepGraph_LayerHistory::Kind::Replaced) + { + myItemUidDeleted.Add(theOriginal); + } + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::RecordDeletedItemUid( + const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theDeleted) +{ + if (!myEnabled || theDeleted.IsEmpty()) + { + return; + } + + BRepGraph_LayerHistory::Event aRecord; + aRecord.OperationName = theOpLabel; + aRecord.SequenceNumber = myRecords.Size(); + aRecord.RecordKind = BRepGraph_LayerHistory::Kind::Deleted; + aRecord.ItemUidMapping.ReSize(theDeleted.Size()); + + for (const BRepGraph_ItemUID& aUid : theDeleted) + { + if (!aUid.IsValid()) + { + continue; + } + myItemUidKnownInputs.Add(aUid); + myItemUidDeleted.Add(aUid); + if (!aRecord.ItemUidMapping.IsBound(aUid)) + { + aRecord.ItemUidMapping.Bind( + aUid, + NCollection_LinearVector(THE_HISTORY_REPLACEMENT_BLOCK_SIZE)); + } + } + + if (!aRecord.ItemUidMapping.IsEmpty()) + { + myRecords.Append(std::move(aRecord)); + touch(); + } +} + +namespace +{ +//! Translate an NCollection_List into a NodeId vector by +//! looking up each shape in @p theOutputs. Missing entries are silently +//! dropped (see Absorb's contract for why). +inline NCollection_LinearVector resolveImages( + const NCollection_List& theImages, + const NCollection_DataMap& theOutputs) +{ + NCollection_LinearVector aResult(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + if (theImages.IsEmpty()) + { + return aResult; + } + for (NCollection_List::Iterator anIt(theImages); anIt.More(); anIt.Next()) + { + const TopoDS_Shape& anImage = anIt.Value(); + if (theOutputs.IsBound(anImage)) + { + appendUniqueNode(aResult, theOutputs.Find(anImage)); + } + } + return aResult; +} + +inline NCollection_LinearVector resolveUidImages( + const BRepGraph& theOutputGraph, + const NCollection_List& theImages, + const NCollection_DataMap& theOutputs) +{ + NCollection_LinearVector aResult(THE_HISTORY_REPLACEMENT_BLOCK_SIZE); + if (theImages.IsEmpty()) + { + return aResult; + } + for (NCollection_List::Iterator anIt(theImages); anIt.More(); anIt.Next()) + { + const TopoDS_Shape& anImage = anIt.Value(); + if (const BRepGraph_NodeId* aNode = theOutputs.Seek(anImage)) + { + const BRepGraph_UID aUID = theOutputGraph.UIDs().Of(*aNode); + if (aUID.IsValid()) + { + appendUniqueUid(aResult, aUID); + } + } + } + return aResult; +} +} // namespace + +//================================================================================================= + +void BRepGraph_LayerHistory::Absorb( + const NCollection_DataMap& theInputs, + const NCollection_DataMap& theOutputs, + const occ::handle& theSource, + const TCollection_AsciiString& theOpLabel) +{ + if (theSource.IsNull() || theInputs.IsEmpty()) + { + return; + } + + NCollection_LinearVector aDeletedNodes(THE_HISTORY_DERIVED_BLOCK_SIZE); + + for (NCollection_DataMap::Iterator anIt( + theInputs); + anIt.More(); + anIt.Next()) + { + const TopoDS_Shape& anInputShape = anIt.Key(); + const BRepGraph_NodeId anInputNode = anIt.Value(); + + // Deleted has precedence: an input that was both removed *and* (per OCCT + // bug) listed as Modified/Generated is still a deletion event. + if (theSource->IsRemoved(anInputShape)) + { + aDeletedNodes.Append(anInputNode); + continue; + } + + { + const NCollection_List& aModified = theSource->Modified(anInputShape); + NCollection_LinearVector aReplacements = + resolveImages(aModified, theOutputs); + if (!aReplacements.IsEmpty()) + { + Record(theOpLabel, + anInputNode, + aReplacements.ToArray1(), + BRepGraph_LayerHistory::Kind::Modified); + } + } + + { + const NCollection_List& aGenerated = theSource->Generated(anInputShape); + NCollection_LinearVector aReplacements = + resolveImages(aGenerated, theOutputs); + if (!aReplacements.IsEmpty()) + { + Record(theOpLabel, + anInputNode, + aReplacements.ToArray1(), + BRepGraph_LayerHistory::Kind::Generated); + } + } + } + + if (!aDeletedNodes.IsEmpty()) + { + RecordDeleted(theOpLabel, aDeletedNodes.ToArray1()); + } +} + +//================================================================================================= + +void BRepGraph_LayerHistory::Absorb( + const BRepGraph& theInputGraph, + const BRepGraph& theOutputGraph, + const NCollection_DataMap& theInputs, + const NCollection_DataMap& theOutputs, + const occ::handle& theSource, + const TCollection_AsciiString& theOpLabel) +{ + if (theSource.IsNull() || theInputs.IsEmpty()) + { + return; + } + + NCollection_LinearVector aDeletedUids(THE_HISTORY_DERIVED_BLOCK_SIZE); + + for (NCollection_DataMap::Iterator anIt( + theInputs); + anIt.More(); + anIt.Next()) + { + const TopoDS_Shape& anInputShape = anIt.Key(); + const BRepGraph_UID anInputUid = theInputGraph.UIDs().Of(anIt.Value()); + if (!anInputUid.IsValid()) + { + continue; + } + + myUidKnownInputs.Add(anInputUid); + + if (theSource->IsRemoved(anInputShape)) + { + aDeletedUids.Append(anInputUid); + continue; + } + + { + const NCollection_List& aModified = theSource->Modified(anInputShape); + NCollection_LinearVector aReplacements = + resolveUidImages(theOutputGraph, aModified, theOutputs); + if (!aReplacements.IsEmpty()) + { + RecordUid(theOpLabel, + anInputUid, + aReplacements.ToArray1(), + BRepGraph_LayerHistory::Kind::Modified); + } + } + + { + const NCollection_List& aGenerated = theSource->Generated(anInputShape); + NCollection_LinearVector aReplacements = + resolveUidImages(theOutputGraph, aGenerated, theOutputs); + if (!aReplacements.IsEmpty()) + { + RecordUid(theOpLabel, + anInputUid, + aReplacements.ToArray1(), + BRepGraph_LayerHistory::Kind::Generated); + } + } + } + + if (!aDeletedUids.IsEmpty()) + { + RecordDeletedUid(theOpLabel, aDeletedUids.ToArray1()); + } +} + +//================================================================================================= + +BRepGraph_NodeId BRepGraph_LayerHistory::FindOriginal(const BRepGraph_NodeId theModified) const +{ + // Walk the reverse map iteratively until a root node is reached. + // Limit iterations to the map extent to protect against cycles. + BRepGraph_NodeId aCurrent = theModified; + size_t aMaxIter = static_cast(myDerivedToOriginals.Extent()); + while (myDerivedToOriginals.IsBound(aCurrent) && aMaxIter > 0) + { + const NCollection_LinearVector& anOrigins = + myDerivedToOriginals.Find(aCurrent); + if (anOrigins.IsEmpty()) + { + break; + } + const BRepGraph_NodeId& anOriginal = anOrigins.First(); + if (anOriginal == aCurrent) + { + break; + } + aCurrent = anOriginal; + --aMaxIter; + } + return aCurrent; +} + +//================================================================================================= + +NCollection_LinearVector BRepGraph_LayerHistory::FindDerived( + const BRepGraph_NodeId theOriginal) const +{ + // Collect every transitively derived node (Modified U Generated) in + // breadth-first order. A visited set guards against infinite loops if + // cycles exist in the forward maps. @p theOriginal itself is excluded + // from the result. + NCollection_LinearVector aResult(THE_HISTORY_DERIVED_BLOCK_SIZE); + NCollection_LinearVector aQueue(THE_HISTORY_QUEUE_BLOCK_SIZE); + NCollection_FlatMap aVisited; + + aQueue.Append(theOriginal); + aVisited.Add(theOriginal); + + size_t aFront = 0; + while (aFront < aQueue.Size()) + { + const BRepGraph_NodeId aNode = aQueue.Value(aFront++); + if (aNode != theOriginal) + { + aResult.Append(aNode); + } + + auto enqueue = [&](const NCollection_LinearVector& theArr) { + for (const BRepGraph_NodeId& aDerived : theArr) + { + if (aVisited.Add(aDerived)) + { + aQueue.Append(aDerived); + } + } + }; + if (myOriginalToModified.IsBound(aNode)) + { + enqueue(myOriginalToModified.Find(aNode)); + } + if (myOriginalToGenerated.IsBound(aNode)) + { + enqueue(myOriginalToGenerated.Find(aNode)); + } + } + + return aResult; +} + +//================================================================================================= + +const NCollection_LinearVector* BRepGraph_LayerHistory::FindModified( + const BRepGraph_NodeId theOriginal) const +{ + return myOriginalToModified.IsBound(theOriginal) ? &myOriginalToModified.Find(theOriginal) + : nullptr; +} + +//================================================================================================= + +const NCollection_LinearVector* BRepGraph_LayerHistory::FindGenerated( + const BRepGraph_NodeId theOriginal) const +{ + return myOriginalToGenerated.IsBound(theOriginal) ? &myOriginalToGenerated.Find(theOriginal) + : nullptr; +} + +//================================================================================================= + +const NCollection_LinearVector* BRepGraph_LayerHistory::FindModified( + const BRepGraph_UID& theUID) const +{ + return myUidOriginalToModified.IsBound(theUID) ? &myUidOriginalToModified.Find(theUID) : nullptr; +} + +//================================================================================================= + +const NCollection_LinearVector* BRepGraph_LayerHistory::FindOriginals( + const BRepGraph_NodeId theDerived) const +{ + return myDerivedToOriginals.IsBound(theDerived) ? &myDerivedToOriginals.Find(theDerived) + : nullptr; +} + +//================================================================================================= + +const NCollection_LinearVector* BRepGraph_LayerHistory::FindGenerated( + const BRepGraph_UID& theUID) const +{ + return myUidOriginalToGenerated.IsBound(theUID) ? &myUidOriginalToGenerated.Find(theUID) + : nullptr; +} + +//================================================================================================= + +bool BRepGraph_LayerHistory::IsDeleted(const BRepGraph_UID& theUID) const +{ + return myUidDeleted.Contains(theUID); +} + +//================================================================================================= + +const NCollection_FlatMap& BRepGraph_LayerHistory::DeletedUids() const +{ + return myUidDeleted; +} + +//================================================================================================= + +bool BRepGraph_LayerHistory::HasKnownInput(const BRepGraph_UID& theUID) const +{ + return myUidKnownInputs.Contains(theUID); +} + +//================================================================================================= + +const NCollection_LinearVector* BRepGraph_LayerHistory::FindModified( + const BRepGraph_ItemUID& theUID) const +{ + return myItemUidOriginalToModified.IsBound(theUID) ? &myItemUidOriginalToModified.Find(theUID) + : nullptr; +} + +//================================================================================================= + +const NCollection_LinearVector* BRepGraph_LayerHistory::FindGenerated( + const BRepGraph_ItemUID& theUID) const +{ + return myItemUidOriginalToGenerated.IsBound(theUID) ? &myItemUidOriginalToGenerated.Find(theUID) + : nullptr; +} + +//================================================================================================= + +bool BRepGraph_LayerHistory::IsDeleted(const BRepGraph_ItemUID& theUID) const +{ + return myItemUidDeleted.Contains(theUID); +} + +//================================================================================================= + +const NCollection_FlatMap& BRepGraph_LayerHistory::DeletedItemUids() const +{ + return myItemUidDeleted; +} + +//================================================================================================= + +bool BRepGraph_LayerHistory::HasKnownInput(const BRepGraph_ItemUID& theUID) const +{ + return myItemUidKnownInputs.Contains(theUID); +} + +//================================================================================================= + +bool BRepGraph_LayerHistory::IsDeleted(const BRepGraph_NodeId theOriginal) const +{ + return myDeleted.Contains(theOriginal); +} + +//================================================================================================= + +const NCollection_FlatMap& BRepGraph_LayerHistory::DeletedNodes() const +{ + return myDeleted; +} + +//================================================================================================= + +NCollection_LinearVector BRepGraph_LayerHistory::FindModified( + const BRepGraph& theGraph, + const BRepGraph_UID& theUID) const +{ + NCollection_LinearVector aResult(THE_HISTORY_DERIVED_BLOCK_SIZE); + if (const NCollection_LinearVector* aUidVec = FindModified(theUID)) + { + return *aUidVec; + } + const BRepGraph_NodeId aNode = theGraph.UIDs().NodeIdFrom(theUID); + if (!aNode.IsValid()) + { + return aResult; + } + if (const NCollection_LinearVector* aVec = FindModified(aNode)) + { + for (const BRepGraph_NodeId& aDer : *aVec) + { + const BRepGraph_UID aUID = theGraph.UIDs().Of(aDer); + if (aUID.IsValid()) + { + aResult.Append(aUID); + } + } + } + return aResult; +} + +//================================================================================================= + +NCollection_LinearVector BRepGraph_LayerHistory::FindGenerated( + const BRepGraph& theGraph, + const BRepGraph_UID& theUID) const +{ + NCollection_LinearVector aResult(THE_HISTORY_DERIVED_BLOCK_SIZE); + if (const NCollection_LinearVector* aUidVec = FindGenerated(theUID)) + { + return *aUidVec; + } + const BRepGraph_NodeId aNode = theGraph.UIDs().NodeIdFrom(theUID); + if (!aNode.IsValid()) + { + return aResult; + } + if (const NCollection_LinearVector* aVec = FindGenerated(aNode)) + { + for (const BRepGraph_NodeId& aDer : *aVec) + { + const BRepGraph_UID aUID = theGraph.UIDs().Of(aDer); + if (aUID.IsValid()) + { + aResult.Append(aUID); + } + } + } + return aResult; +} + +//================================================================================================= + +bool BRepGraph_LayerHistory::IsDeleted(const BRepGraph& theGraph, const BRepGraph_UID& theUID) const +{ + if (IsDeleted(theUID)) + { + return true; + } + const BRepGraph_NodeId aNode = theGraph.UIDs().NodeIdFrom(theUID); + if (!aNode.IsValid()) + { + return false; + } + return myDeleted.Contains(aNode); +} + +//================================================================================================= + +NCollection_LinearVector BRepGraph_LayerHistory::DeletedUids( + const BRepGraph& theGraph) const +{ + NCollection_LinearVector aResult(myDeleted.Size() + myUidDeleted.Size()); + for (NCollection_FlatMap::Iterator anIt(myUidDeleted); anIt.More(); anIt.Next()) + { + appendUniqueUid(aResult, anIt.Value()); + } + for (NCollection_FlatMap::Iterator anIt(myDeleted); anIt.More(); anIt.Next()) + { + const BRepGraph_UID aUID = theGraph.UIDs().Of(anIt.Value()); + if (aUID.IsValid()) + { + appendUniqueUid(aResult, aUID); + } + } + return aResult; +} + +//================================================================================================= + +size_t BRepGraph_LayerHistory::NbRecords() const +{ + return myRecords.Size(); +} + +//================================================================================================= + +const BRepGraph_LayerHistory::Event& BRepGraph_LayerHistory::Record(const size_t theRecordIdx) const +{ + return myRecords.Value(theRecordIdx); +} + +//================================================================================================= + +void BRepGraph_LayerHistory::SetEnabled(const bool theVal) +{ + if (myEnabled == theVal) + { + return; + } + myEnabled = theVal; + touch(); +} + +//================================================================================================= + +bool BRepGraph_LayerHistory::IsEnabled() const +{ + return myEnabled; +} + +//================================================================================================= + +void BRepGraph_LayerHistory::Clear() noexcept +{ + myRecords.Clear(true); + myDerivedToOriginals.Clear(); + myOriginalToModified.Clear(); + myOriginalToGenerated.Clear(); + myDeleted.Clear(); + myUidOriginalToModified.Clear(); + myUidOriginalToGenerated.Clear(); + myUidKnownInputs.Clear(); + myUidDeleted.Clear(); + myItemUidOriginalToModified.Clear(); + myItemUidOriginalToGenerated.Clear(); + myItemUidKnownInputs.Clear(); + myItemUidDeleted.Clear(); + touch(); +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerHistory.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerHistory.hxx new file mode 100644 index 0000000000..f994c2c709 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerHistory.hxx @@ -0,0 +1,422 @@ +// 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. + +#ifndef _BRepGraph_LayerHistory_HeaderFile +#define _BRepGraph_LayerHistory_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class BRepGraph; +class BRepTools_History; + +//! History layer for BRepGraph. +//! +//! BRepGraph_LayerHistory maintains an append-only log of modification events +//! and per-kind lookup maps for efficient queries. Four event kinds are +//! tracked (see #BRepGraph_LayerHistory::Kind): +//! - **Modified**: input -> { modified images } (default). +//! - **Generated**: input -> { generated images } (new entities born +//! from the input but not sharing its identity). +//! - **Deleted**: input has been consumed and has no image in the +//! result. +//! - **Replaced**: input was structurally detached and replaced by another +//! node; this maps as Modified and also marks the input as deleted. +//! +//! Recording can be toggled on/off at runtime. Graph-owned history is registered +//! as a layer and accessed through #Ensure / #Find; algorithms wrapping OCCT's +//! `BRepTools_History` can import results through #Absorb. +class BRepGraph_LayerHistory : public BRepGraph_Layer +{ +public: + //! Classification of a history event. + enum class Kind : std::uint8_t + { + Modified = 0, //!< Default; input persists into the result(s). + Generated = 1, //!< Output entity is freshly produced from the input. + Deleted = 2, //!< Input has no image in the result. + Replaced = 3 //!< Input was detached and continued by replacement(s). + }; + + //! One atomic modification event recorded in the graph's history log. + struct Event + { + Event() = default; + + TCollection_AsciiString OperationName; + size_t SequenceNumber = 0; + Kind RecordKind = Kind::Modified; + + //! Key: original node id before the operation. + //! Value: sequence of replacement node ids after the operation. + NCollection_DataMap> Mapping; + + //! UID-keyed mapping for cross-graph history records. + NCollection_DataMap> UidMapping; + + //! ItemUID-keyed mapping for durable all-domain history records. + NCollection_DataMap> + ItemUidMapping; + + //! Optional diagnostic representation. + TCollection_AsciiString ExtraInfo; + }; + + //! Default constructor. + Standard_EXPORT BRepGraph_LayerHistory(); + + //! Stable layer GUID. + Standard_EXPORT static const Standard_GUID& GetID(); + + //! Layer type identity. + [[nodiscard]] Standard_EXPORT const Standard_GUID& ID() const override; + + //! Layer display name. + [[nodiscard]] Standard_EXPORT const TCollection_AsciiString& Name() const override; + + //! Record a modification: theOriginal was replaced by theReplacements. + //! + //! @note When @p theReplacements is empty the record is auto-downgraded to + //! Kind::Deleted and @p theOriginal is added to the deleted set, + //! regardless of @p theKind. Use #RecordDeleted directly for the + //! deletion case to avoid relying on this implicit conversion. + //! @param[in] theOpLabel human-readable operation name + //! @param[in] theOriginal node id before the operation + //! @param[in] theReplacements node ids after the operation + //! @param[in] theKind classification of this record (default Modified) + Standard_EXPORT void Record( + const TCollection_AsciiString& theOpLabel, + const BRepGraph_NodeId theOriginal, + const NCollection_Array1& theReplacements, + const BRepGraph_LayerHistory::Kind theKind = BRepGraph_LayerHistory::Kind::Modified); + + //! Record a batch of 1-to-1 modifications in a single history event. + //! Each original is paired with the replacement at the same logical position. + //! More efficient than calling Record() in a loop: creates one HistoryRecord + //! and updates the per-kind maps with minimal overhead. + //! @param[in] theOpLabel human-readable operation name + //! @param[in] theOriginals node ids before the operation + //! @param[in] theReplacements node ids after the operation (same length) + //! @param[in] theExtraInfo optional diagnostic info stored on the record + //! @param[in] theKind classification of this record (default Modified) + Standard_EXPORT void RecordBatch( + const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theOriginals, + const NCollection_Array1& theReplacements, + const TCollection_AsciiString& theExtraInfo = TCollection_AsciiString(), + const BRepGraph_LayerHistory::Kind theKind = BRepGraph_LayerHistory::Kind::Modified); + + //! Record that a collection of inputs has been consumed by the operation + //! and has no image in the result. Each input is appended to the + //! deleted set and emits a single audit record with empty replacements. + //! @param[in] theOpLabel human-readable operation name + //! @param[in] theDeleted node ids that have been removed + Standard_EXPORT void RecordDeleted(const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theDeleted); + + //! Record replacements: each original is logically removed/detached and + //! continued by the corresponding replacement. Replaced records participate + //! in modified-image queries and also mark originals as deleted. + Standard_EXPORT void RecordReplaced(const TCollection_AsciiString& theOpLabel, + const BRepGraph_NodeId theOriginal, + const BRepGraph_NodeId theReplacement); + + //! Record a batch of 1-to-1 replacements in a single history event. + Standard_EXPORT void RecordReplacedBatch( + const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theOriginals, + const NCollection_Array1& theReplacements, + const TCollection_AsciiString& theExtraInfo = TCollection_AsciiString()); + + //! Record a UID-keyed modification/generation event. + //! + //! This is the durable-history path for operations whose source and result + //! identities may live in different BRepGraph instances. Existing NodeId + //! records remain available for in-graph algorithms; UID records are queried + //! directly by cross-graph consumers. + Standard_EXPORT void RecordUid( + const TCollection_AsciiString& theOpLabel, + const BRepGraph_UID& theOriginal, + const NCollection_Array1& theReplacements, + const BRepGraph_LayerHistory::Kind theKind = BRepGraph_LayerHistory::Kind::Modified); + + //! Record UID-keyed deletions. + Standard_EXPORT void RecordDeletedUid(const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theDeleted); + + //! Record an all-domain ItemUID-keyed modification/generation event. + Standard_EXPORT void RecordItemUid( + const TCollection_AsciiString& theOpLabel, + const BRepGraph_ItemUID& theOriginal, + const NCollection_Array1& theReplacements, + const BRepGraph_LayerHistory::Kind theKind = BRepGraph_LayerHistory::Kind::Modified); + + //! Record ItemUID-keyed deletions. + Standard_EXPORT void RecordDeletedItemUid( + const TCollection_AsciiString& theOpLabel, + const NCollection_Array1& theDeleted); + + //! Import a BRepTools_History into this graph-native history log. + //! + //! Iterates @p theInputs, queries @p theSource for Modified / Generated / + //! IsRemoved, translates each TopoDS_Shape image to a NodeId via + //! @p theOutputs, and emits the corresponding records. + //! + //! Semantics: + //! - For every input shape whose Modified() list is non-empty: + //! emit a Modified record. + //! - For every input shape whose Generated() list is non-empty: + //! emit a Generated record. + //! - For every input shape with IsRemoved() == true: accumulate into + //! a single Deleted record (IsRemoved takes precedence over + //! Modified/Generated to handle a known OCCT bug where a shape can + //! appear in both the removed set and the generated map). + //! + //! Output TopoDS_Shapes that do not appear in @p theOutputs are silently + //! dropped (expected for subshapes merged into a parent compound whose + //! identity is preserved at a higher level). + //! + //! @param[in] theInputs TopoDS_Shape -> NodeId for every input subshape + //! that should be tracked + //! @param[in] theOutputs TopoDS_Shape -> NodeId for every subshape added + //! to the graph by this operation (typically from + //! BRepGraph::ShapesView::Add with TrackAddedNodes) + //! @param[in] theSource BRepTools_History from the OCCT algorithm. + //! Null is accepted (no-op). + //! @param[in] theOpLabel record label written into every emitted record + Standard_EXPORT void Absorb( + const NCollection_DataMap& theInputs, + const NCollection_DataMap& theOutputs, + const occ::handle& theSource, + const TCollection_AsciiString& theOpLabel); + + //! Import a BRepTools_History using persistent UIDs from source/result graphs. + //! + //! This overload is the canonical bridge for cross-graph algorithms: input + //! shapes are resolved in @p theInputGraph, output shapes are resolved in + //! @p theOutputGraph, and the resulting history is stored by UID. + Standard_EXPORT void Absorb( + const BRepGraph& theInputGraph, + const BRepGraph& theOutputGraph, + const NCollection_DataMap& theInputs, + const NCollection_DataMap& theOutputs, + const occ::handle& theSource, + const TCollection_AsciiString& theOpLabel); + + //! Walk backwards from a modified node to its original. + //! Follows the reverse map recursively until a root is reached. + //! @param[in] theModified node id to trace back + //! @return the root original node id, or theModified itself if not found + [[nodiscard]] Standard_EXPORT BRepGraph_NodeId + FindOriginal(const BRepGraph_NodeId theModified) const; + + //! Walk forwards from an original node to all derived nodes, including + //! both Modified and Generated descendants. Follows the forward maps + //! recursively, collecting every transitively-reachable descendant + //! (intermediate nodes and leaves alike, but not @p theOriginal itself). + //! @param[in] theOriginal node id to trace forward + //! @return all transitively derived node ids in breadth-first order + [[nodiscard]] Standard_EXPORT NCollection_LinearVector FindDerived( + const BRepGraph_NodeId theOriginal) const; + + //! Direct lookup of the Modified images of @p theOriginal, non-recursive. + //! @param[in] theOriginal node id to query + //! @return pointer to the stored vector, or nullptr if @p theOriginal has + //! no Modified record (note: nullptr does not imply IsDeleted). + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector* FindModified( + const BRepGraph_NodeId theOriginal) const; + + //! Direct lookup of the Generated images of @p theOriginal, non-recursive. + //! @param[in] theOriginal node id to query + //! @return pointer to the stored vector, or nullptr if @p theOriginal has + //! no Generated record. + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector* FindGenerated( + const BRepGraph_NodeId theOriginal) const; + + //! Test whether @p theOriginal was deleted by some recorded operation. + //! @param[in] theOriginal node id to query + //! @return true if @p theOriginal is in the deleted set + [[nodiscard]] Standard_EXPORT bool IsDeleted(const BRepGraph_NodeId theOriginal) const; + + //! Borrowed access to the full deleted set. + //! @return reference to the deleted-node set + [[nodiscard]] Standard_EXPORT const NCollection_FlatMap& DeletedNodes() const; + + //! UID-keyed Modified images stored directly in this history. + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector* FindModified( + const BRepGraph_UID& theUID) const; + + //! Direct lookup of all immediate node origins of @p theDerived. + //! A derived entity can have more than one parent in reconstructive algorithms. + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector* FindOriginals( + const BRepGraph_NodeId theDerived) const; + + //! UID-keyed Generated images stored directly in this history. + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector* FindGenerated( + const BRepGraph_UID& theUID) const; + + //! UID-keyed deletion test stored directly in this history. + [[nodiscard]] Standard_EXPORT bool IsDeleted(const BRepGraph_UID& theUID) const; + + //! UID-keyed deleted set stored directly in this history. + [[nodiscard]] Standard_EXPORT const NCollection_FlatMap& DeletedUids() const; + + //! Test whether @p theUID was registered as an operation input. + [[nodiscard]] Standard_EXPORT bool HasKnownInput(const BRepGraph_UID& theUID) const; + + //! ItemUID-keyed Modified images stored directly in this history. + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector* FindModified( + const BRepGraph_ItemUID& theUID) const; + + //! ItemUID-keyed Generated images stored directly in this history. + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector* FindGenerated( + const BRepGraph_ItemUID& theUID) const; + + //! ItemUID-keyed deletion test stored directly in this history. + [[nodiscard]] Standard_EXPORT bool IsDeleted(const BRepGraph_ItemUID& theUID) const; + + //! ItemUID-keyed deleted set stored directly in this history. + [[nodiscard]] Standard_EXPORT const NCollection_FlatMap& DeletedItemUids() + const; + + //! Test whether @p theUID was registered as an operation input. + [[nodiscard]] Standard_EXPORT bool HasKnownInput(const BRepGraph_ItemUID& theUID) const; + + //! UID-keyed convenience: Modified images of the input identified by + //! @p theUID, resolved against @p theGraph. Returns an empty vector if + //! the UID cannot be resolved or has no Modified record. + //! @param[in] theGraph graph used to translate UID <-> NodeId + //! @param[in] theUID UID of the input entity + //! @return UIDs of the modified images (in record-insertion order) + [[nodiscard]] Standard_EXPORT NCollection_LinearVector FindModified( + const BRepGraph& theGraph, + const BRepGraph_UID& theUID) const; + + //! UID-keyed convenience: Generated images. See #FindModified for the + //! resolution contract. + //! @param[in] theGraph graph used to translate UID <-> NodeId + //! @param[in] theUID UID of the input entity + //! @return UIDs of the generated images (in record-insertion order) + [[nodiscard]] Standard_EXPORT NCollection_LinearVector FindGenerated( + const BRepGraph& theGraph, + const BRepGraph_UID& theUID) const; + + //! UID-keyed convenience: deletion test. + //! @param[in] theGraph graph used to resolve the UID + //! @param[in] theUID UID of the input entity + //! @return true if the resolved NodeId is in the deleted set + [[nodiscard]] Standard_EXPORT bool IsDeleted(const BRepGraph& theGraph, + const BRepGraph_UID& theUID) const; + + //! UID-keyed convenience: dump the full deleted set as UIDs. + //! @param[in] theGraph graph used to translate NodeId -> UID + //! @return UIDs of all deleted entities (insertion order is not stable) + [[nodiscard]] Standard_EXPORT NCollection_LinearVector DeletedUids( + const BRepGraph& theGraph) const; + + //! Number of recorded history events. + //! @return record count + [[nodiscard]] Standard_EXPORT size_t NbRecords() const; + + //! Access a record by index (0-based). + //! @param[in] theRecordIdx zero-based index into the records vector + //! @return the history record at the given index + [[nodiscard]] Standard_EXPORT const Event& Record(const size_t theRecordIdx) const; + + //! Enable or disable history recording. + //! @param[in] theVal true to enable, false to disable + Standard_EXPORT void SetEnabled(const bool theVal); + + //! Query whether history recording is enabled. + //! @return true if recording is active + [[nodiscard]] Standard_EXPORT bool IsEnabled() const; + + //! Clear all records and lookup maps. + Standard_EXPORT void Clear() noexcept override; + + //! Layer removal callback. Records pure graph deletions when enabled. + Standard_EXPORT void OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept override; + + //! Copy history records whose source items have copied target items. + Standard_EXPORT void CopyTo(const BRepGraph_CopyRemap& theCopy) const override; + + //! Clear derived caches by dropping collected history. + Standard_EXPORT void InvalidateAll() noexcept override; + + DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerHistory, BRepGraph_Layer) + +private: + //! Rebuild all lookup caches from myRecords. + void rebuildCaches(); + + NCollection_DynamicArray myRecords; + + //! Full reverse map: derived node -> all immediate original nodes. + NCollection_DataMap> + myDerivedToOriginals; + + //! Forward map: original node -> vector of Modified images. + NCollection_DataMap> + myOriginalToModified; + + //! Forward map: original node -> vector of Generated images. + NCollection_DataMap> + myOriginalToGenerated; + + //! Flat set of inputs that have been consumed (no image in the result). + NCollection_FlatMap myDeleted; + + //! UID-keyed forward map: original UID -> Modified image UIDs. + NCollection_DataMap> + myUidOriginalToModified; + + //! UID-keyed forward map: original UID -> Generated image UIDs. + NCollection_DataMap> + myUidOriginalToGenerated; + + //! UID-keyed operation inputs, including inputs with no images. + NCollection_FlatMap myUidKnownInputs; + + //! UID-keyed consumed inputs. + NCollection_FlatMap myUidDeleted; + + //! ItemUID-keyed forward map: original UID -> Modified image UIDs. + NCollection_DataMap> + myItemUidOriginalToModified; + + //! ItemUID-keyed forward map: original UID -> Generated image UIDs. + NCollection_DataMap> + myItemUidOriginalToGenerated; + + //! ItemUID-keyed operation inputs, including inputs with no images. + NCollection_FlatMap myItemUidKnownInputs; + + //! ItemUID-keyed consumed inputs. + NCollection_FlatMap myItemUidDeleted; + + bool myEnabled = true; +}; + +#endif // _BRepGraph_LayerHistory_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerIterator.hxx index eeac605c9a..d50cec6321 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerIterator.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerIterator.hxx @@ -50,16 +50,13 @@ public: void Next() { ++myCurrent; } //! Return the current layer handle. - [[nodiscard]] const occ::handle& Value() const - { - return myRegistry->Layer(myCurrent); - } + [[nodiscard]] occ::handle Value() const { return myRegistry->Layer(myCurrent); } //! Return the current slot index in the registry. - [[nodiscard]] int Slot() const { return myCurrent; } + [[nodiscard]] uint32_t Slot() const { return myCurrent; } //! Number of layers in the registry. - [[nodiscard]] int NbLayers() const { return myCount; } + [[nodiscard]] uint32_t NbLayers() const { return myCount; } //! STL range-for support. NCollection_ForwardRangeIterator begin() @@ -72,8 +69,8 @@ public: private: const BRepGraph_LayerRegistry* myRegistry; - int myCount; - int myCurrent; + uint32_t myCount; + uint32_t myCurrent; }; #endif // _BRepGraph_LayerIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerLock.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerLock.cxx new file mode 100644 index 0000000000..736965efeb --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerLock.cxx @@ -0,0 +1,843 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerLock, BRepGraph_Layer) + +namespace +{ +using OwnerMap = NCollection_FlatDataMap; +} + +//================================================================================================= + +BRepGraph_LayerLock::ScopedOwnerEdit::ScopedOwnerEdit(BRepGraph_LayerLock& theLayer, + const BRepGraph_ItemId theItem, + const Standard_GUID& theOwnerId + [[maybe_unused]]) + : myLayer(&theLayer), + myItem(theItem), + myIsActive(false) +{ + Standard_ProgramError_Raise_if(!theItem.IsValid(), + "BRepGraph_LayerLock::ScopedOwnerEdit: invalid item"); + Standard_GUID anOwnerId [[maybe_unused]]; + Standard_ProgramError_Raise_if(!theLayer.FindOwnerId(theItem, anOwnerId) + || anOwnerId != theOwnerId, + "BRepGraph_LayerLock::ScopedOwnerEdit: owner mismatch"); + theLayer.setItemOwned(theItem, false); + myIsActive = true; +} + +//================================================================================================= + +BRepGraph_LayerLock::ScopedOwnerEdit::~ScopedOwnerEdit() +{ + if (myLayer != nullptr && myIsActive) + { + myLayer->setItemOwned(myItem, true); + } +} + +//================================================================================================= + +BRepGraph_LayerLock::ScopedOwnerEdit::ScopedOwnerEdit(ScopedOwnerEdit&& theOther) noexcept + : myLayer(theOther.myLayer), + myItem(theOther.myItem), + myIsActive(theOther.myIsActive) +{ + theOther.myLayer = nullptr; + theOther.myIsActive = false; +} + +//================================================================================================= + +BRepGraph_LayerLock::ScopedOwnerEdit& BRepGraph_LayerLock::ScopedOwnerEdit::operator=( + ScopedOwnerEdit&& theOther) noexcept +{ + if (this != &theOther) + { + if (myLayer != nullptr && myIsActive) + { + myLayer->setItemOwned(myItem, true); + } + myLayer = theOther.myLayer; + myItem = theOther.myItem; + myIsActive = theOther.myIsActive; + theOther.myLayer = nullptr; + theOther.myIsActive = false; + } + return *this; +} + +//================================================================================================= + +BRepGraph_LayerLock::BRepGraph_LayerLock() = default; + +//================================================================================================= + +const Standard_GUID& BRepGraph_LayerLock::GetID() +{ + static const Standard_GUID THE_ID("6f4f4d72-2e64-4ad9-8d02-d3833fe4b9d3"); + return THE_ID; +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_LayerLock::ID() const +{ + return GetID(); +} + +//================================================================================================= + +bool BRepGraph_LayerLock::FindOwnerId(const BRepGraph_ItemId theItem, + Standard_GUID& theOwnerId) const +{ + if (!theItem.IsValid()) + { + return false; + } + + // Refs: check direct entry, then traverse via parent node. + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Reference) + { + const Standard_GUID* aDirectId = myRefOwners.Seek(theItem); + if (aDirectId != nullptr) + { + theOwnerId = *aDirectId; + return true; + } + BRepGraph_ItemId aRootItem; + if (findRootRefId(theItem.RefId(), aRootItem)) + { + const OwnerMap* aMap = + aRootItem.ItemDomain() == BRepGraph_ItemId::Domain::Node ? &myNodeOwners : &myRefOwners; + const Standard_GUID* anOwnerId = aMap->Seek(aRootItem); + if (anOwnerId != nullptr) + { + theOwnerId = *anOwnerId; + return true; + } + } + return false; + } + + // Nodes: check direct entry, then traverse upward. + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Node) + { + const Standard_GUID* aDirectId = myNodeOwners.Seek(theItem); + if (aDirectId != nullptr) + { + theOwnerId = *aDirectId; + return true; + } + BRepGraph_ItemId aRootItem; + if (findRootNodeId(theItem.NodeId(), aRootItem)) + { + const Standard_GUID* anOwnerId = myNodeOwners.Seek(aRootItem); + if (anOwnerId != nullptr) + { + theOwnerId = *anOwnerId; + return true; + } + } + return false; + } + + return false; +} + +//================================================================================================= + +bool BRepGraph_LayerLock::HasOwner(const BRepGraph_ItemId theItem) const +{ + BRepGraph* aGraph = AttachedGraph(); + if (aGraph == nullptr || !theItem.IsValid()) + { + return false; + } + + const BRepGraphInc_Storage& aStorage = aGraph->incStorage(); + + switch (theItem.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: + return BRepGraph_NodeId::Visit(theItem.NodeId(), [&](const auto& theTypedId) { + return aStorage.IsOwned(theTypedId); + }); + case BRepGraph_ItemId::Domain::Reference: + return BRepGraph_RefId::Visit(theItem.RefId(), [&](const auto& theTypedId) { + return aStorage.IsOwned(theTypedId); + }); + case BRepGraph_ItemId::Domain::None: + return false; + } + return false; +} + +//================================================================================================= + +void BRepGraph_LayerLock::ReserveOwners(const size_t theNbOwners) +{ + myNodeOwners.Reserve(static_cast(myNodeOwners.Extent()) + theNbOwners); + myRefOwners.Reserve(static_cast(myRefOwners.Extent()) + theNbOwners); +} + +//================================================================================================= + +void BRepGraph_LayerLock::SetOwner(const BRepGraph_ItemId theItem, const Standard_GUID& theOwnerId) +{ + const bool aWasSet = SetOwner(theItem, theOwnerId, true); + Standard_ASSERT_RAISE(aWasSet, "BRepGraph_LayerLock::SetOwner: rejected or invalid item"); +} + +//================================================================================================= + +bool BRepGraph_LayerLock::SetOwner(const BRepGraph_ItemId theItem, + const Standard_GUID& theOwnerId, + const bool theToUpdateRevision) +{ + if (!theItem.IsValid()) + { + return false; + } + + // Check if item already has a direct root entry. + OwnerMap* aMap = + theItem.ItemDomain() == BRepGraph_ItemId::Domain::Reference ? &myRefOwners : &myNodeOwners; + + Standard_GUID* anExisting = aMap->ChangeSeek(theItem); + if (anExisting != nullptr) + { + // Direct entry exists. + if (*anExisting == theOwnerId) + { + // Same GUID: idempotent re-registration. Ensure bit is set. + setItemOwned(theItem, true); + if (theToUpdateRevision) + { + touch(); + } + return false; // No storage change. + } + // Different GUID on same item: reject. + return false; + } + + Standard_GUID anAncestorOwnerId; + if (isCoveredByAncestor(theItem, anAncestorOwnerId)) + { + if (anAncestorOwnerId == theOwnerId) + { + setItemOwned(theItem, true); + if (theToUpdateRevision) + { + touch(); + } + } + return false; + } + + NCollection_LinearVector aSameOwnerDescendantRoots; + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Node) + { + BRepGraph* aGraph = AttachedGraph(); + if (aGraph != nullptr) + { + for (BRepGraph_ChildExplorer anExp(*aGraph, theItem.NodeId()); anExp.More(); anExp.Next()) + { + const BRepGraph_ItemId aChildItem(anExp.Current().DefId); + if (const Standard_GUID* aChildOwner = myNodeOwners.Seek(aChildItem)) + { + if (*aChildOwner != theOwnerId) + { + return false; + } + aSameOwnerDescendantRoots.Append(aChildItem); + } + + const BRepGraph_RefId aRefId = anExp.CurrentRef(); + if (aRefId.IsValid()) + { + const BRepGraph_ItemId aRefItem(aRefId); + if (const Standard_GUID* aRefOwner = myRefOwners.Seek(aRefItem)) + { + if (*aRefOwner != theOwnerId) + { + return false; + } + aSameOwnerDescendantRoots.Append(aRefItem); + } + } + } + } + } + + for (const BRepGraph_ItemId& aDescendantRoot : aSameOwnerDescendantRoots) + { + if (aDescendantRoot.ItemDomain() == BRepGraph_ItemId::Domain::Node) + { + myNodeOwners.UnBind(aDescendantRoot); + } + else if (aDescendantRoot.ItemDomain() == BRepGraph_ItemId::Domain::Reference) + { + myRefOwners.UnBind(aDescendantRoot); + } + } + + // New root entry. + aMap->Bind(theItem, theOwnerId); + setItemOwned(theItem, true); + + // Propagate bit-flag to descendants for nodes. + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Node) + { + expandOwnership(theItem.NodeId(), true); + } + + if (theToUpdateRevision) + { + touch(); + } + return true; +} + +//================================================================================================= + +void BRepGraph_LayerLock::TouchOwners() +{ + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerLock::UnsetOwner(const BRepGraph_ItemId theItem) +{ + if (!theItem.IsValid()) + { + return; + } + + bool wasRemoved = false; + + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Node) + { + wasRemoved = myNodeOwners.UnBind(theItem); + if (wasRemoved) + { + setItemOwned(theItem, false); + expandOwnership(theItem.NodeId(), false); + rebuildOwnedFlagsFromRoots(); + } + } + else if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Reference) + { + wasRemoved = myRefOwners.UnBind(theItem); + if (wasRemoved) + { + setItemOwned(theItem, false); + rebuildOwnedFlagsFromRoots(); + } + } + + if (wasRemoved) + { + touch(); + } +} + +//================================================================================================= + +void BRepGraph_LayerLock::UnsetOwner(const BRepGraph_ItemId theItem, + const Standard_GUID& theOwnerId) +{ + if (!theItem.IsValid()) + { + return; + } + + // Verify the root entry matches the GUID before unsetting. + const Standard_GUID* anOwnerId = nullptr; + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Node) + { + anOwnerId = myNodeOwners.Seek(theItem); + } + else if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Reference) + { + anOwnerId = myRefOwners.Seek(theItem); + } + if (anOwnerId == nullptr || *anOwnerId != theOwnerId) + { + return; + } + UnsetOwner(theItem); +} + +//================================================================================================= + +const TCollection_AsciiString& BRepGraph_LayerLock::Name() const +{ + static const TCollection_AsciiString THE_NAME("Lock"); + return THE_NAME; +} + +//================================================================================================= + +void BRepGraph_LayerLock::OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept +{ + const BRepGraph_ItemId anItem(theNode); + if (myNodeOwners.UnBind(anItem)) + { + setItemOwned(anItem, false); + expandOwnership(theNode, false); + rebuildOwnedFlagsFromRoots(); + touch(); + } +} + +//================================================================================================= + +void BRepGraph_LayerLock::OnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept +{ + const BRepGraph_ItemId anOldItem(theOldNode); + const BRepGraph_ItemId aNewItem(theNewNode); + const Standard_GUID* anOwnerId = myNodeOwners.Seek(anOldItem); + if (anOwnerId == nullptr) + { + return; + } + + const Standard_GUID anOwnerIdCopy = *anOwnerId; + myNodeOwners.UnBind(anOldItem); + myNodeOwners.Bind(aNewItem, anOwnerIdCopy); + setItemOwned(anOldItem, false); + setItemOwned(aNewItem, true); + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerLock::CopyTo(const BRepGraph_CopyRemap& theCopy) const +{ + if (myNodeOwners.IsEmpty() && myRefOwners.IsEmpty()) + { + return; + } + + occ::handle aTarget = + theCopy.TargetGraph().LayerRegistry().Ensure(); + aTarget->ReserveOwners(static_cast(myNodeOwners.Extent() + myRefOwners.Extent())); + bool hasCopied = false; + + // Copy node root entries. + for (OwnerMap::Iterator anIt(myNodeOwners); anIt.More(); anIt.Next()) + { + const BRepGraph_ItemId* aTargetItem = theCopy.TargetItem(anIt.Key()); + if (aTargetItem == nullptr || !aTargetItem->IsValid()) + { + continue; + } + hasCopied = aTarget->SetOwner(*aTargetItem, anIt.Value(), false) || hasCopied; + } + + // Copy ref root entries. + for (OwnerMap::Iterator anIt(myRefOwners); anIt.More(); anIt.Next()) + { + const BRepGraph_ItemId* aTargetItem = theCopy.TargetItem(anIt.Key()); + if (aTargetItem == nullptr || !aTargetItem->IsValid()) + { + continue; + } + hasCopied = aTarget->SetOwner(*aTargetItem, anIt.Value(), false) || hasCopied; + } + + if (hasCopied) + { + aTarget->TouchOwners(); + } +} + +//================================================================================================= + +void BRepGraph_LayerLock::OnRefRemoved(const BRepGraph_RefId theRef) noexcept +{ + const BRepGraph_ItemId anItem(theRef); + if (myRefOwners.UnBind(anItem)) + { + setItemOwned(anItem, false); + touch(); + } +} + +//================================================================================================= + +void BRepGraph_LayerLock::InvalidateAll() noexcept +{ + Clear(); +} + +//================================================================================================= + +void BRepGraph_LayerLock::Clear() noexcept +{ + // Clear bits on all node root entries and their descendants. + for (OwnerMap::Iterator anIt(myNodeOwners); anIt.More(); anIt.Next()) + { + setItemOwned(anIt.Key(), false); + expandOwnership(anIt.Key().NodeId(), false); + } + myNodeOwners.Clear(true); + + // Clear bits on all ref root entries. + for (OwnerMap::Iterator anIt(myRefOwners); anIt.More(); anIt.Next()) + { + setItemOwned(anIt.Key(), false); + } + myRefOwners.Clear(true); + + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerLock::setItemOwned(const BRepGraph_ItemId theItem, const bool theIsOwned) const +{ + BRepGraph* aGraph = AttachedGraph(); + if (aGraph == nullptr) + { + return; + } + + BRepGraphInc_Storage& aStorage = aGraph->incStorage(); + + switch (theItem.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: { + BRepGraph_NodeId::Visit(theItem.NodeId(), [&](const auto& theTypedId) { + aStorage.SetOwned(theTypedId, theIsOwned); + }); + return; + } + case BRepGraph_ItemId::Domain::Reference: { + BRepGraph_RefId::Visit(theItem.RefId(), [&](const auto& theTypedId) { + aStorage.SetOwned(theTypedId, theIsOwned); + }); + return; + } + case BRepGraph_ItemId::Domain::None: + return; + } +} + +//================================================================================================= + +void BRepGraph_LayerLock::expandOwnership(const BRepGraph_NodeId theRoot, const bool theIsOwned) +{ + BRepGraph* aGraph = AttachedGraph(); + if (aGraph == nullptr) + { + return; + } + + for (BRepGraph_ChildExplorer anExp(*aGraph, theRoot); anExp.More(); anExp.Next()) + { + // Set/clear bit on child node and on the relation ref used to reach it. + setItemOwned(BRepGraph_ItemId(anExp.Current().DefId), theIsOwned); + const BRepGraph_RefId aRef = anExp.CurrentRef(); + if (aRef.IsValid()) + { + setItemOwned(BRepGraph_ItemId(aRef), theIsOwned); + } + } +} + +//================================================================================================= + +void BRepGraph_LayerLock::rebuildOwnedFlagsFromRoots() +{ + for (OwnerMap::Iterator anIt(myNodeOwners); anIt.More(); anIt.Next()) + { + setItemOwned(anIt.Key(), true); + expandOwnership(anIt.Key().NodeId(), true); + } + + for (OwnerMap::Iterator anIt(myRefOwners); anIt.More(); anIt.Next()) + { + setItemOwned(anIt.Key(), true); + } +} + +//================================================================================================= + +bool BRepGraph_LayerLock::findRootNodeId(const BRepGraph_NodeId theNode, + BRepGraph_ItemId& theRootItem) const +{ + BRepGraph* aGraph = AttachedGraph(); + if (aGraph == nullptr) + { + return false; + } + + const BRepGraphInc_Storage& aStorage = aGraph->incStorage(); + + // Check if this node's bit-flag is set at all. + bool isOwned = false; + BRepGraph_NodeId::Visit(theNode, + [&](const auto& theTypedId) { isOwned = aStorage.IsOwned(theTypedId); }); + if (!isOwned) + { + return false; + } + + // Check if this node is a root (parent is NOT owned). + bool parentOwned = false; + for (auto [anId, aLoc, anOri] : + BRepGraph_ParentExplorer(*aGraph, + theNode, + BRepGraph_ParentExplorer::TraversalMode::DirectParents)) + { + BRepGraph_NodeId::Visit(anId, [&](const auto& theTypedId) { + if (aStorage.IsOwned(theTypedId)) + { + parentOwned = true; + } + }); + if (parentOwned) + { + break; + } + } + + if (!parentOwned) + { + // This node IS the root. + theRootItem = BRepGraph_ItemId(theNode); + return true; + } + + // Traverse upward to find the root. + for (auto [anId, aLoc, anOri] : BRepGraph_ParentExplorer(*aGraph, theNode)) + { + // Check if this ancestor is owned. + bool ancestorOwned = false; + BRepGraph_NodeId::Visit(anId, [&](const auto& theTypedId) { + ancestorOwned = aStorage.IsOwned(theTypedId); + }); + if (!ancestorOwned) + { + // Previous ancestor was the root. We've gone past it. + break; + } + + // Check if this ancestor's parent is NOT owned (making it the root). + bool ancestorParentOwned = false; + for (auto [aParentId, aPLoc, aPOri] : + BRepGraph_ParentExplorer(*aGraph, + anId, + BRepGraph_ParentExplorer::TraversalMode::DirectParents)) + { + BRepGraph_NodeId::Visit(aParentId, [&](const auto& theTypedId) { + if (aStorage.IsOwned(theTypedId)) + { + ancestorParentOwned = true; + } + }); + if (ancestorParentOwned) + { + break; + } + } + + if (!ancestorParentOwned) + { + theRootItem = BRepGraph_ItemId(anId); + return true; + } + } + + return false; +} + +//================================================================================================= + +bool BRepGraph_LayerLock::findRootRefId(const BRepGraph_RefId theRef, + BRepGraph_ItemId& theRootItem) const +{ + // Check direct entry in ref map. + const BRepGraph_ItemId anItem(theRef); + if (myRefOwners.IsBound(anItem)) + { + theRootItem = anItem; + return true; + } + + // Get parent node and traverse from there. + const BRepGraph_NodeId aParentNode = parentNodeId(theRef); + if (aParentNode.IsValid()) + { + return findRootNodeId(aParentNode, theRootItem); + } + + return false; +} + +//================================================================================================= + +BRepGraph_NodeId BRepGraph_LayerLock::parentNodeId(const BRepGraph_RefId theRef) const +{ + BRepGraph* aGraph = AttachedGraph(); + if (aGraph == nullptr || !theRef.IsValid()) + { + return BRepGraph_NodeId(); + } + + const BRepGraphInc_Storage& aStorage = aGraph->incStorage(); + + return BRepGraph_RefId::Visit(theRef, [&](const auto& theTypedRef) -> BRepGraph_NodeId { + using RefT = std::decay_t; + if constexpr (std::is_same_v) + { + if (theTypedRef.IsValid(aStorage.NbShellRefs())) + { + return aStorage.ShellRef(theTypedRef).ParentSolidId; + } + } + else if constexpr (std::is_same_v) + { + if (theTypedRef.IsValid(aStorage.NbFaceRefs())) + { + return aStorage.FaceRef(theTypedRef).ParentShellId; + } + } + else if constexpr (std::is_same_v) + { + if (theTypedRef.IsValid(aStorage.NbWireRefs())) + { + return aStorage.WireRef(theTypedRef).ParentFaceId; + } + } + else if constexpr (std::is_same_v) + { + if (theTypedRef.IsValid(aStorage.NbSolidRefs())) + { + return BRepGraph_NodeId(aStorage.SolidRef(theTypedRef).ParentCompSolidId); + } + } + else if constexpr (std::is_same_v) + { + if (theTypedRef.IsValid(aStorage.NbChildRefs())) + { + return BRepGraph_NodeId(aStorage.ChildRef(theTypedRef).ParentCompoundId); + } + } + else if constexpr (std::is_same_v) + { + if (theTypedRef.IsValid(aStorage.NbOccurrenceRefs())) + { + return BRepGraph_NodeId(aStorage.OccurrenceRef(theTypedRef).ParentProductId); + } + } + else if constexpr (std::is_same_v) + { + if (theTypedRef.IsValid(aStorage.NbVertexRefs())) + { + return BRepGraph_NodeId(aStorage.VertexRef(theTypedRef).ParentEdgeId); + } + } + return BRepGraph_NodeId(); + }); +} + +//================================================================================================= + +bool BRepGraph_LayerLock::isCoveredByAncestor(const BRepGraph_ItemId theItem, + Standard_GUID& theRootOwnerId) const +{ + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Node) + { + BRepGraph* aGraph = AttachedGraph(); + if (aGraph == nullptr) + { + return false; + } + + for (auto [anId, aLoc, anOri] : BRepGraph_ParentExplorer(*aGraph, theItem.NodeId())) + { + const BRepGraph_ItemId anAncestorItem(anId); + const Standard_GUID* anOwnerId = myNodeOwners.Seek(anAncestorItem); + if (anOwnerId != nullptr) + { + theRootOwnerId = *anOwnerId; + return true; + } + } + return false; + } + + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Reference) + { + const BRepGraph_NodeId aParentNode = parentNodeId(theItem.RefId()); + if (aParentNode.IsValid()) + { + // Check if parent is a root. + const BRepGraph_ItemId aParentItem(aParentNode); + const Standard_GUID* anOwnerId = myNodeOwners.Seek(aParentItem); + if (anOwnerId != nullptr) + { + theRootOwnerId = *anOwnerId; + return true; + } + // Check ancestors of parent. + return isCoveredByAncestor(aParentItem, theRootOwnerId); + } + return false; + } + + return false; +} + +//================================================================================================= + +void BRepGraph_LayerLock::removeRootEntry(const BRepGraph_ItemId theItem) noexcept +{ + if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Node) + { + if (myNodeOwners.UnBind(theItem)) + { + setItemOwned(theItem, false); + touch(); + } + } + else if (theItem.ItemDomain() == BRepGraph_ItemId::Domain::Reference) + { + if (myRefOwners.UnBind(theItem)) + { + setItemOwned(theItem, false); + touch(); + } + } +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerLock.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerLock.hxx new file mode 100644 index 0000000000..0ad756efd1 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerLock.hxx @@ -0,0 +1,200 @@ +// 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. + +#ifndef _BRepGraph_LayerLock_HeaderFile +#define _BRepGraph_LayerLock_HeaderFile + +#include +#include +#include +#include + +//! Owner metadata layer for owned BRepGraph items. +//! +//! Uses a root-based ownership model: only the highest owned item per group +//! is stored in the map. All descendants receive the fast IsOwned bit-flag +//! via automatic downward propagation. Owner lookup traverses upward to +//! find the root entry. +//! +//! Overlapping roots are forbidden: SetOwner rejects if the item is already +//! covered by an ancestor root with a different GUID. +//! +//! HasOwner() checks the IsOwned bit-flag (O(1)). +//! FindOwnerId() traverses upward to find the root entry (O(depth)). +class BRepGraph_LayerLock : public BRepGraph_Layer +{ +public: + //! Scoped permission for an owner layer to edit one item it owns. + //! + //! The scope traverses upward to find the root owner for GUID verification, + //! then temporarily clears the fast owned bit on the specific item so existing + //! editor mutation APIs can be reused by the owning layer. + class ScopedOwnerEdit + { + public: + Standard_EXPORT ScopedOwnerEdit(BRepGraph_LayerLock& theLayer, + const BRepGraph_ItemId theItem, + const Standard_GUID& theOwnerId); + Standard_EXPORT ~ScopedOwnerEdit(); + + ScopedOwnerEdit(const ScopedOwnerEdit&) = delete; + ScopedOwnerEdit& operator=(const ScopedOwnerEdit&) = delete; + + Standard_EXPORT ScopedOwnerEdit(ScopedOwnerEdit&& theOther) noexcept; + Standard_EXPORT ScopedOwnerEdit& operator=(ScopedOwnerEdit&& theOther) noexcept; + + private: + BRepGraph_LayerLock* myLayer = nullptr; + BRepGraph_ItemId myItem; + bool myIsActive = false; + }; + + //! Create lock-owner storage. + Standard_EXPORT BRepGraph_LayerLock(); + + //! Return fixed layer type GUID. + [[nodiscard]] Standard_EXPORT static const Standard_GUID& GetID(); + + //! Return this layer type GUID. + [[nodiscard]] Standard_EXPORT const Standard_GUID& ID() const override; + + //! Return owner ID for an item. + //! Traverses upward for nodes/refs to find the root owner entry. + //! @return true when the item has a resolved owner and @p theOwnerId was filled. + [[nodiscard]] Standard_EXPORT bool FindOwnerId(const BRepGraph_ItemId theItem, + Standard_GUID& theOwnerId) const; + + //! Return owner ID for a node. + [[nodiscard]] bool FindOwnerId(const BRepGraph_NodeId theNode, Standard_GUID& theOwnerId) const + { + return FindOwnerId(BRepGraph_ItemId(theNode), theOwnerId); + } + + //! Return owner ID for a reference. + [[nodiscard]] bool FindOwnerId(const BRepGraph_RefId theRef, Standard_GUID& theOwnerId) const + { + return FindOwnerId(BRepGraph_ItemId(theRef), theOwnerId); + } + + //! Return true if an item's IsOwned bit-flag is set. + //! This is an O(1) check. Use FindOwnerId() to resolve the actual owner GUID. + [[nodiscard]] Standard_EXPORT bool HasOwner(const BRepGraph_ItemId theItem) const; + + //! Return true if a node's IsOwned bit-flag is set. + [[nodiscard]] bool HasOwner(const BRepGraph_NodeId theNode) const + { + return HasOwner(BRepGraph_ItemId(theNode)); + } + + //! Return true if a reference's IsOwned bit-flag is set. + [[nodiscard]] bool HasOwner(const BRepGraph_RefId theRef) const + { + return HasOwner(BRepGraph_ItemId(theRef)); + } + + //! Register an owner ID and set the graph item's ownership flag. + //! For nodes, propagates the IsOwned bit-flag to all descendants. + //! Rejects if the item is already covered by an ancestor root with a different GUID. + Standard_EXPORT void SetOwner(const BRepGraph_ItemId theItem, const Standard_GUID& theOwnerId); + + //! Register an owner ID and set the graph item's ownership flag. + //! Returns true when owner storage changed. Revision update can be deferred by bulk callers. + Standard_EXPORT bool SetOwner(const BRepGraph_ItemId theItem, + const Standard_GUID& theOwnerId, + const bool theToUpdateRevision); + + //! Register an owner ID and set the node ownership flag. + void SetOwner(const BRepGraph_NodeId theNode, const Standard_GUID& theOwnerId) + { + SetOwner(BRepGraph_ItemId(theNode), theOwnerId); + } + + //! Register an owner ID and set the reference ownership flag. + void SetOwner(const BRepGraph_RefId theRef, const Standard_GUID& theOwnerId) + { + SetOwner(BRepGraph_ItemId(theRef), theOwnerId); + } + + //! Remove an owner and clear the graph item's ownership flag. + //! For node roots, clears the IsOwned bit-flag on all descendants. + Standard_EXPORT void UnsetOwner(const BRepGraph_ItemId theItem); + + //! Remove an owner and clear the graph item's ownership flag if owner ID matches. + Standard_EXPORT void UnsetOwner(const BRepGraph_ItemId theItem, const Standard_GUID& theOwnerId); + + //! Remove an owner and clear the node ownership flag. + void UnsetOwner(const BRepGraph_NodeId theNode) { UnsetOwner(BRepGraph_ItemId(theNode)); } + + //! Remove an owner and clear the reference ownership flag. + void UnsetOwner(const BRepGraph_RefId theRef) { UnsetOwner(BRepGraph_ItemId(theRef)); } + + //! Return true if at least one root entry exists. + [[nodiscard]] bool HasOwners() const + { + return myNodeOwners.Extent() != 0 || myRefOwners.Extent() != 0; + } + + //! Reserve owner map buckets for bulk registration. + Standard_EXPORT void ReserveOwners(const size_t theNbOwners); + + //! Mark owner metadata changed after a bulk update. + Standard_EXPORT void TouchOwners(); + + Standard_EXPORT const TCollection_AsciiString& Name() const override; + Standard_EXPORT void OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept override; + Standard_EXPORT void OnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept override; + Standard_EXPORT void CopyTo(const BRepGraph_CopyRemap& theCopy) const override; + Standard_EXPORT void OnRefRemoved(const BRepGraph_RefId theRef) noexcept override; + Standard_EXPORT void InvalidateAll() noexcept override; + Standard_EXPORT void Clear() noexcept override; + + DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerLock, BRepGraph_Layer) + +private: + //! Set or clear the IsOwned bit-flag on a single item. + Standard_EXPORT void setItemOwned(const BRepGraph_ItemId theItem, const bool theIsOwned) const; + + //! Propagate the IsOwned bit-flag to all descendants of a root node. + //! Walks nodes via ChildExplorer, refs via CurrentRef(), and reps via definition fields. + void expandOwnership(const BRepGraph_NodeId theRoot, const bool theIsOwned); + + //! Rebuild fast IsOwned bit-flags from remaining root maps. + void rebuildOwnedFlagsFromRoots(); + + //! Find the root node entry that covers a given node. + //! Checks direct map entry first, then traverses upward via ParentExplorer. + [[nodiscard]] bool findRootNodeId(const BRepGraph_NodeId theNode, + BRepGraph_ItemId& theRootItem) const; + + //! Find the root entry that covers a given ref. + //! Checks direct map entry first, then traverses via parent node. + [[nodiscard]] bool findRootRefId(const BRepGraph_RefId theRef, + BRepGraph_ItemId& theRootItem) const; + + //! Get the parent node of a ref from its storage struct. + [[nodiscard]] BRepGraph_NodeId parentNodeId(const BRepGraph_RefId theRef) const; + + //! Check if an item is already covered by an ancestor root. + //! Returns true if an ancestor root exists. If so, fills theRootOwnerId. + [[nodiscard]] bool isCoveredByAncestor(const BRepGraph_ItemId theItem, + Standard_GUID& theRootOwnerId) const; + + //! Remove a root entry from the appropriate map and clear its bit-flag. + void removeRootEntry(const BRepGraph_ItemId theItem) noexcept; + + NCollection_FlatDataMap myNodeOwners; + NCollection_FlatDataMap myRefOwners; +}; + +#endif // _BRepGraph_LayerLock_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParam.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParam.cxx deleted file mode 100644 index 9071e4958f..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParam.cxx +++ /dev/null @@ -1,847 +0,0 @@ -// 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 - -IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerParam, BRepGraph_Layer) - -namespace -{ - -static const TCollection_AsciiString THE_LAYER_NAME("Param"); - -template -void appendUnique(NCollection_DataMap>& theMap, - const KeyT theKey, - const BRepGraph_VertexId theVertex) -{ - if (!theMap.IsBound(theKey)) - { - NCollection_DynamicArray aVertices; - aVertices.Append(theVertex); - theMap.Bind(theKey, aVertices); - return; - } - - NCollection_DynamicArray& aVertices = theMap.ChangeFind(theKey); - for (const BRepGraph_VertexId& aVtx : aVertices) - { - if (aVtx == theVertex) - { - return; - } - } - aVertices.Append(theVertex); -} - -template -void removeVertex(NCollection_DataMap>& theMap, - const KeyT theKey, - const BRepGraph_VertexId theVertex) noexcept -{ - if (!theMap.IsBound(theKey)) - { - return; - } - - NCollection_DynamicArray& aVertices = theMap.ChangeFind(theKey); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aVertices); anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value() != theVertex) - { - continue; - } - if (anIdx < static_cast(aVertices.Size()) - 1u) - { - aVertices.ChangeValue(static_cast(anIdx)) = aVertices.Value(aVertices.Size() - 1u); - } - aVertices.EraseLast(); - break; - } - - if (aVertices.IsEmpty()) - { - theMap.UnBind(theKey); - } -} - -static BRepGraph_VertexId remapVertex( - const NCollection_DataMap& theRemapMap, - const BRepGraph_VertexId theVertex) -{ - const BRepGraph_NodeId* aNewId = theRemapMap.Seek(theVertex); - if (aNewId == nullptr || aNewId->NodeKind != BRepGraph_NodeId::Kind::Vertex) - { - return BRepGraph_VertexId(); - } - return BRepGraph_VertexId(*aNewId); -} - -static BRepGraph_EdgeId remapEdge( - const NCollection_DataMap& theRemapMap, - const BRepGraph_EdgeId theEdge) -{ - const BRepGraph_NodeId* aNewId = theRemapMap.Seek(theEdge); - if (aNewId == nullptr || aNewId->NodeKind != BRepGraph_NodeId::Kind::Edge) - { - return BRepGraph_EdgeId(); - } - return BRepGraph_EdgeId(*aNewId); -} - -static BRepGraph_FaceId remapFace( - const NCollection_DataMap& theRemapMap, - const BRepGraph_FaceId theFace) -{ - const BRepGraph_NodeId* aNewId = theRemapMap.Seek(theFace); - if (aNewId == nullptr || aNewId->NodeKind != BRepGraph_NodeId::Kind::Face) - { - return BRepGraph_FaceId(); - } - return BRepGraph_FaceId(*aNewId); -} - -static BRepGraph_CoEdgeId remapCoEdge( - const NCollection_DataMap& theRemapMap, - const BRepGraph_CoEdgeId theCoEdge) -{ - const BRepGraph_NodeId* aNewId = theRemapMap.Seek(theCoEdge); - if (aNewId == nullptr || aNewId->NodeKind != BRepGraph_NodeId::Kind::CoEdge) - { - return BRepGraph_CoEdgeId(); - } - return BRepGraph_CoEdgeId(*aNewId); -} - -} // namespace - -//================================================================================================= - -const Standard_GUID& BRepGraph_LayerParam::GetID() -{ - static const Standard_GUID THE_LAYER_ID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10001"); - return THE_LAYER_ID; -} - -//================================================================================================= - -const Standard_GUID& BRepGraph_LayerParam::ID() const -{ - return GetID(); -} - -//================================================================================================= - -const TCollection_AsciiString& BRepGraph_LayerParam::Name() const -{ - return THE_LAYER_NAME; -} - -//================================================================================================= - -const BRepGraph_LayerParam::VertexParams* BRepGraph_LayerParam::FindVertexParams( - const BRepGraph_VertexId theVertex) const -{ - return myVertexParams.Seek(theVertex); -} - -//================================================================================================= - -bool BRepGraph_LayerParam::FindPointOnCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_EdgeId theEdge, - double* const theParameter) const -{ - const VertexParams* aParams = FindVertexParams(theVertex); - if (aParams == nullptr) - { - return false; - } - - for (const PointOnCurveEntry& anEntry : aParams->PointsOnCurve) - { - if (anEntry.EdgeDefId != theEdge) - { - continue; - } - if (theParameter != nullptr) - { - *theParameter = anEntry.Parameter; - } - return true; - } - return false; -} - -//================================================================================================= - -bool BRepGraph_LayerParam::FindPointOnSurface(const BRepGraph_VertexId theVertex, - const BRepGraph_FaceId theFace, - gp_Pnt2d* const theUV) const -{ - const VertexParams* aParams = FindVertexParams(theVertex); - if (aParams == nullptr) - { - return false; - } - - for (const PointOnSurfaceEntry& anEntry : aParams->PointsOnSurface) - { - if (anEntry.FaceDefId != theFace) - { - continue; - } - if (theUV != nullptr) - { - *theUV = gp_Pnt2d(anEntry.ParameterU, anEntry.ParameterV); - } - return true; - } - return false; -} - -//================================================================================================= - -bool BRepGraph_LayerParam::FindPointOnPCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_CoEdgeId theCoEdge, - double* const theParameter) const -{ - const VertexParams* aParams = FindVertexParams(theVertex); - if (aParams == nullptr) - { - return false; - } - - for (const PointOnPCurveEntry& anEntry : aParams->PointsOnPCurve) - { - if (anEntry.CoEdgeDefId != theCoEdge) - { - continue; - } - if (theParameter != nullptr) - { - *theParameter = anEntry.Parameter; - } - return true; - } - return false; -} - -//================================================================================================= - -uint32_t BRepGraph_LayerParam::NbPointsOnCurve(const BRepGraph_VertexId theVertex) const -{ - const VertexParams* aParams = FindVertexParams(theVertex); - return aParams == nullptr ? 0u : static_cast(aParams->PointsOnCurve.Size()); -} - -//================================================================================================= - -uint32_t BRepGraph_LayerParam::NbPointsOnSurface(const BRepGraph_VertexId theVertex) const -{ - const VertexParams* aParams = FindVertexParams(theVertex); - return aParams == nullptr ? 0u : static_cast(aParams->PointsOnSurface.Size()); -} - -//================================================================================================= - -uint32_t BRepGraph_LayerParam::NbPointsOnPCurve(const BRepGraph_VertexId theVertex) const -{ - const VertexParams* aParams = FindVertexParams(theVertex); - return aParams == nullptr ? 0u : static_cast(aParams->PointsOnPCurve.Size()); -} - -//================================================================================================= - -BRepGraph_LayerParam::VertexParams& BRepGraph_LayerParam::changeVertexParams( - const BRepGraph_VertexId theVertex) -{ - if (!myVertexParams.IsBound(theVertex)) - { - myVertexParams.Bind(theVertex, VertexParams()); - } - return myVertexParams.ChangeFind(theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::bindEdgeToVertex(const BRepGraph_EdgeId theEdge, - const BRepGraph_VertexId theVertex) -{ - appendUnique(myEdgeToVertices, theEdge, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::bindFaceToVertex(const BRepGraph_FaceId theFace, - const BRepGraph_VertexId theVertex) -{ - appendUnique(myFaceToVertices, theFace, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::bindCoEdgeToVertex(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_VertexId theVertex) -{ - appendUnique(myCoEdgeToVertices, theCoEdge, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::unbindEdgeFromVertex(const BRepGraph_EdgeId theEdge, - const BRepGraph_VertexId theVertex) noexcept -{ - removeVertex(myEdgeToVertices, theEdge, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::unbindFaceFromVertex(const BRepGraph_FaceId theFace, - const BRepGraph_VertexId theVertex) noexcept -{ - removeVertex(myFaceToVertices, theFace, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::unbindCoEdgeFromVertex(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_VertexId theVertex) noexcept -{ - removeVertex(myCoEdgeToVertices, theCoEdge, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::SetPointOnCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_EdgeId theEdge, - const double theParameter) -{ - VertexParams& aParams = changeVertexParams(theVertex); - for (NCollection_DynamicArray::Iterator anIt(aParams.PointsOnCurve); - anIt.More(); - anIt.Next()) - { - if (anIt.Value().EdgeDefId != theEdge) - { - continue; - } - anIt.ChangeValue().Parameter = theParameter; - return; - } - - PointOnCurveEntry& anEntry = aParams.PointsOnCurve.Appended(); - anEntry.Parameter = theParameter; - anEntry.EdgeDefId = theEdge; - bindEdgeToVertex(theEdge, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::SetPointOnSurface(const BRepGraph_VertexId theVertex, - const BRepGraph_FaceId theFace, - const double theParameterU, - const double theParameterV) -{ - VertexParams& aParams = changeVertexParams(theVertex); - for (NCollection_DynamicArray::Iterator anIt(aParams.PointsOnSurface); - anIt.More(); - anIt.Next()) - { - if (anIt.Value().FaceDefId != theFace) - { - continue; - } - anIt.ChangeValue().ParameterU = theParameterU; - anIt.ChangeValue().ParameterV = theParameterV; - return; - } - - PointOnSurfaceEntry& anEntry = aParams.PointsOnSurface.Appended(); - anEntry.ParameterU = theParameterU; - anEntry.ParameterV = theParameterV; - anEntry.FaceDefId = theFace; - bindFaceToVertex(theFace, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::SetPointOnPCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_CoEdgeId theCoEdge, - const double theParameter) -{ - VertexParams& aParams = changeVertexParams(theVertex); - for (NCollection_DynamicArray::Iterator anIt(aParams.PointsOnPCurve); - anIt.More(); - anIt.Next()) - { - if (anIt.Value().CoEdgeDefId != theCoEdge) - { - continue; - } - anIt.ChangeValue().Parameter = theParameter; - return; - } - - PointOnPCurveEntry& anEntry = aParams.PointsOnPCurve.Appended(); - anEntry.Parameter = theParameter; - anEntry.CoEdgeDefId = theCoEdge; - bindCoEdgeToVertex(theCoEdge, theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::removePointOnCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_EdgeId theEdge) noexcept -{ - if (!myVertexParams.IsBound(theVertex)) - { - return; - } - - VertexParams& aParams = myVertexParams.ChangeFind(theVertex); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aParams.PointsOnCurve); - anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value().EdgeDefId != theEdge) - { - continue; - } - if (anIdx < static_cast(aParams.PointsOnCurve.Size()) - 1u) - { - aParams.PointsOnCurve.ChangeValue(static_cast(anIdx)) = - aParams.PointsOnCurve.Value(aParams.PointsOnCurve.Size() - 1u); - } - aParams.PointsOnCurve.EraseLast(); - unbindEdgeFromVertex(theEdge, theVertex); - break; - } - - if (aParams.IsEmpty()) - { - myVertexParams.UnBind(theVertex); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::removePointOnSurface(const BRepGraph_VertexId theVertex, - const BRepGraph_FaceId theFace) noexcept -{ - if (!myVertexParams.IsBound(theVertex)) - { - return; - } - - VertexParams& aParams = myVertexParams.ChangeFind(theVertex); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aParams.PointsOnSurface); - anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value().FaceDefId != theFace) - { - continue; - } - if (anIdx < static_cast(aParams.PointsOnSurface.Size()) - 1u) - { - aParams.PointsOnSurface.ChangeValue(static_cast(anIdx)) = - aParams.PointsOnSurface.Value(aParams.PointsOnSurface.Size() - 1u); - } - aParams.PointsOnSurface.EraseLast(); - unbindFaceFromVertex(theFace, theVertex); - break; - } - - if (aParams.IsEmpty()) - { - myVertexParams.UnBind(theVertex); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::removePointOnPCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_CoEdgeId theCoEdge) noexcept -{ - if (!myVertexParams.IsBound(theVertex)) - { - return; - } - - VertexParams& aParams = myVertexParams.ChangeFind(theVertex); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aParams.PointsOnPCurve); - anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value().CoEdgeDefId != theCoEdge) - { - continue; - } - if (anIdx < static_cast(aParams.PointsOnPCurve.Size()) - 1u) - { - aParams.PointsOnPCurve.ChangeValue(static_cast(anIdx)) = - aParams.PointsOnPCurve.Value(aParams.PointsOnPCurve.Size() - 1u); - } - aParams.PointsOnPCurve.EraseLast(); - unbindCoEdgeFromVertex(theCoEdge, theVertex); - break; - } - - if (aParams.IsEmpty()) - { - myVertexParams.UnBind(theVertex); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::removeVertexBindings(const BRepGraph_VertexId theVertex) noexcept -{ - const VertexParams* aParams = myVertexParams.Seek(theVertex); - if (aParams == nullptr) - { - return; - } - - for (const PointOnCurveEntry& anEntry : aParams->PointsOnCurve) - { - unbindEdgeFromVertex(anEntry.EdgeDefId, theVertex); - } - for (const PointOnSurfaceEntry& anEntry : aParams->PointsOnSurface) - { - unbindFaceFromVertex(anEntry.FaceDefId, theVertex); - } - for (const PointOnPCurveEntry& anEntry : aParams->PointsOnPCurve) - { - unbindCoEdgeFromVertex(anEntry.CoEdgeDefId, theVertex); - } - myVertexParams.UnBind(theVertex); -} - -//================================================================================================= - -void BRepGraph_LayerParam::invalidateEdgeBindings(const BRepGraph_EdgeId theEdge) noexcept -{ - const NCollection_DynamicArray* aVertices = myEdgeToVertices.Seek(theEdge); - if (aVertices == nullptr) - { - return; - } - - const NCollection_DynamicArray aBoundVertices = *aVertices; - for (const BRepGraph_VertexId& aVtx : aBoundVertices) - { - removePointOnCurve(aVtx, theEdge); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::invalidateFaceBindings(const BRepGraph_FaceId theFace) noexcept -{ - const NCollection_DynamicArray* aVertices = myFaceToVertices.Seek(theFace); - if (aVertices == nullptr) - { - return; - } - - const NCollection_DynamicArray aBoundVertices = *aVertices; - for (const BRepGraph_VertexId& aVtx : aBoundVertices) - { - removePointOnSurface(aVtx, theFace); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::invalidateCoEdgeBindings(const BRepGraph_CoEdgeId theCoEdge) noexcept -{ - const NCollection_DynamicArray* aVertices = - myCoEdgeToVertices.Seek(theCoEdge); - if (aVertices == nullptr) - { - return; - } - - const NCollection_DynamicArray aBoundVertices = *aVertices; - for (const BRepGraph_VertexId& aVtx : aBoundVertices) - { - removePointOnPCurve(aVtx, theCoEdge); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::migrateVertexBindings(const BRepGraph_VertexId theOldVertex, - const BRepGraph_VertexId theNewVertex) noexcept -{ - const VertexParams* aParams = myVertexParams.Seek(theOldVertex); - if (aParams == nullptr) - { - return; - } - - const VertexParams aOldParams = *aParams; - removeVertexBindings(theOldVertex); - - for (const PointOnCurveEntry& anEntry : aOldParams.PointsOnCurve) - { - SetPointOnCurve(theNewVertex, anEntry.EdgeDefId, anEntry.Parameter); - } - for (const PointOnSurfaceEntry& anEntry : aOldParams.PointsOnSurface) - { - SetPointOnSurface(theNewVertex, anEntry.FaceDefId, anEntry.ParameterU, anEntry.ParameterV); - } - for (const PointOnPCurveEntry& anEntry : aOldParams.PointsOnPCurve) - { - SetPointOnPCurve(theNewVertex, anEntry.CoEdgeDefId, anEntry.Parameter); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::migrateEdgeBindings(const BRepGraph_EdgeId theOldEdge, - const BRepGraph_EdgeId theNewEdge) noexcept -{ - const NCollection_DynamicArray* aVertices = myEdgeToVertices.Seek(theOldEdge); - if (aVertices == nullptr) - { - return; - } - - const NCollection_DynamicArray aBoundVertices = *aVertices; - for (const BRepGraph_VertexId& aVtx : aBoundVertices) - { - double aParameter = 0.0; - if (!FindPointOnCurve(aVtx, theOldEdge, &aParameter)) - { - continue; - } - removePointOnCurve(aVtx, theOldEdge); - SetPointOnCurve(aVtx, theNewEdge, aParameter); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::migrateFaceBindings(const BRepGraph_FaceId theOldFace, - const BRepGraph_FaceId theNewFace) noexcept -{ - const NCollection_DynamicArray* aVertices = myFaceToVertices.Seek(theOldFace); - if (aVertices == nullptr) - { - return; - } - - const NCollection_DynamicArray aBoundVertices = *aVertices; - for (const BRepGraph_VertexId& aVtx : aBoundVertices) - { - gp_Pnt2d aUV; - if (!FindPointOnSurface(aVtx, theOldFace, &aUV)) - { - continue; - } - removePointOnSurface(aVtx, theOldFace); - SetPointOnSurface(aVtx, theNewFace, aUV.X(), aUV.Y()); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::migrateCoEdgeBindings(const BRepGraph_CoEdgeId theOldCoEdge, - const BRepGraph_CoEdgeId theNewCoEdge) noexcept -{ - const NCollection_DynamicArray* aVertices = - myCoEdgeToVertices.Seek(theOldCoEdge); - if (aVertices == nullptr) - { - return; - } - - const NCollection_DynamicArray aBoundVertices = *aVertices; - for (const BRepGraph_VertexId& aVtx : aBoundVertices) - { - double aParameter = 0.0; - if (!FindPointOnPCurve(aVtx, theOldCoEdge, &aParameter)) - { - continue; - } - removePointOnPCurve(aVtx, theOldCoEdge); - SetPointOnPCurve(aVtx, theNewCoEdge, aParameter); - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::OnNodeRemoved(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) noexcept -{ - switch (theNode.NodeKind) - { - case BRepGraph_NodeId::Kind::Vertex: - if (theReplacement.NodeKind == BRepGraph_NodeId::Kind::Vertex && theReplacement.IsValid()) - { - migrateVertexBindings(BRepGraph_VertexId(theNode), BRepGraph_VertexId(theReplacement)); - } - else - { - removeVertexBindings(BRepGraph_VertexId(theNode)); - } - break; - case BRepGraph_NodeId::Kind::Edge: - if (theReplacement.NodeKind == BRepGraph_NodeId::Kind::Edge && theReplacement.IsValid()) - { - migrateEdgeBindings(BRepGraph_EdgeId(theNode), BRepGraph_EdgeId(theReplacement)); - } - else - { - invalidateEdgeBindings(BRepGraph_EdgeId(theNode)); - } - break; - case BRepGraph_NodeId::Kind::Face: - if (theReplacement.NodeKind == BRepGraph_NodeId::Kind::Face && theReplacement.IsValid()) - { - migrateFaceBindings(BRepGraph_FaceId(theNode), BRepGraph_FaceId(theReplacement)); - } - else - { - invalidateFaceBindings(BRepGraph_FaceId(theNode)); - } - break; - case BRepGraph_NodeId::Kind::CoEdge: - if (theReplacement.NodeKind == BRepGraph_NodeId::Kind::CoEdge && theReplacement.IsValid()) - { - migrateCoEdgeBindings(BRepGraph_CoEdgeId(theNode), BRepGraph_CoEdgeId(theReplacement)); - } - else - { - invalidateCoEdgeBindings(BRepGraph_CoEdgeId(theNode)); - } - break; - default: - break; - } -} - -//================================================================================================= - -void BRepGraph_LayerParam::OnCompact( - const NCollection_DataMap& theRemapMap) noexcept -{ - NCollection_DataMap aNewParams; - NCollection_DataMap> aNewEdgeToVtx; - NCollection_DataMap> aNewFaceToVtx; - NCollection_DataMap> - aNewCoEdgeToVtx; - - for (const auto& [aOldVertex, aOldParams] : myVertexParams.Items()) - { - const BRepGraph_VertexId aNewVertex = remapVertex(theRemapMap, aOldVertex); - if (!aNewVertex.IsValid()) - { - continue; - } - - VertexParams aNewVP; - - for (const PointOnCurveEntry& anOldEntry : aOldParams.PointsOnCurve) - { - const BRepGraph_EdgeId aNewEdge = remapEdge(theRemapMap, anOldEntry.EdgeDefId); - if (!aNewEdge.IsValid()) - { - continue; - } - PointOnCurveEntry& anEntry = aNewVP.PointsOnCurve.Appended(); - anEntry.Parameter = anOldEntry.Parameter; - anEntry.EdgeDefId = aNewEdge; - appendUnique(aNewEdgeToVtx, aNewEdge, aNewVertex); - } - for (const PointOnSurfaceEntry& anOldEntry : aOldParams.PointsOnSurface) - { - const BRepGraph_FaceId aNewFace = remapFace(theRemapMap, anOldEntry.FaceDefId); - if (!aNewFace.IsValid()) - { - continue; - } - PointOnSurfaceEntry& anEntry = aNewVP.PointsOnSurface.Appended(); - anEntry.ParameterU = anOldEntry.ParameterU; - anEntry.ParameterV = anOldEntry.ParameterV; - anEntry.FaceDefId = aNewFace; - appendUnique(aNewFaceToVtx, aNewFace, aNewVertex); - } - for (const PointOnPCurveEntry& anOldEntry : aOldParams.PointsOnPCurve) - { - const BRepGraph_CoEdgeId aNewCoEdge = remapCoEdge(theRemapMap, anOldEntry.CoEdgeDefId); - if (!aNewCoEdge.IsValid()) - { - continue; - } - PointOnPCurveEntry& anEntry = aNewVP.PointsOnPCurve.Appended(); - anEntry.Parameter = anOldEntry.Parameter; - anEntry.CoEdgeDefId = aNewCoEdge; - appendUnique(aNewCoEdgeToVtx, aNewCoEdge, aNewVertex); - } - - if (!aNewVP.IsEmpty()) - { - // Merge into existing entry if multiple old vertices remap to the same new vertex. - VertexParams* anExisting = aNewParams.ChangeSeek(aNewVertex); - if (anExisting != nullptr) - { - for (const PointOnCurveEntry& anEntry : aNewVP.PointsOnCurve) - { - anExisting->PointsOnCurve.Append(anEntry); - } - for (const PointOnSurfaceEntry& anEntry : aNewVP.PointsOnSurface) - { - anExisting->PointsOnSurface.Append(anEntry); - } - for (const PointOnPCurveEntry& anEntry : aNewVP.PointsOnPCurve) - { - anExisting->PointsOnPCurve.Append(anEntry); - } - } - else - { - aNewParams.Bind(aNewVertex, std::move(aNewVP)); - } - } - } - - myVertexParams = std::move(aNewParams); - myEdgeToVertices = std::move(aNewEdgeToVtx); - myFaceToVertices = std::move(aNewFaceToVtx); - myCoEdgeToVertices = std::move(aNewCoEdgeToVtx); -} - -//================================================================================================= - -void BRepGraph_LayerParam::InvalidateAll() noexcept -{ - Clear(); -} - -//================================================================================================= - -void BRepGraph_LayerParam::Clear() noexcept -{ - myVertexParams.Clear(); - myEdgeToVertices.Clear(); - myFaceToVertices.Clear(); - myCoEdgeToVertices.Clear(); -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParam.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParam.hxx deleted file mode 100644 index 6afa5467c5..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParam.hxx +++ /dev/null @@ -1,167 +0,0 @@ -// 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. - -#ifndef _BRepGraph_LayerParam_HeaderFile -#define _BRepGraph_LayerParam_HeaderFile - -#include - -#include -#include -#include - -//! @brief Persistent vertex point-representation store: point-on-curve, -//! point-on-surface, and point-on-PCurve parameters per vertex. -//! -//! Mirrors classical BRep_PointRepresentation entries on TVertex: each vertex -//! may carry parameters identifying its location on incident edges, faces, or -//! coedges (PCurves). The layer is the single source of truth for these -//! parameters in BRepGraph. -//! -//! ## Lifetime policy -//! The layer is **persistent metadata**: stored values survive arbitrary -//! mutations to the referenced vertices, edges, faces, and coedges. Only the -//! following events discard data: -//! - OnNodeRemoved - the referenced node is gone; entries naming it are -//! dropped (or migrated when a replacement is provided). -//! - OnCompact - ids are remapped; entries pointing to removed nodes drop. -//! - InvalidateAll() / Clear() - explicit caller request. -//! The layer does NOT subscribe to OnNodeModified: a tolerance bump, parameter -//! range adjustment, or NaturalRestriction toggle on a referenced node leaves -//! point-representation data intact. Callers that change geometry are -//! responsible for refreshing affected entries. -class BRepGraph_LayerParam : public BRepGraph_Layer -{ -public: - //! Return fixed layer type GUID. - [[nodiscard]] Standard_EXPORT static const Standard_GUID& GetID(); - - //! Return this layer type GUID. - [[nodiscard]] Standard_EXPORT const Standard_GUID& ID() const override; - - struct PointOnCurveEntry - { - double Parameter = 0.0; - BRepGraph_EdgeId EdgeDefId; - }; - - struct PointOnSurfaceEntry - { - double ParameterU = 0.0; - double ParameterV = 0.0; - BRepGraph_FaceId FaceDefId; - }; - - struct PointOnPCurveEntry - { - double Parameter = 0.0; - BRepGraph_CoEdgeId CoEdgeDefId; - }; - - struct VertexParams - { - NCollection_DynamicArray PointsOnCurve; - NCollection_DynamicArray PointsOnSurface; - NCollection_DynamicArray PointsOnPCurve; - - [[nodiscard]] bool IsEmpty() const - { - return PointsOnCurve.IsEmpty() && PointsOnSurface.IsEmpty() && PointsOnPCurve.IsEmpty(); - } - }; - - Standard_EXPORT const VertexParams* FindVertexParams(const BRepGraph_VertexId theVertex) const; - - Standard_EXPORT bool FindPointOnCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_EdgeId theEdge, - double* const theParameter = nullptr) const; - - Standard_EXPORT bool FindPointOnSurface(const BRepGraph_VertexId theVertex, - const BRepGraph_FaceId theFace, - gp_Pnt2d* const theUV = nullptr) const; - - Standard_EXPORT bool FindPointOnPCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_CoEdgeId theCoEdge, - double* const theParameter = nullptr) const; - - Standard_EXPORT uint32_t NbPointsOnCurve(const BRepGraph_VertexId theVertex) const; - Standard_EXPORT uint32_t NbPointsOnSurface(const BRepGraph_VertexId theVertex) const; - Standard_EXPORT uint32_t NbPointsOnPCurve(const BRepGraph_VertexId theVertex) const; - - [[nodiscard]] bool HasBindings() const { return myVertexParams.Extent() != 0; } - - Standard_EXPORT void SetPointOnCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_EdgeId theEdge, - const double theParameter); - - Standard_EXPORT void SetPointOnSurface(const BRepGraph_VertexId theVertex, - const BRepGraph_FaceId theFace, - const double theParameterU, - const double theParameterV); - - Standard_EXPORT void SetPointOnPCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_CoEdgeId theCoEdge, - const double theParameter); - - Standard_EXPORT const TCollection_AsciiString& Name() const override; - Standard_EXPORT void OnNodeRemoved(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) noexcept override; - Standard_EXPORT void OnCompact( - const NCollection_DataMap& theRemapMap) noexcept override; - Standard_EXPORT void InvalidateAll() noexcept override; - Standard_EXPORT void Clear() noexcept override; - - DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerParam, BRepGraph_Layer) - -private: - void removeVertexBindings(const BRepGraph_VertexId theVertex) noexcept; - void invalidateEdgeBindings(const BRepGraph_EdgeId theEdge) noexcept; - void invalidateFaceBindings(const BRepGraph_FaceId theFace) noexcept; - void invalidateCoEdgeBindings(const BRepGraph_CoEdgeId theCoEdge) noexcept; - void migrateVertexBindings(const BRepGraph_VertexId theOldVertex, - const BRepGraph_VertexId theNewVertex) noexcept; - void migrateEdgeBindings(const BRepGraph_EdgeId theOldEdge, - const BRepGraph_EdgeId theNewEdge) noexcept; - void migrateFaceBindings(const BRepGraph_FaceId theOldFace, - const BRepGraph_FaceId theNewFace) noexcept; - void migrateCoEdgeBindings(const BRepGraph_CoEdgeId theOldCoEdge, - const BRepGraph_CoEdgeId theNewCoEdge) noexcept; - - VertexParams& changeVertexParams(const BRepGraph_VertexId theVertex); - void bindEdgeToVertex(const BRepGraph_EdgeId theEdge, const BRepGraph_VertexId theVertex); - void bindFaceToVertex(const BRepGraph_FaceId theFace, const BRepGraph_VertexId theVertex); - void bindCoEdgeToVertex(const BRepGraph_CoEdgeId theCoEdge, const BRepGraph_VertexId theVertex); - void unbindEdgeFromVertex(const BRepGraph_EdgeId theEdge, - const BRepGraph_VertexId theVertex) noexcept; - void unbindFaceFromVertex(const BRepGraph_FaceId theFace, - const BRepGraph_VertexId theVertex) noexcept; - void unbindCoEdgeFromVertex(const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_VertexId theVertex) noexcept; - void removePointOnCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_EdgeId theEdge) noexcept; - void removePointOnSurface(const BRepGraph_VertexId theVertex, - const BRepGraph_FaceId theFace) noexcept; - void removePointOnPCurve(const BRepGraph_VertexId theVertex, - const BRepGraph_CoEdgeId theCoEdge) noexcept; - -private: - NCollection_DataMap myVertexParams; - NCollection_DataMap> - myEdgeToVertices; - NCollection_DataMap> - myFaceToVertices; - NCollection_DataMap> - myCoEdgeToVertices; -}; - -#endif // _BRepGraph_LayerParam_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParametric.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParametric.cxx new file mode 100644 index 0000000000..c43a4d96d5 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParametric.cxx @@ -0,0 +1,40 @@ +// 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 "BRepGraph_LayerParametric.hxx" + +#include +#include +#include +#include + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerParametric, BRepGraph_Layer) + +//================================================================================================= + +BRepGraph* BRepGraph_LayerParametric::graphForMutation() const +{ + BRepGraph* aGraph = AttachedGraph(); + Standard_ProgramError_Raise_if(aGraph == nullptr, + "BRepGraph_LayerParametric: layer is detached from graph"); + return aGraph; +} + +//================================================================================================= + +occ::handle BRepGraph_LayerParametric::lockLayer(BRepGraph& theGraph) const +{ + return theGraph.LayerRegistry().Ensure(); +} + +//================================================================================================= diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParametric.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParametric.hxx new file mode 100644 index 0000000000..a6743d0cf6 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerParametric.hxx @@ -0,0 +1,130 @@ +// 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. + +#ifndef _BRepGraph_LayerParametric_HeaderFile +#define _BRepGraph_LayerParametric_HeaderFile + +#include + +#include +#include + +class BRepGraph_LayerLock; + +//! @brief Base layer for graph-owned parametric generators. +//! +//! The class defines the common instance identity, generation flags, mesh +//! quality controls, and graph access helpers shared by higher-level parametric +//! layers. +//! Concrete layers such as BRepGraphPrim box, plane, or loft generators build +//! their own parameter schema and manifest storage on top of this base. +class BRepGraph_LayerParametric : public BRepGraph_Layer +{ +public: + //! Controls which graph artifacts should be created or refreshed. + enum class GenerationFlag : uint32_t + { + Topology = 0x01, //!< Create or preserve topological nodes and references. + Geometry = 0x02, //!< Bind analytic reps; absence keeps generated topology mesh-only. + Mesh = 0x04 //!< Create or preserve triangulation representations. + }; + + //! High-level mesh quality hint shared by parametric generators. + enum class MeshQuality : uint8_t + { + VeryCoarse, //!< Minimal preview-oriented detail. + Coarse, //!< Low detail for fast authoring feedback. + Medium, //!< Balanced default-quality detail. + Fine, //!< High-quality detail for closer inspection. + VeryFine //!< Maximum detail requested by the caller. + }; + + //! Result of adding a new parametric instance to a graph. + struct AddResult + { + uint32_t Instance = THE_INVALID_INSTANCE; //!< Created instance identifier. + BRepGraph_NodeId Root; //!< Root topology node of the created subtree. + }; + + //! Reserved sentinel used when an operation does not create an instance. + static constexpr uint32_t THE_INVALID_INSTANCE = std::numeric_limits::max(); + + //! Default generation mode builds topology and analytic geometry only. + static constexpr uint32_t THE_DEFAULT_GENERATION_FLAGS = + static_cast(GenerationFlag::Topology) + | static_cast(GenerationFlag::Geometry); + + //! Convert one generation flag into its bit-mask value. + //! @param[in] theFlag generation flag to convert + //! @return bit-mask value for the requested generation flag + [[nodiscard]] static constexpr uint32_t GenerationMask(const GenerationFlag theFlag) + { + return static_cast(theFlag); + } + + //! Return true when the flag mask contains the requested generation flag. + //! @param[in] theFlags generation mask built from GenerationFlag bits + //! @param[in] theFlag generation flag to test + //! @return true when the flag is present in the mask + [[nodiscard]] static constexpr bool HasGenerationFlag(const uint32_t theFlags, + const GenerationFlag theFlag) + { + return (theFlags & GenerationMask(theFlag)) != 0; + } + + //! Select one integer value from a mesh-quality ladder. + //! @param[in] theQuality requested shared mesh quality + //! @param[in] theVeryCoarse value for MeshQuality::VeryCoarse + //! @param[in] theCoarse value for MeshQuality::Coarse + //! @param[in] theMedium value for MeshQuality::Medium + //! @param[in] theFine value for MeshQuality::Fine + //! @param[in] theVeryFine value for MeshQuality::VeryFine + //! @return selected value for the requested quality + [[nodiscard]] static constexpr uint32_t MeshQualityValue(const MeshQuality theQuality, + const uint32_t theVeryCoarse, + const uint32_t theCoarse, + const uint32_t theMedium, + const uint32_t theFine, + const uint32_t theVeryFine) + { + switch (theQuality) + { + case MeshQuality::VeryCoarse: + return theVeryCoarse; + case MeshQuality::Coarse: + return theCoarse; + case MeshQuality::Medium: + return theMedium; + case MeshQuality::Fine: + return theFine; + case MeshQuality::VeryFine: + return theVeryFine; + } + return theMedium; + } + +protected: + //! Return the attached graph and raise if the layer is detached. + //! @return attached graph for mutation operations + [[nodiscard]] Standard_EXPORT BRepGraph* graphForMutation() const; + + //! Return the ownership layer used by parametric generators. + //! @param[in] theGraph graph whose ownership layer should be returned + //! @return ownership layer handle + [[nodiscard]] Standard_EXPORT occ::handle lockLayer( + BRepGraph& theGraph) const; + + DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerParametric, BRepGraph_Layer) +}; + +#endif // _BRepGraph_LayerParametric_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.cxx index c705bdbd27..4b711f351f 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.cxx @@ -13,16 +13,71 @@ #include +#include +#include #include +#include + +#include +#include //================================================================================================= -int BRepGraph_LayerRegistry::RegisterLayer(const occ::handle& theLayer) +BRepGraph_LayerRegistry::BRepGraph_LayerRegistry() = default; + +//================================================================================================= + +BRepGraph_LayerRegistry::BRepGraph_LayerRegistry(BRepGraph_LayerRegistry&& theOther) noexcept { - if (theLayer.IsNull()) + std::unique_lock aLock(theOther.myMutex); + myLayers = std::move(theOther.myLayers); + myGuidToSlot = std::move(theOther.myGuidToSlot); + mySubscribedKindsMask = theOther.mySubscribedKindsMask; + mySubscribedRefKindsMask = theOther.mySubscribedRefKindsMask; + myGraph = theOther.myGraph; + theOther.mySubscribedKindsMask = 0; + theOther.mySubscribedRefKindsMask = 0; + theOther.myGraph = nullptr; +} + +//================================================================================================= + +BRepGraph_LayerRegistry& BRepGraph_LayerRegistry::operator=( + BRepGraph_LayerRegistry&& theOther) noexcept +{ + if (this != &theOther) { - return -1; + std::unique_lock aThisLock(myMutex, std::defer_lock); + std::unique_lock anOtherLock(theOther.myMutex, std::defer_lock); + std::lock(aThisLock, anOtherLock); + + detachAllLocked(); + myLayers = std::move(theOther.myLayers); + myGuidToSlot = std::move(theOther.myGuidToSlot); + mySubscribedKindsMask = theOther.mySubscribedKindsMask; + mySubscribedRefKindsMask = theOther.mySubscribedRefKindsMask; + myGraph = theOther.myGraph; + theOther.mySubscribedKindsMask = 0; + theOther.mySubscribedRefKindsMask = 0; + theOther.myGraph = nullptr; } + return *this; +} + +//================================================================================================= + +uint32_t BRepGraph_LayerRegistry::RegisterLayer(const occ::handle& theLayer) +{ + std::unique_lock aLock(myMutex); + return registerLayerLocked(theLayer); +} + +//================================================================================================= + +uint32_t BRepGraph_LayerRegistry::registerLayerLocked(const occ::handle& theLayer) +{ + Standard_ProgramError_Raise_if(theLayer.IsNull(), + "BRepGraph_LayerRegistry::RegisterLayer() - null layer"); const Standard_GUID& aGUID = theLayer->ID(); const uint32_t* aSlot = myGuidToSlot.Seek(aGUID); @@ -31,35 +86,38 @@ int BRepGraph_LayerRegistry::RegisterLayer(const occ::handle& t const occ::handle& aPrev = myLayers.Value(static_cast(*aSlot)); if (!aPrev.IsNull() && aPrev.get() != theLayer.get()) { - aPrev->setOwningGraph(nullptr); + aPrev->detachContext(); } - theLayer->setOwningGraph(myOwningGraph); + theLayer->attachGraph(myGraph); myLayers.ChangeValue(static_cast(*aSlot)) = theLayer; recomputeSubscribedKindsMask(); - return static_cast(*aSlot); + return *aSlot; } + Standard_OutOfRange_Raise_if(myLayers.Size() > std::numeric_limits::max(), + "BRepGraph_LayerRegistry - too many registered layers"); const uint32_t aNewSlot = static_cast(myLayers.Size()); - theLayer->setOwningGraph(myOwningGraph); + theLayer->attachGraph(myGraph); myLayers.Append(theLayer); myGuidToSlot.Bind(aGUID, aNewSlot); mySubscribedKindsMask |= theLayer->SubscribedKinds(); mySubscribedRefKindsMask |= theLayer->SubscribedRefKinds(); - return static_cast(aNewSlot); + return aNewSlot; } //================================================================================================= void BRepGraph_LayerRegistry::UnregisterLayer(const Standard_GUID& theGUID) { - const uint32_t* aSlotPtr = myGuidToSlot.Seek(theGUID); + std::unique_lock aLock(myMutex); + const uint32_t* aSlotPtr = myGuidToSlot.Seek(theGUID); if (aSlotPtr == nullptr) { return; } const uint32_t aSlot = *aSlotPtr; - const uint32_t aLastSlot = static_cast(myLayers.Size()) - 1; + const uint32_t aLastSlot = static_cast(myLayers.Size() - 1); const occ::handle aRemoved = myLayers.Value(static_cast(aSlot)); if (aSlot != aLastSlot) { @@ -73,27 +131,46 @@ void BRepGraph_LayerRegistry::UnregisterLayer(const Standard_GUID& theGUID) recomputeSubscribedKindsMask(); if (!aRemoved.IsNull()) { - aRemoved->setOwningGraph(nullptr); + aRemoved->detachContext(); } } //================================================================================================= -void BRepGraph_LayerRegistry::SetOwningGraph(BRepGraph* theGraph) noexcept +void BRepGraph_LayerRegistry::Attach(BRepGraph* theGraph) noexcept { - myOwningGraph = theGraph; + std::unique_lock aLock(myMutex); + myGraph = theGraph; for (const occ::handle& aLayer : myLayers) { if (!aLayer.IsNull()) { - aLayer->setOwningGraph(theGraph); + aLayer->attachGraph(myGraph); } } } //================================================================================================= +void BRepGraph_LayerRegistry::Detach() noexcept +{ + std::unique_lock aLock(myMutex); + detachAllLocked(); + myGraph = nullptr; +} + +//================================================================================================= + occ::handle BRepGraph_LayerRegistry::FindLayer(const Standard_GUID& theGUID) const +{ + std::shared_lock aLock(myMutex); + return findLayerLocked(theGUID); +} + +//================================================================================================= + +occ::handle BRepGraph_LayerRegistry::findLayerLocked( + const Standard_GUID& theGUID) const { const uint32_t* aSlot = myGuidToSlot.Seek(theGUID); return aSlot != nullptr ? myLayers.Value(static_cast(*aSlot)) @@ -102,29 +179,90 @@ occ::handle BRepGraph_LayerRegistry::FindLayer(const Standard_G //================================================================================================= -int BRepGraph_LayerRegistry::FindSlot(const Standard_GUID& theGUID) const +bool BRepGraph_LayerRegistry::FindSlot(const Standard_GUID& theGUID, uint32_t& theSlot) const { - const uint32_t* aSlot = myGuidToSlot.Seek(theGUID); - return aSlot != nullptr ? static_cast(*aSlot) : -1; + std::shared_lock aLock(myMutex); + const uint32_t* aSlot = myGuidToSlot.Seek(theGUID); + if (aSlot == nullptr) + { + return false; + } + theSlot = *aSlot; + return true; } //================================================================================================= -const occ::handle& BRepGraph_LayerRegistry::Layer(const int theSlot) const +occ::handle BRepGraph_LayerRegistry::Layer(const uint32_t theSlot) const { - Standard_OutOfRange_Raise_if(theSlot < 0 || static_cast(theSlot) >= myLayers.Size(), - "BRepGraph_LayerRegistry::Layer() - invalid slot"); + std::shared_lock aLock(myMutex); + if (static_cast(theSlot) >= myLayers.Size()) + { + return occ::handle(); + } return myLayers.Value(static_cast(theSlot)); } //================================================================================================= -void BRepGraph_LayerRegistry::DispatchOnNodeRemoved(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) noexcept +occ::handle BRepGraph_LayerRegistry::layerAt(const uint32_t theSlot) const { - for (const occ::handle& aLayer : myLayers) + std::shared_lock aLock(myMutex); + if (static_cast(theSlot) >= myLayers.Size()) { - aLayer->OnNodeRemoved(theNode, theReplacement); + return occ::handle(); + } + return myLayers.Value(static_cast(theSlot)); +} + +//================================================================================================= + +void BRepGraph_LayerRegistry::DispatchOnNodeRemoved(const BRepGraph_NodeId theNode) noexcept +{ + for (uint32_t aSlot = 0;; ++aSlot) + { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } + aLayer->OnNodeRemoved(theNode); + } +} + +//================================================================================================= + +void BRepGraph_LayerRegistry::DispatchOnItemRemoved(const BRepGraph_ItemId theItem) noexcept +{ + if (!theItem.IsValid()) + { + return; + } + + for (uint32_t aSlot = 0;; ++aSlot) + { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } + aLayer->OnItemRemoved(theItem); + } +} + +//================================================================================================= + +void BRepGraph_LayerRegistry::DispatchOnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept +{ + for (uint32_t aSlot = 0;; ++aSlot) + { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } + aLayer->OnNodeReplaced(theOldNode, theNewNode); } } @@ -132,14 +270,22 @@ void BRepGraph_LayerRegistry::DispatchOnNodeRemoved(const BRepGraph_NodeId theNo void BRepGraph_LayerRegistry::DispatchNodeModified(const BRepGraph_NodeId theNode) noexcept { - if (!HasModificationSubscribers()) { - return; + std::shared_lock aLock(myMutex); + if (mySubscribedKindsMask == 0) + { + return; + } } const int aKindBit = BRepGraph_Layer::KindBit(theNode.NodeKind); - for (const occ::handle& aLayer : myLayers) + for (uint32_t aSlot = 0;; ++aSlot) { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } if ((aLayer->SubscribedKinds() & aKindBit) != 0) { aLayer->OnNodeModified(theNode); @@ -149,17 +295,47 @@ void BRepGraph_LayerRegistry::DispatchNodeModified(const BRepGraph_NodeId theNod //================================================================================================= -void BRepGraph_LayerRegistry::DispatchNodesModified( - const NCollection_DynamicArray& theModifiedNodes, - const int theModifiedKindsMask) noexcept +void BRepGraph_LayerRegistry::DispatchItemModified(const BRepGraph_ItemId theItem) noexcept { - if (!HasModificationSubscribers() || theModifiedKindsMask == 0) + if (!theItem.IsValid()) { return; } - for (const occ::handle& aLayer : myLayers) + switch (theItem.ItemDomain()) { + case BRepGraph_ItemId::Domain::Node: + DispatchNodeModified(theItem.NodeId()); + return; + case BRepGraph_ItemId::Domain::Reference: + DispatchRefModified(theItem.RefId()); + return; + case BRepGraph_ItemId::Domain::None: + return; + } +} + +//================================================================================================= + +void BRepGraph_LayerRegistry::DispatchNodesModified( + const NCollection_Array1& theModifiedNodes, + const int theModifiedKindsMask) noexcept +{ + { + std::shared_lock aLock(myMutex); + if (mySubscribedKindsMask == 0 || theModifiedKindsMask == 0) + { + return; + } + } + + for (uint32_t aSlot = 0;; ++aSlot) + { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } if ((aLayer->SubscribedKinds() & theModifiedKindsMask) != 0) { aLayer->OnNodesModified(theModifiedNodes); @@ -169,12 +345,60 @@ void BRepGraph_LayerRegistry::DispatchNodesModified( //================================================================================================= -void BRepGraph_LayerRegistry::DispatchOnCompact( - const NCollection_DataMap& theRemapMap) noexcept +void BRepGraph_LayerRegistry::CopyLayersTo( + BRepGraph& theTargetGraph, + const NCollection_FlatDataMap& theItemRemap, + const BRepGraph_CopyRemap::Mode theMode) const { - for (const occ::handle& aLayer : myLayers) + if (theMode == BRepGraph_CopyRemap::Mode::Compact) { - aLayer->OnCompact(theRemapMap); + BRepGraph_LayerRegistry& aSelf = const_cast(*this); + std::unique_lock aLock(aSelf.myMutex); + BRepGraph* aSourceGraph = myGraph; + // Compact: collect old layer handles, unregister all, call CopyTo on each + // which creates fresh layers in the target (same graph) via Ensure(). + NCollection_LinearVector> aOldLayers(myLayers.Size()); + for (const occ::handle& aLayer : myLayers) + { + if (!aLayer.IsNull()) + { + aOldLayers.Append(aLayer); + } + } + + aSelf.detachAllLocked(); + aSelf.myLayers.Clear(); + aSelf.myGuidToSlot.Clear(); + aSelf.mySubscribedKindsMask = 0; + aSelf.mySubscribedRefKindsMask = 0; + aLock.unlock(); + + // Call CopyTo on each old (now detached) layer. + const BRepGraph_CopyRemap aCopy(*aSourceGraph, theTargetGraph, theItemRemap, theMode); + for (const occ::handle& aLayer : aOldLayers) + { + aLayer->CopyTo(aCopy); + } + } + else + { + BRepGraph* aSourceGraph = nullptr; + { + std::shared_lock aLock(myMutex); + aSourceGraph = myGraph; + } + + // Copy: standard pass-through to each layer's CopyTo. + const BRepGraph_CopyRemap aCopy(*aSourceGraph, theTargetGraph, theItemRemap, theMode); + for (uint32_t aSlot = 0;; ++aSlot) + { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } + aLayer->CopyTo(aCopy); + } } } @@ -182,8 +406,13 @@ void BRepGraph_LayerRegistry::DispatchOnCompact( void BRepGraph_LayerRegistry::ClearAll() noexcept { - for (const occ::handle& aLayer : myLayers) + for (uint32_t aSlot = 0;; ++aSlot) { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } aLayer->Clear(); } } @@ -192,8 +421,13 @@ void BRepGraph_LayerRegistry::ClearAll() noexcept void BRepGraph_LayerRegistry::InvalidateAll() noexcept { - for (const occ::handle& aLayer : myLayers) + for (uint32_t aSlot = 0;; ++aSlot) { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } aLayer->InvalidateAll(); } } @@ -202,8 +436,13 @@ void BRepGraph_LayerRegistry::InvalidateAll() noexcept void BRepGraph_LayerRegistry::DispatchOnRefRemoved(const BRepGraph_RefId theRef) noexcept { - for (const occ::handle& aLayer : myLayers) + for (uint32_t aSlot = 0;; ++aSlot) { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } aLayer->OnRefRemoved(theRef); } } @@ -212,14 +451,22 @@ void BRepGraph_LayerRegistry::DispatchOnRefRemoved(const BRepGraph_RefId theRef) void BRepGraph_LayerRegistry::DispatchRefModified(const BRepGraph_RefId theRef) noexcept { - if (!HasRefModificationSubscribers()) { - return; + std::shared_lock aLock(myMutex); + if (mySubscribedRefKindsMask == 0) + { + return; + } } const int aRefKindBit = BRepGraph_Layer::RefKindBit(theRef.RefKind); - for (const occ::handle& aLayer : myLayers) + for (uint32_t aSlot = 0;; ++aSlot) { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } if ((aLayer->SubscribedRefKinds() & aRefKindBit) != 0) { aLayer->OnRefModified(theRef); @@ -230,19 +477,40 @@ void BRepGraph_LayerRegistry::DispatchRefModified(const BRepGraph_RefId theRef) //================================================================================================= void BRepGraph_LayerRegistry::DispatchRefsModified( - const NCollection_DynamicArray& theModifiedRefs, - const int theModifiedRefKindsMask) noexcept + const NCollection_Array1& theModifiedRefs, + const int theModifiedRefKindsMask) noexcept { - if (!HasRefModificationSubscribers() || theModifiedRefKindsMask == 0) { - return; + std::shared_lock aLock(myMutex); + if (mySubscribedRefKindsMask == 0 || theModifiedRefKindsMask == 0) + { + return; + } } - for (const occ::handle& aLayer : myLayers) + for (uint32_t aSlot = 0;; ++aSlot) { + occ::handle aLayer = layerAt(aSlot); + if (aLayer.IsNull()) + { + return; + } if ((aLayer->SubscribedRefKinds() & theModifiedRefKindsMask) != 0) { - aLayer->OnRefsModified(theModifiedRefs, theModifiedRefKindsMask); + aLayer->OnRefsModified(theModifiedRefs); + } + } +} + +//================================================================================================= + +void BRepGraph_LayerRegistry::detachAllLocked() noexcept +{ + for (const occ::handle& aLayer : myLayers) + { + if (!aLayer.IsNull()) + { + aLayer->detachContext(); } } } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.hxx index 8b02b2a849..fb86a382f9 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegistry.hxx @@ -14,14 +14,18 @@ #ifndef _BRepGraph_LayerRegistry_HeaderFile #define _BRepGraph_LayerRegistry_HeaderFile +#include #include #include - #include -#include +#include +#include #include #include +#include +#include + //! @brief Dense GUID-keyed runtime registry of graph layers. //! //! Stores registered layers in a compact vector for O(1) slot access and a @@ -31,23 +35,17 @@ class BRepGraph_LayerRegistry public: DEFINE_STANDARD_ALLOC - BRepGraph_LayerRegistry() = default; + Standard_EXPORT BRepGraph_LayerRegistry(); BRepGraph_LayerRegistry(const BRepGraph_LayerRegistry&) = delete; BRepGraph_LayerRegistry& operator=(const BRepGraph_LayerRegistry&) = delete; - BRepGraph_LayerRegistry(BRepGraph_LayerRegistry&&) noexcept = default; - BRepGraph_LayerRegistry& operator=(BRepGraph_LayerRegistry&&) noexcept = default; - - //! Bind the owning graph. Propagates to every registered layer. - Standard_EXPORT void SetOwningGraph(BRepGraph* theGraph) noexcept; - - //! Owning graph bound via SetOwningGraph(), or nullptr. - [[nodiscard]] BRepGraph* OwningGraph() const noexcept { return myOwningGraph; } + Standard_EXPORT BRepGraph_LayerRegistry(BRepGraph_LayerRegistry&& theOther) noexcept; + Standard_EXPORT BRepGraph_LayerRegistry& operator=(BRepGraph_LayerRegistry&& theOther) noexcept; //! Register a layer. Replaces an existing layer with the same GUID. - //! @return slot index in the internal dense vector, or -1 for null input. - Standard_EXPORT int RegisterLayer(const occ::handle& theLayer); + //! @return slot index in the internal dense vector. + Standard_EXPORT uint32_t RegisterLayer(const occ::handle& theLayer); //! Remove a layer by GUID. Standard_EXPORT void UnregisterLayer(const Standard_GUID& theGUID); @@ -59,48 +57,103 @@ public: //! Typed convenience lookup by layer GUID. template [[nodiscard]] occ::handle FindLayer() const + { + return Find(); + } + + //! Typed lookup by layer GUID. + template + [[nodiscard]] occ::handle Find() const { return occ::down_cast(FindLayer(T::GetID())); } - //! Return current slot for a GUID, or -1 if not registered. - [[nodiscard]] Standard_EXPORT int FindSlot(const Standard_GUID& theGUID) const; + //! Return an existing layer or create and register a default one. + template + [[nodiscard]] occ::handle Ensure() + { + std::unique_lock aLock(myMutex); + occ::handle aLayer = occ::down_cast(findLayerLocked(T::GetID())); + if (aLayer.IsNull()) + { + aLayer = new T(); + registerLayerLocked(aLayer); + } + return aLayer; + } - //! Return layer by slot index. - [[nodiscard]] Standard_EXPORT const occ::handle& Layer(const int theSlot) const; + //! Return current slot for a GUID. + [[nodiscard]] Standard_EXPORT bool FindSlot(const Standard_GUID& theGUID, + uint32_t& theSlot) const; + + //! Return layer by slot index, or null handle if the slot is out of range. + [[nodiscard]] Standard_EXPORT occ::handle Layer(uint32_t theSlot) const; //! Number of registered layers. - [[nodiscard]] int NbLayers() const { return myLayers.Length(); } + [[nodiscard]] uint32_t NbLayers() const + { + std::shared_lock aLock(myMutex); + return static_cast(myLayers.Size()); + } //! True if any registered layer subscribes to node modification events. - [[nodiscard]] bool HasModificationSubscribers() const { return mySubscribedKindsMask != 0; } + [[nodiscard]] bool HasModificationSubscribers() const + { + std::shared_lock aLock(myMutex); + return mySubscribedKindsMask != 0; + } //! Bitwise OR of all registered layer node subscription masks. - [[nodiscard]] int SubscribedKindsMask() const { return mySubscribedKindsMask; } + [[nodiscard]] int SubscribedKindsMask() const + { + std::shared_lock aLock(myMutex); + return mySubscribedKindsMask; + } //! Dispatch OnNodeRemoved to all registered layers. - Standard_EXPORT void DispatchOnNodeRemoved(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) noexcept; + Standard_EXPORT void DispatchOnNodeRemoved(const BRepGraph_NodeId theNode) noexcept; + + //! Dispatch generic item removal to all registered layers. + Standard_EXPORT void DispatchOnItemRemoved(const BRepGraph_ItemId theItem) noexcept; + + //! Dispatch OnNodeReplaced to all registered layers. + Standard_EXPORT void DispatchOnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept; //! Dispatch OnNodeModified to subscribed layers. Standard_EXPORT void DispatchNodeModified(const BRepGraph_NodeId theNode) noexcept; + //! Dispatch generic item modification through the matching typed subscription path. + Standard_EXPORT void DispatchItemModified(const BRepGraph_ItemId theItem) noexcept; + //! Dispatch OnNodesModified to subscribed layers. Standard_EXPORT void DispatchNodesModified( - const NCollection_DynamicArray& theModifiedNodes, - const int theModifiedKindsMask) noexcept; + const NCollection_Array1& theModifiedNodes, + const int theModifiedKindsMask) noexcept; - //! Dispatch OnCompact to all registered layers. - Standard_EXPORT void DispatchOnCompact( - const NCollection_DataMap& theRemapMap) noexcept; - - // --- Reference dispatch --- + //! Ask every registered source layer to copy itself into the target graph. + //! For Mode::Compact, layers are unregistered first and CopyTo creates fresh instances. + //! @param[in] theTargetGraph target graph to receive layer data + //! @param[in] theItemRemap source -> target item id mapping + //! @param[in] theMode Copy or Compact semantics + Standard_EXPORT void CopyLayersTo( + BRepGraph& theTargetGraph, + const NCollection_FlatDataMap& theItemRemap, + const BRepGraph_CopyRemap::Mode theMode) const; //! True if any registered layer subscribes to reference modification events. - [[nodiscard]] bool HasRefModificationSubscribers() const { return mySubscribedRefKindsMask != 0; } + [[nodiscard]] bool HasRefModificationSubscribers() const + { + std::shared_lock aLock(myMutex); + return mySubscribedRefKindsMask != 0; + } //! Bitwise OR of all registered layer reference subscription masks. - [[nodiscard]] int SubscribedRefKindsMask() const { return mySubscribedRefKindsMask; } + [[nodiscard]] int SubscribedRefKindsMask() const + { + std::shared_lock aLock(myMutex); + return mySubscribedRefKindsMask; + } //! Dispatch OnRefRemoved to all registered layers (unconditional - not filtered). Standard_EXPORT void DispatchOnRefRemoved(const BRepGraph_RefId theRef) noexcept; @@ -110,24 +163,42 @@ public: //! Dispatch OnRefsModified to subscribed layers (deferred/batch mode). Standard_EXPORT void DispatchRefsModified( - const NCollection_DynamicArray& theModifiedRefs, - const int theModifiedRefKindsMask) noexcept; + const NCollection_Array1& theModifiedRefs, + const int theModifiedRefKindsMask) noexcept; - //! Clear all registered layer payloads without unregistering them. + //! Clear all registered layer data without unregistering services. Standard_EXPORT void ClearAll() noexcept; - //! Invalidate all registered layer payloads. + //! Invalidate all registered layer data. Standard_EXPORT void InvalidateAll() noexcept; private: + friend class ::BRepGraph; + friend struct ::BRepGraph_Data; + + //! Attach this registry to graph owner. Propagates context to registered layers. + Standard_EXPORT void Attach(BRepGraph* theGraph) noexcept; + + //! Clear the graph data binding. + Standard_EXPORT void Detach() noexcept; + + [[nodiscard]] Standard_EXPORT occ::handle findLayerLocked( + const Standard_GUID& theGUID) const; + + [[nodiscard]] Standard_EXPORT occ::handle layerAt(uint32_t theSlot) const; + + Standard_EXPORT uint32_t registerLayerLocked(const occ::handle& theLayer); + Standard_EXPORT void recomputeSubscribedKindsMask(); + Standard_EXPORT void detachAllLocked() noexcept; private: - NCollection_DynamicArray> myLayers; + NCollection_LinearVector> myLayers; NCollection_DataMap myGuidToSlot; uint32_t mySubscribedKindsMask = 0; uint32_t mySubscribedRefKindsMask = 0; - BRepGraph* myOwningGraph = nullptr; + BRepGraph* myGraph = nullptr; + mutable std::shared_mutex myMutex; }; #endif // _BRepGraph_LayerRegistry_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegularity.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegularity.cxx deleted file mode 100644 index 27adcf695e..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegularity.cxx +++ /dev/null @@ -1,581 +0,0 @@ -// 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 - -#include - -IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerRegularity, BRepGraph_Layer) - -namespace -{ - -static const TCollection_AsciiString THE_LAYER_NAME("Regularity"); - -void appendUnique( - NCollection_DataMap>& theMap, - const BRepGraph_FaceId theFace, - const BRepGraph_EdgeId theEdge) -{ - if (!theMap.IsBound(theFace)) - { - NCollection_DynamicArray anEdges; - anEdges.Append(theEdge); - theMap.Bind(theFace, anEdges); - return; - } - - NCollection_DynamicArray& anEdges = theMap.ChangeFind(theFace); - for (const BRepGraph_EdgeId& aEdge : anEdges) - { - if (aEdge == theEdge) - { - return; - } - } - anEdges.Append(theEdge); -} - -void removeEdge( - NCollection_DataMap>& theMap, - const BRepGraph_FaceId theFace, - const BRepGraph_EdgeId theEdge) noexcept -{ - if (!theMap.IsBound(theFace)) - { - return; - } - - NCollection_DynamicArray& anEdges = theMap.ChangeFind(theFace); - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(anEdges); anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value() != theEdge) - { - continue; - } - if (anIdx < static_cast(anEdges.Size()) - 1u) - { - anEdges.ChangeValue(static_cast(anIdx)) = anEdges.Value(anEdges.Size() - 1u); - } - anEdges.EraseLast(); - break; - } - - if (anEdges.IsEmpty()) - { - theMap.UnBind(theFace); - } -} - -static BRepGraph_EdgeId remapEdge( - const NCollection_DataMap& theRemapMap, - const BRepGraph_EdgeId theEdge) -{ - const BRepGraph_NodeId* aNewId = theRemapMap.Seek(theEdge); - if (aNewId == nullptr || aNewId->NodeKind != BRepGraph_NodeId::Kind::Edge) - { - return BRepGraph_EdgeId(); - } - return BRepGraph_EdgeId(*aNewId); -} - -static BRepGraph_FaceId remapFace( - const NCollection_DataMap& theRemapMap, - const BRepGraph_FaceId theFace) -{ - const BRepGraph_NodeId* aNewId = theRemapMap.Seek(theFace); - if (aNewId == nullptr || aNewId->NodeKind != BRepGraph_NodeId::Kind::Face) - { - return BRepGraph_FaceId(); - } - return BRepGraph_FaceId(*aNewId); -} - -} // namespace - -//================================================================================================= - -const Standard_GUID& BRepGraph_LayerRegularity::GetID() -{ - static const Standard_GUID THE_LAYER_ID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10002"); - return THE_LAYER_ID; -} - -//================================================================================================= - -const Standard_GUID& BRepGraph_LayerRegularity::ID() const -{ - return GetID(); -} - -//================================================================================================= - -const TCollection_AsciiString& BRepGraph_LayerRegularity::Name() const -{ - return THE_LAYER_NAME; -} - -//================================================================================================= - -const BRepGraph_LayerRegularity::EdgeRegularities* BRepGraph_LayerRegularity::FindEdgeRegularities( - const BRepGraph_EdgeId theEdge) const -{ - return myEdgeRegularities.Seek(theEdge); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::normalizeFacePair(BRepGraph_FaceId& theFace1, - BRepGraph_FaceId& theFace2) const noexcept -{ - if (theFace2 < theFace1) - { - std::swap(theFace1, theFace2); - } -} - -//================================================================================================= - -bool BRepGraph_LayerRegularity::FindContinuity(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2, - GeomAbs_Shape* const theContinuity) const -{ - BRepGraph_FaceId aFace1 = theFace1; - BRepGraph_FaceId aFace2 = theFace2; - normalizeFacePair(aFace1, aFace2); - - const EdgeRegularities* aRegularities = FindEdgeRegularities(theEdge); - if (aRegularities == nullptr) - { - return false; - } - - for (const RegularityEntry& anEntry : aRegularities->Entries) - { - if (anEntry.FaceEntity1 != aFace1 || anEntry.FaceEntity2 != aFace2) - { - continue; - } - if (theContinuity != nullptr) - { - *theContinuity = anEntry.Continuity; - } - return true; - } - return false; -} - -//================================================================================================= - -uint32_t BRepGraph_LayerRegularity::NbRegularities(const BRepGraph_EdgeId theEdge) const -{ - const EdgeRegularities* aRegularities = FindEdgeRegularities(theEdge); - return aRegularities == nullptr ? 0u : static_cast(aRegularities->Entries.Size()); -} - -//================================================================================================= - -GeomAbs_Shape BRepGraph_LayerRegularity::MaxContinuity(const BRepGraph_EdgeId theEdge) const -{ - const EdgeRegularities* aRegularities = FindEdgeRegularities(theEdge); - if (aRegularities == nullptr) - { - return GeomAbs_C0; - } - - GeomAbs_Shape aMaxContinuity = GeomAbs_C0; - for (const RegularityEntry& anEntry : aRegularities->Entries) - { - if (anEntry.Continuity > aMaxContinuity) - { - aMaxContinuity = anEntry.Continuity; - } - } - return aMaxContinuity; -} - -//================================================================================================= - -BRepGraph_LayerRegularity::EdgeRegularities& BRepGraph_LayerRegularity::changeEdgeRegularities( - const BRepGraph_EdgeId theEdge) -{ - if (!myEdgeRegularities.IsBound(theEdge)) - { - myEdgeRegularities.Bind(theEdge, EdgeRegularities()); - } - return myEdgeRegularities.ChangeFind(theEdge); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::bindFaceToEdge(const BRepGraph_FaceId theFace, - const BRepGraph_EdgeId theEdge) -{ - appendUnique(myFaceToEdges, theFace, theEdge); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::unbindFaceFromEdge(const BRepGraph_FaceId theFace, - const BRepGraph_EdgeId theEdge) noexcept -{ - removeEdge(myFaceToEdges, theFace, theEdge); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::SetRegularity(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2, - const GeomAbs_Shape theContinuity) -{ - BRepGraph_FaceId aFace1 = theFace1; - BRepGraph_FaceId aFace2 = theFace2; - normalizeFacePair(aFace1, aFace2); - - EdgeRegularities& aRegularities = changeEdgeRegularities(theEdge); - for (NCollection_DynamicArray::Iterator anIt(aRegularities.Entries); anIt.More(); - anIt.Next()) - { - if (anIt.Value().FaceEntity1 != aFace1 || anIt.Value().FaceEntity2 != aFace2) - { - continue; - } - anIt.ChangeValue().Continuity = theContinuity; - return; - } - - RegularityEntry& anEntry = aRegularities.Entries.Appended(); - anEntry.FaceEntity1 = aFace1; - anEntry.FaceEntity2 = aFace2; - anEntry.Continuity = theContinuity; - bindFaceToEdge(aFace1, theEdge); - bindFaceToEdge(aFace2, theEdge); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::CopyRegularities(const BRepGraph_EdgeId theSourceEdge, - const BRepGraph_EdgeId theTargetEdge) -{ - if (theSourceEdge == theTargetEdge) - { - return; - } - - const EdgeRegularities* aSourceRegularities = myEdgeRegularities.Seek(theSourceEdge); - if (aSourceRegularities == nullptr) - { - return; - } - - const EdgeRegularities aSnapshot = *aSourceRegularities; - for (const RegularityEntry& anEntry : aSnapshot.Entries) - { - SetRegularity(theTargetEdge, anEntry.FaceEntity1, anEntry.FaceEntity2, anEntry.Continuity); - } -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::RemoveRegularities(const BRepGraph_EdgeId theEdge) noexcept -{ - removeEdgeBindings(theEdge); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::removeRegularity(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2) noexcept -{ - if (!myEdgeRegularities.IsBound(theEdge)) - { - return; - } - - BRepGraph_FaceId aFace1 = theFace1; - BRepGraph_FaceId aFace2 = theFace2; - normalizeFacePair(aFace1, aFace2); - - EdgeRegularities& aRegularities = myEdgeRegularities.ChangeFind(theEdge); - bool aFound = false; - uint32_t anIdx = 0; - for (NCollection_DynamicArray::Iterator anIt(aRegularities.Entries); anIt.More(); - anIt.Next(), ++anIdx) - { - if (anIt.Value().FaceEntity1 != aFace1 || anIt.Value().FaceEntity2 != aFace2) - { - continue; - } - if (anIdx < static_cast(aRegularities.Entries.Size()) - 1u) - { - aRegularities.Entries.ChangeValue(static_cast(anIdx)) = - aRegularities.Entries.Value(aRegularities.Entries.Size() - 1u); - } - aRegularities.Entries.EraseLast(); - aFound = true; - break; - } - - if (!aFound) - { - return; - } - - // Only unbind a face if no remaining entry still references it on this edge. - bool aFace1StillReferenced = false; - bool aFace2StillReferenced = false; - for (const RegularityEntry& anEntry : aRegularities.Entries) - { - if (anEntry.FaceEntity1 == aFace1 || anEntry.FaceEntity2 == aFace1) - { - aFace1StillReferenced = true; - } - if (anEntry.FaceEntity1 == aFace2 || anEntry.FaceEntity2 == aFace2) - { - aFace2StillReferenced = true; - } - } - if (!aFace1StillReferenced) - { - unbindFaceFromEdge(aFace1, theEdge); - } - if (!aFace2StillReferenced) - { - unbindFaceFromEdge(aFace2, theEdge); - } - - if (aRegularities.IsEmpty()) - { - myEdgeRegularities.UnBind(theEdge); - } -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::removeEdgeBindings(const BRepGraph_EdgeId theEdge) noexcept -{ - const EdgeRegularities* aRegularities = myEdgeRegularities.Seek(theEdge); - if (aRegularities == nullptr) - { - return; - } - - for (const RegularityEntry& anEntry : aRegularities->Entries) - { - unbindFaceFromEdge(anEntry.FaceEntity1, theEdge); - unbindFaceFromEdge(anEntry.FaceEntity2, theEdge); - } - myEdgeRegularities.UnBind(theEdge); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::invalidateFaceBindings(const BRepGraph_FaceId theFace) noexcept -{ - const NCollection_DynamicArray* anEdges = myFaceToEdges.Seek(theFace); - if (anEdges == nullptr) - { - return; - } - - const NCollection_DynamicArray aBoundEdges = *anEdges; - for (const BRepGraph_EdgeId& aEdgeId : aBoundEdges) - { - const EdgeRegularities* aRegularities = myEdgeRegularities.Seek(aEdgeId); - if (aRegularities == nullptr) - { - continue; - } - const EdgeRegularities aEntries = *aRegularities; - for (const RegularityEntry& anEntry : aEntries.Entries) - { - if (anEntry.FaceEntity1 == theFace || anEntry.FaceEntity2 == theFace) - { - removeRegularity(aEdgeId, anEntry.FaceEntity1, anEntry.FaceEntity2); - } - } - } -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::migrateEdgeBindings(const BRepGraph_EdgeId theOldEdge, - const BRepGraph_EdgeId theNewEdge) noexcept -{ - const EdgeRegularities* aRegularities = myEdgeRegularities.Seek(theOldEdge); - if (aRegularities == nullptr) - { - return; - } - - const EdgeRegularities anOldRegularities = *aRegularities; - removeEdgeBindings(theOldEdge); - for (const RegularityEntry& anEntry : anOldRegularities.Entries) - { - SetRegularity(theNewEdge, anEntry.FaceEntity1, anEntry.FaceEntity2, anEntry.Continuity); - } -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::migrateFaceBindings(const BRepGraph_FaceId theOldFace, - const BRepGraph_FaceId theNewFace) noexcept -{ - const NCollection_DynamicArray* anEdges = myFaceToEdges.Seek(theOldFace); - if (anEdges == nullptr) - { - return; - } - - const NCollection_DynamicArray aBoundEdges = *anEdges; - for (const BRepGraph_EdgeId& aEdgeId : aBoundEdges) - { - const EdgeRegularities* aRegularities = myEdgeRegularities.Seek(aEdgeId); - if (aRegularities == nullptr) - { - continue; - } - const EdgeRegularities aEntries = *aRegularities; - for (const RegularityEntry& anEntry : aEntries.Entries) - { - if (anEntry.FaceEntity1 != theOldFace && anEntry.FaceEntity2 != theOldFace) - { - continue; - } - removeRegularity(aEdgeId, anEntry.FaceEntity1, anEntry.FaceEntity2); - const BRepGraph_FaceId aFace1 = - anEntry.FaceEntity1 == theOldFace ? theNewFace : anEntry.FaceEntity1; - const BRepGraph_FaceId aFace2 = - anEntry.FaceEntity2 == theOldFace ? theNewFace : anEntry.FaceEntity2; - SetRegularity(aEdgeId, aFace1, aFace2, anEntry.Continuity); - } - } -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::OnNodeRemoved(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) noexcept -{ - switch (theNode.NodeKind) - { - case BRepGraph_NodeId::Kind::Edge: - if (theReplacement.NodeKind == BRepGraph_NodeId::Kind::Edge && theReplacement.IsValid()) - { - migrateEdgeBindings(BRepGraph_EdgeId(theNode), BRepGraph_EdgeId(theReplacement)); - } - else - { - removeEdgeBindings(BRepGraph_EdgeId(theNode)); - } - break; - case BRepGraph_NodeId::Kind::Face: - if (theReplacement.NodeKind == BRepGraph_NodeId::Kind::Face && theReplacement.IsValid()) - { - migrateFaceBindings(BRepGraph_FaceId(theNode), BRepGraph_FaceId(theReplacement)); - } - else - { - invalidateFaceBindings(BRepGraph_FaceId(theNode)); - } - break; - default: - break; - } -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::OnCompact( - const NCollection_DataMap& theRemapMap) noexcept -{ - NCollection_DataMap aNewEdgeRegs; - NCollection_DataMap> aNewFaceToEdges; - - for (const auto& [aOldEdge, aOldRegularities] : myEdgeRegularities.Items()) - { - const BRepGraph_EdgeId aNewEdge = remapEdge(theRemapMap, aOldEdge); - if (!aNewEdge.IsValid()) - { - continue; - } - for (const RegularityEntry& anOldEntry : aOldRegularities.Entries) - { - BRepGraph_FaceId aNewFace1 = remapFace(theRemapMap, anOldEntry.FaceEntity1); - BRepGraph_FaceId aNewFace2 = remapFace(theRemapMap, anOldEntry.FaceEntity2); - if (!aNewFace1.IsValid() || !aNewFace2.IsValid()) - { - continue; - } - if (aNewFace2 < aNewFace1) - { - std::swap(aNewFace1, aNewFace2); - } - - if (!aNewEdgeRegs.IsBound(aNewEdge)) - { - aNewEdgeRegs.Bind(aNewEdge, EdgeRegularities()); - } - EdgeRegularities& aRegularities = aNewEdgeRegs.ChangeFind(aNewEdge); - - // Deduplicate: if same face pair already exists for this edge, update continuity. - bool aDuplicate = false; - for (NCollection_DynamicArray::Iterator anIt(aRegularities.Entries); - anIt.More(); - anIt.Next()) - { - if (anIt.Value().FaceEntity1 == aNewFace1 && anIt.Value().FaceEntity2 == aNewFace2) - { - anIt.ChangeValue().Continuity = anOldEntry.Continuity; - aDuplicate = true; - break; - } - } - if (aDuplicate) - { - continue; - } - - RegularityEntry& anEntry = aRegularities.Entries.Appended(); - anEntry.FaceEntity1 = aNewFace1; - anEntry.FaceEntity2 = aNewFace2; - anEntry.Continuity = anOldEntry.Continuity; - - appendUnique(aNewFaceToEdges, aNewFace1, aNewEdge); - appendUnique(aNewFaceToEdges, aNewFace2, aNewEdge); - } - } - - myEdgeRegularities = std::move(aNewEdgeRegs); - myFaceToEdges = std::move(aNewFaceToEdges); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::InvalidateAll() noexcept -{ - Clear(); -} - -//================================================================================================= - -void BRepGraph_LayerRegularity::Clear() noexcept -{ - myEdgeRegularities.Clear(); - myFaceToEdges.Clear(); -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegularity.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegularity.hxx deleted file mode 100644 index 0b420f97f0..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerRegularity.hxx +++ /dev/null @@ -1,120 +0,0 @@ -// 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. - -#ifndef _BRepGraph_LayerRegularity_HeaderFile -#define _BRepGraph_LayerRegularity_HeaderFile - -#include - -#include -#include -#include - -//! @brief Persistent edge-continuity store, keyed by (edge, F1, F2). -//! -//! Each entry holds the geometric continuity (C^k / G^k) across the face pair -//! at a given edge. F1 == F2 represents seam continuity across a closed -//! surface's seam line; F1 != F2 represents inter-face regularity. The schema -//! mirrors classical BRep_Tool::Continuity(edge, F1, F2). -//! -//! ## Lifetime policy -//! The layer is **persistent metadata**: stored values survive arbitrary -//! mutations to the referenced edges and faces. Only the following events -//! discard data: -//! - OnNodeRemoved(edge|face) - the referenced node is gone; entries naming -//! it are dropped (or migrated when a replacement is provided). -//! - OnCompact - ids are remapped; entries pointing to removed nodes drop. -//! - InvalidateAll() / Clear() - explicit caller request. -//! In particular, this layer does NOT subscribe to OnNodeModified: a tolerance -//! bump or NaturalRestriction toggle leaves stored continuity intact. Callers -//! that change the underlying geometry are responsible for refreshing affected -//! entries (typically via SetRegularity, removeRegularity, or InvalidateAll). -class BRepGraph_LayerRegularity : public BRepGraph_Layer -{ -public: - //! Return fixed layer type GUID. - [[nodiscard]] Standard_EXPORT static const Standard_GUID& GetID(); - - //! Return this layer type GUID. - [[nodiscard]] Standard_EXPORT const Standard_GUID& ID() const override; - - struct RegularityEntry - { - BRepGraph_FaceId FaceEntity1; - BRepGraph_FaceId FaceEntity2; - GeomAbs_Shape Continuity = GeomAbs_C0; - }; - - struct EdgeRegularities - { - NCollection_DynamicArray Entries; - - [[nodiscard]] bool IsEmpty() const { return Entries.IsEmpty(); } - }; - - Standard_EXPORT const EdgeRegularities* FindEdgeRegularities( - const BRepGraph_EdgeId theEdge) const; - - Standard_EXPORT bool FindContinuity(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2, - GeomAbs_Shape* const theContinuity = nullptr) const; - - Standard_EXPORT uint32_t NbRegularities(const BRepGraph_EdgeId theEdge) const; - Standard_EXPORT GeomAbs_Shape MaxContinuity(const BRepGraph_EdgeId theEdge) const; - - [[nodiscard]] bool HasBindings() const { return myEdgeRegularities.Extent() != 0; } - - Standard_EXPORT void SetRegularity(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2, - const GeomAbs_Shape theContinuity); - - //! Copy all regularity entries from one edge to another. - Standard_EXPORT void CopyRegularities(const BRepGraph_EdgeId theSourceEdge, - const BRepGraph_EdgeId theTargetEdge); - - //! Remove all regularity entries bound to the edge. - Standard_EXPORT void RemoveRegularities(const BRepGraph_EdgeId theEdge) noexcept; - - Standard_EXPORT const TCollection_AsciiString& Name() const override; - Standard_EXPORT void OnNodeRemoved(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) noexcept override; - Standard_EXPORT void OnCompact( - const NCollection_DataMap& theRemapMap) noexcept override; - Standard_EXPORT void InvalidateAll() noexcept override; - Standard_EXPORT void Clear() noexcept override; - - DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerRegularity, BRepGraph_Layer) - -private: - void normalizeFacePair(BRepGraph_FaceId& theFace1, BRepGraph_FaceId& theFace2) const noexcept; - EdgeRegularities& changeEdgeRegularities(const BRepGraph_EdgeId theEdge); - void bindFaceToEdge(const BRepGraph_FaceId theFace, const BRepGraph_EdgeId theEdge); - void unbindFaceFromEdge(const BRepGraph_FaceId theFace, const BRepGraph_EdgeId theEdge) noexcept; - void removeRegularity(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2) noexcept; - void removeEdgeBindings(const BRepGraph_EdgeId theEdge) noexcept; - void invalidateFaceBindings(const BRepGraph_FaceId theFace) noexcept; - void migrateEdgeBindings(const BRepGraph_EdgeId theOldEdge, - const BRepGraph_EdgeId theNewEdge) noexcept; - void migrateFaceBindings(const BRepGraph_FaceId theOldFace, - const BRepGraph_FaceId theNewFace) noexcept; - -private: - NCollection_DataMap myEdgeRegularities; - NCollection_DataMap> myFaceToEdges; -}; - -#endif // _BRepGraph_LayerRegularity_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerTopoSupplement.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerTopoSupplement.cxx new file mode 100644 index 0000000000..c8232c6698 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerTopoSupplement.cxx @@ -0,0 +1,386 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerTopoSupplement, BRepGraph_Layer) + +namespace +{ +const TCollection_AsciiString THE_SUPPLEMENT_LAYER_NAME("LayerTopoSupplement"); + +bool isSupportedOwnerKind(const BRepGraph_NodeId::Kind theKind) +{ + switch (theKind) + { + case BRepGraph_NodeId::Kind::Vertex: + case BRepGraph_NodeId::Kind::Edge: + case BRepGraph_NodeId::Kind::Face: + case BRepGraph_NodeId::Kind::Shell: + case BRepGraph_NodeId::Kind::Solid: + case BRepGraph_NodeId::Kind::CompSolid: + case BRepGraph_NodeId::Kind::Compound: + return true; + case BRepGraph_NodeId::Kind::Wire: + case BRepGraph_NodeId::Kind::CoEdge: + case BRepGraph_NodeId::Kind::Product: + case BRepGraph_NodeId::Kind::Occurrence: + return false; + } + return false; +} + +bool isAttachmentKindCompatible(const BRepGraph_NodeId::Kind theOwnerKind, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind) +{ + switch (theKind) + { + case BRepGraph_LayerTopoSupplement::AttachmentKind::GenericSupplementShape: + return isSupportedOwnerKind(theOwnerKind); + case BRepGraph_LayerTopoSupplement::AttachmentKind::VertexSupplementShape: + return theOwnerKind == BRepGraph_NodeId::Kind::Vertex; + case BRepGraph_LayerTopoSupplement::AttachmentKind::EdgeInternalVertex: + return theOwnerKind == BRepGraph_NodeId::Kind::Edge; + case BRepGraph_LayerTopoSupplement::AttachmentKind::FaceDirectVertex: + return theOwnerKind == BRepGraph_NodeId::Kind::Face; + case BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape: + return theOwnerKind == BRepGraph_NodeId::Kind::Solid; + case BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape: + return theOwnerKind == BRepGraph_NodeId::Kind::Shell; + case BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape: + return theOwnerKind == BRepGraph_NodeId::Kind::CompSolid; + case BRepGraph_LayerTopoSupplement::AttachmentKind::CompoundAuxShape: + return theOwnerKind == BRepGraph_NodeId::Kind::Compound; + } + return false; +} +} // namespace + +//================================================================================================= + +const Standard_GUID& BRepGraph_LayerTopoSupplement::GetID() +{ + static const Standard_GUID THE_GUID("bd76a339-9958-4d92-a3de-1296bd67a992"); + return THE_GUID; +} + +//================================================================================================= + +const Standard_GUID& BRepGraph_LayerTopoSupplement::ID() const +{ + return GetID(); +} + +//================================================================================================= + +const TCollection_AsciiString& BRepGraph_LayerTopoSupplement::Name() const +{ + return THE_SUPPLEMENT_LAYER_NAME; +} + +//================================================================================================= + +const BRepGraph_LayerTopoSupplement::Entry* BRepGraph_LayerTopoSupplement::FindByUid( + const uint64_t theUid) const +{ + return myEntries.Seek(theUid); +} + +//================================================================================================= + +const NCollection_LinearVector& BRepGraph_LayerTopoSupplement::AttachedTo( + const BRepGraph_NodeId theOwner) const +{ + const NCollection_LinearVector* aFound = myOwnerToUids.Seek(theOwner); + return aFound != nullptr ? *aFound : myEmptyUids; +} + +//================================================================================================= + +uint64_t BRepGraph_LayerTopoSupplement::AddAttachment(const BRepGraph_NodeId theOwner, + const AttachmentKind theKind, + const TopoDS_Shape& theShape) +{ + const uint64_t aUid = myNextUid; + if (!AddAttachmentWithUid(theOwner, aUid, theKind, theShape)) + { + return 0; + } + return aUid; +} + +//================================================================================================= + +bool BRepGraph_LayerTopoSupplement::AddAttachmentWithUid(const BRepGraph_NodeId theOwner, + const uint64_t theUid, + const AttachmentKind theKind, + const TopoDS_Shape& theShape) +{ + if (!theOwner.IsValid() || !isSupportedOwnerKind(theOwner.NodeKind) || theShape.IsNull() + || !isAttachmentKindCompatible(theOwner.NodeKind, theKind) || theUid == 0 + || myEntries.IsBound(theUid)) + { + return false; + } + + Entry anEntry; + anEntry.BaseOwner = theOwner; + anEntry.LocalUid = theUid; + anEntry.Kind = theKind; + anEntry.Shape = theShape; + myEntries.Bind(anEntry.LocalUid, anEntry); + + NCollection_LinearVector* aOwnerEntries = myOwnerToUids.ChangeSeek(theOwner); + if (aOwnerEntries == nullptr) + { + NCollection_LinearVector anIds; + anIds.Append(anEntry.LocalUid); + myOwnerToUids.Bind(theOwner, anIds); + } + else + { + aOwnerEntries->Append(anEntry.LocalUid); + } + + if (theUid >= myNextUid) + { + myNextUid = theUid + 1; + } + if (BRepGraph* aGraph = AttachedGraph()) + { + aGraph->Shapes().ClearCached(theOwner); + } + touch(); + return true; +} + +//================================================================================================= + +bool BRepGraph_LayerTopoSupplement::RemoveAttachment(const uint64_t theUid) +{ + Entry* anEntry = myEntries.ChangeSeek(theUid); + if (anEntry == nullptr) + { + return false; + } + + NCollection_LinearVector* aOwnerEntries = myOwnerToUids.ChangeSeek(anEntry->BaseOwner); + if (aOwnerEntries != nullptr) + { + for (size_t anIdx = 0; anIdx < aOwnerEntries->Size(); ++anIdx) + { + if (aOwnerEntries->Value(anIdx) != theUid) + { + continue; + } + + for (size_t aMoveIdx = anIdx + 1; aMoveIdx < aOwnerEntries->Size(); ++aMoveIdx) + { + aOwnerEntries->SetValue(aMoveIdx - 1, aOwnerEntries->Value(aMoveIdx)); + } + aOwnerEntries->EraseLast(); + break; + } + if (aOwnerEntries->IsEmpty()) + { + myOwnerToUids.UnBind(anEntry->BaseOwner); + } + } + + if (BRepGraph* aGraph = AttachedGraph()) + { + aGraph->Shapes().ClearCached(anEntry->BaseOwner); + } + myEntries.UnBind(theUid); + touch(); + return true; +} + +//================================================================================================= + +void BRepGraph_LayerTopoSupplement::Validate() const +{ + for (NCollection_DataMap::Iterator anIt(myEntries); anIt.More(); anIt.Next()) + { + const Entry& anEntry = anIt.Value(); + if (anEntry.LocalUid == 0 || !anEntry.BaseOwner.IsValid() + || !isSupportedOwnerKind(anEntry.BaseOwner.NodeKind) || anEntry.Shape.IsNull() + || !isAttachmentKindCompatible(anEntry.BaseOwner.NodeKind, anEntry.Kind)) + { + throw Standard_ProgramError("BRepGraph_LayerTopoSupplement::Validate() - invalid entry"); + } + + const NCollection_LinearVector* aUids = myOwnerToUids.Seek(anEntry.BaseOwner); + if (aUids == nullptr) + { + throw Standard_ProgramError( + "BRepGraph_LayerTopoSupplement::Validate() - missing owner index"); + } + + bool isFound = false; + for (const uint64_t aUid : *aUids) + { + if (aUid == anEntry.LocalUid) + { + isFound = true; + break; + } + } + if (!isFound) + { + throw Standard_ProgramError( + "BRepGraph_LayerTopoSupplement::Validate() - missing owner linkage"); + } + } +} + +//================================================================================================= + +void BRepGraph_LayerTopoSupplement::OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept +{ + removeOwner(theNode); +} + +//================================================================================================= + +void BRepGraph_LayerTopoSupplement::OnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept +{ + if (!theOldNode.IsValid()) + { + return; + } + if (!theNewNode.IsValid() || theOldNode.NodeKind != theNewNode.NodeKind) + { + removeOwner(theOldNode); + return; + } + + NCollection_LinearVector* anOldList = myOwnerToUids.ChangeSeek(theOldNode); + if (anOldList == nullptr) + { + return; + } + + NCollection_LinearVector* aNewList = myOwnerToUids.ChangeSeek(theNewNode); + if (aNewList == nullptr) + { + NCollection_LinearVector aMoved = *anOldList; + myOwnerToUids.Bind(theNewNode, aMoved); + aNewList = myOwnerToUids.ChangeSeek(theNewNode); + } + else + { + for (const uint64_t aUid : *anOldList) + { + aNewList->Append(aUid); + } + } + + for (const uint64_t aUid : *anOldList) + { + Entry* anEntry = myEntries.ChangeSeek(aUid); + if (anEntry != nullptr) + { + anEntry->BaseOwner = theNewNode; + } + } + myOwnerToUids.UnBind(theOldNode); + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerTopoSupplement::CopyTo(const BRepGraph_CopyRemap& theCopy) const +{ + if (myEntries.IsEmpty()) + { + return; + } + + occ::handle aTarget = + theCopy.TargetGraph().LayerRegistry().Ensure(); + + NCollection_LinearVector aSourceEntries; + aSourceEntries.Reserve(myEntries.Extent()); + for (NCollection_DataMap::Iterator anIt(myEntries); anIt.More(); anIt.Next()) + { + aSourceEntries.Append(anIt.Value()); + } + for (const Entry& anEntry : aSourceEntries) + { + const BRepGraph_ItemId* aTargetItem = theCopy.TargetItem(BRepGraph_ItemId(anEntry.BaseOwner)); + if (aTargetItem == nullptr || !aTargetItem->IsNode()) + { + continue; + } + + const BRepGraph_NodeId aTargetOwner = aTargetItem->NodeId(); + if (!aTargetOwner.IsValid()) + { + continue; + } + const bool hasUidCollision = aTarget->myEntries.IsBound(anEntry.LocalUid); + const bool wasAdded = hasUidCollision + ? aTarget->AddAttachment(aTargetOwner, anEntry.Kind, anEntry.Shape) != 0 + : aTarget->AddAttachmentWithUid(aTargetOwner, + anEntry.LocalUid, + anEntry.Kind, + anEntry.Shape); + Standard_ASSERT_RAISE(wasAdded, + "BRepGraph_LayerTopoSupplement::CopyTo: failed to copy attachment"); + } +} + +//================================================================================================= + +void BRepGraph_LayerTopoSupplement::InvalidateAll() noexcept +{ + Clear(); +} + +//================================================================================================= + +void BRepGraph_LayerTopoSupplement::Clear() noexcept +{ + myEntries.Clear(); + myOwnerToUids.Clear(); + myNextUid = 1; + touch(); +} + +//================================================================================================= + +void BRepGraph_LayerTopoSupplement::removeOwner(const BRepGraph_NodeId theOwner) noexcept +{ + NCollection_LinearVector* aOwnerEntries = myOwnerToUids.ChangeSeek(theOwner); + if (aOwnerEntries == nullptr) + { + return; + } + + for (const uint64_t aUid : *aOwnerEntries) + { + myEntries.UnBind(aUid); + } + myOwnerToUids.UnBind(theOwner); + touch(); +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerTopoSupplement.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerTopoSupplement.hxx new file mode 100644 index 0000000000..df44a75280 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_LayerTopoSupplement.hxx @@ -0,0 +1,137 @@ +// 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. + +#ifndef _BRepGraph_LayerTopoSupplement_HeaderFile +#define _BRepGraph_LayerTopoSupplement_HeaderFile + +#include +#include +#include +#include + +//! @brief Runtime-only storage for supplemental TopoDS topology fragments. +//! +//! This layer stores non-core topology extracted from a source shape and +//! attached to supported core graph owners. These attachments are not +//! serialized and are intended only to preserve live +//! `TopoDS -> Graph -> TopoDS` behavior. +class BRepGraph_LayerTopoSupplement : public BRepGraph_Layer +{ +public: + //! @brief Semantic role of one supplemental attachment. + enum class AttachmentKind + { + VertexSupplementShape, + EdgeInternalVertex, + FaceDirectVertex, + SolidAuxShape, + ShellAuxShape, + CompSolidAuxShape, + CompoundAuxShape, + GenericSupplementShape + }; + + //! @brief Stored runtime attachment record. + struct Entry + { + BRepGraph_NodeId BaseOwner; + uint64_t LocalUid = 0; + AttachmentKind Kind = AttachmentKind::GenericSupplementShape; + TopoDS_Shape Shape; + }; + + //! @brief Return the fixed layer type GUID. + [[nodiscard]] Standard_EXPORT static const Standard_GUID& GetID(); + + //! @brief Return the runtime type GUID for this layer instance. + [[nodiscard]] Standard_EXPORT const Standard_GUID& ID() const override; + + //! @brief Return a short stable layer name for diagnostics and registry lookup. + [[nodiscard]] Standard_EXPORT const TCollection_AsciiString& Name() const override; + + //! @brief Find one attachment entry by its layer-local uid. + //! @param[in] theUid layer-local attachment uid + //! @return pointer to the entry, or `nullptr` when not found + [[nodiscard]] Standard_EXPORT const Entry* FindByUid(uint64_t theUid) const; + + //! @brief Return all attachment uids currently owned by one core node. + //! @param[in] theOwner core topology owner node + //! @return owner-local insertion-ordered list of attachment uids + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& AttachedTo( + BRepGraph_NodeId theOwner) const; + + //! @brief Add one supplemental shape attachment to a supported core owner node. + //! Supported owner kinds are vertex, edge, face, shell, solid, compsolid, and compound. + //! @param[in] theOwner active core topology owner + //! @param[in] theKind semantic attachment kind + //! @param[in] theShape attached supplemental shape + //! @return non-zero layer-local uid on success, `0` on rejection + Standard_EXPORT uint64_t AddAttachment(BRepGraph_NodeId theOwner, + AttachmentKind theKind, + const TopoDS_Shape& theShape); + + //! @brief Add one supplemental shape attachment with an explicitly preserved uid. + //! Supported owner kinds are vertex, edge, face, shell, solid, compsolid, and compound. + //! @param[in] theOwner active core topology owner + //! @param[in] theUid layer-local attachment uid to preserve + //! @param[in] theKind semantic attachment kind + //! @param[in] theShape attached supplemental shape + //! @return `true` on success, `false` when the uid or input is rejected + Standard_EXPORT bool AddAttachmentWithUid(BRepGraph_NodeId theOwner, + uint64_t theUid, + AttachmentKind theKind, + const TopoDS_Shape& theShape); + + //! @brief Remove one supplemental attachment by uid. + //! @param[in] theUid layer-local attachment uid + //! @return `true` when the attachment existed and was removed + Standard_EXPORT bool RemoveAttachment(uint64_t theUid); + + //! @brief Validate internal owner/uid bookkeeping invariants. + //! @throws Standard_ProgramError on inconsistent internal state + Standard_EXPORT void Validate() const; + + //! @brief Drop all attachments owned by a removed node. + //! @param[in] theNode removed core node + Standard_EXPORT void OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept override; + + //! @brief Migrate attachments from one owner node to another compatible node. + //! @param[in] theOldNode previous owner node + //! @param[in] theNewNode replacement owner node + Standard_EXPORT void OnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept override; + + //! @brief Copy remapped attachments to the target graph. + Standard_EXPORT void CopyTo(const BRepGraph_CopyRemap& theCopy) const override; + + //! @brief Invalidate all cached state in the layer. + Standard_EXPORT void InvalidateAll() noexcept override; + + //! @brief Remove every stored supplemental attachment. + Standard_EXPORT void Clear() noexcept override; + + DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerTopoSupplement, BRepGraph_Layer) + +private: + //! @brief Remove all attachments belonging to one owner node. + //! @param[in] theOwner owner node to erase + void removeOwner(BRepGraph_NodeId theOwner) noexcept; + +private: + NCollection_DataMap myEntries; + NCollection_DataMap> myOwnerToUids; + NCollection_LinearVector myEmptyUids; + uint64_t myNextUid = 1; +}; + +#endif // _BRepGraph_LayerTopoSupplement_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshCache.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshCache.cxx deleted file mode 100644 index eb6be50eb0..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshCache.cxx +++ /dev/null @@ -1,275 +0,0 @@ -// 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 - -//================================================================================================= - -template -void BRepGraph_MeshCacheStorage::ensureSize(NCollection_DynamicArray& theVec, - const size_t theIndex) -{ - while (theVec.Size() <= theIndex) - { - theVec.Append(T()); - } -} - -//================================================================================================= - -bool BRepGraph_MeshCacheStorage::HasFaceMesh(const BRepGraph_FaceId theFace) const -{ - if (!theFace.IsValidIn(myFaceMeshes)) - { - return false; - } - return myFaceMeshes.Value(static_cast(theFace.Index)).IsPresent(); -} - -//================================================================================================= - -const BRepGraph_MeshCache::FaceMeshEntry* BRepGraph_MeshCacheStorage::FindFaceMesh( - const BRepGraph_FaceId theFace) const -{ - if (!theFace.IsValidIn(myFaceMeshes)) - { - return nullptr; - } - const BRepGraph_MeshCache::FaceMeshEntry& anEntry = - myFaceMeshes.Value(static_cast(theFace.Index)); - if (!anEntry.IsPresent()) - { - return nullptr; - } - return &anEntry; -} - -//================================================================================================= - -BRepGraph_MeshCache::FaceMeshEntry& BRepGraph_MeshCacheStorage::ChangeFaceMesh( - const BRepGraph_FaceId theFace) -{ - ensureSize(myFaceMeshes, static_cast(theFace.Index)); - return myFaceMeshes.ChangeValue(static_cast(theFace.Index)); -} - -//================================================================================================= - -void BRepGraph_MeshCacheStorage::ClearFaceMesh(const BRepGraph_FaceId theFace) -{ - if (theFace.IsValidIn(myFaceMeshes)) - { - myFaceMeshes.ChangeValue(static_cast(theFace.Index)).Reset(); - } -} - -//================================================================================================= - -bool BRepGraph_MeshCacheStorage::HasCoEdgeMesh(const BRepGraph_CoEdgeId theCoEdge) const -{ - if (!theCoEdge.IsValidIn(myCoEdgeMeshes)) - { - return false; - } - return myCoEdgeMeshes.Value(static_cast(theCoEdge.Index)).IsPresent(); -} - -//================================================================================================= - -const BRepGraph_MeshCache::CoEdgeMeshEntry* BRepGraph_MeshCacheStorage::FindCoEdgeMesh( - const BRepGraph_CoEdgeId theCoEdge) const -{ - if (!theCoEdge.IsValidIn(myCoEdgeMeshes)) - { - return nullptr; - } - const BRepGraph_MeshCache::CoEdgeMeshEntry& anEntry = - myCoEdgeMeshes.Value(static_cast(theCoEdge.Index)); - if (!anEntry.IsPresent()) - { - return nullptr; - } - return &anEntry; -} - -//================================================================================================= - -BRepGraph_MeshCache::CoEdgeMeshEntry& BRepGraph_MeshCacheStorage::ChangeCoEdgeMesh( - const BRepGraph_CoEdgeId theCoEdge) -{ - ensureSize(myCoEdgeMeshes, static_cast(theCoEdge.Index)); - return myCoEdgeMeshes.ChangeValue(static_cast(theCoEdge.Index)); -} - -//================================================================================================= - -void BRepGraph_MeshCacheStorage::ClearCoEdgeMesh(const BRepGraph_CoEdgeId theCoEdge) -{ - if (theCoEdge.IsValidIn(myCoEdgeMeshes)) - { - myCoEdgeMeshes.ChangeValue(static_cast(theCoEdge.Index)).Reset(); - } -} - -//================================================================================================= - -bool BRepGraph_MeshCacheStorage::HasEdgeMesh(const BRepGraph_EdgeId theEdge) const -{ - if (!theEdge.IsValidIn(myEdgeMeshes)) - { - return false; - } - return myEdgeMeshes.Value(static_cast(theEdge.Index)).IsPresent(); -} - -//================================================================================================= - -const BRepGraph_MeshCache::EdgeMeshEntry* BRepGraph_MeshCacheStorage::FindEdgeMesh( - const BRepGraph_EdgeId theEdge) const -{ - if (!theEdge.IsValidIn(myEdgeMeshes)) - { - return nullptr; - } - const BRepGraph_MeshCache::EdgeMeshEntry& anEntry = - myEdgeMeshes.Value(static_cast(theEdge.Index)); - if (!anEntry.IsPresent()) - { - return nullptr; - } - return &anEntry; -} - -//================================================================================================= - -BRepGraph_MeshCache::EdgeMeshEntry& BRepGraph_MeshCacheStorage::ChangeEdgeMesh( - const BRepGraph_EdgeId theEdge) -{ - ensureSize(myEdgeMeshes, static_cast(theEdge.Index)); - return myEdgeMeshes.ChangeValue(static_cast(theEdge.Index)); -} - -//================================================================================================= - -void BRepGraph_MeshCacheStorage::ClearEdgeMesh(const BRepGraph_EdgeId theEdge) -{ - if (theEdge.IsValidIn(myEdgeMeshes)) - { - myEdgeMeshes.ChangeValue(static_cast(theEdge.Index)).Reset(); - } -} - -//================================================================================================= - -void BRepGraph_MeshCacheStorage::Clear() -{ - myFaceMeshes.Clear(); - myCoEdgeMeshes.Clear(); - myEdgeMeshes.Clear(); -} - -//================================================================================================= - -void BRepGraph_MeshCacheStorage::OnCompact( - const NCollection_DataMap& theNodeRemapMap) -{ - // Remap face mesh entries. - { - NCollection_DynamicArray aNewFaces; - for (NCollection_DataMap::Iterator anIter(theNodeRemapMap); - anIter.More(); - anIter.Next()) - { - const BRepGraph_NodeId& anOldId = anIter.Key(); - const BRepGraph_NodeId& aNewId = anIter.Value(); - if (anOldId.NodeKind != BRepGraph_NodeId::Kind::Face) - { - continue; - } - const BRepGraph_FaceId anOldFaceId(anOldId); - if (!anOldFaceId.IsValidIn(myFaceMeshes)) - { - continue; - } - const BRepGraph_MeshCache::FaceMeshEntry& anOldEntry = - myFaceMeshes.Value(static_cast(anOldFaceId.Index)); - if (!anOldEntry.IsPresent()) - { - continue; - } - ensureSize(aNewFaces, static_cast(aNewId.Index)); - aNewFaces.ChangeValue(static_cast(aNewId.Index)) = anOldEntry; - } - myFaceMeshes = std::move(aNewFaces); - } - - // Remap coedge mesh entries. - { - NCollection_DynamicArray aNewCoEdges; - for (NCollection_DataMap::Iterator anIter(theNodeRemapMap); - anIter.More(); - anIter.Next()) - { - const BRepGraph_NodeId& anOldId = anIter.Key(); - const BRepGraph_NodeId& aNewId = anIter.Value(); - if (anOldId.NodeKind != BRepGraph_NodeId::Kind::CoEdge) - { - continue; - } - const BRepGraph_CoEdgeId anOldCoEdgeId(anOldId); - if (!anOldCoEdgeId.IsValidIn(myCoEdgeMeshes)) - { - continue; - } - const BRepGraph_MeshCache::CoEdgeMeshEntry& anOldEntry = - myCoEdgeMeshes.Value(static_cast(anOldCoEdgeId.Index)); - if (!anOldEntry.IsPresent()) - { - continue; - } - ensureSize(aNewCoEdges, static_cast(aNewId.Index)); - aNewCoEdges.ChangeValue(static_cast(aNewId.Index)) = anOldEntry; - } - myCoEdgeMeshes = std::move(aNewCoEdges); - } - - // Remap edge mesh entries. - { - NCollection_DynamicArray aNewEdges; - for (NCollection_DataMap::Iterator anIter(theNodeRemapMap); - anIter.More(); - anIter.Next()) - { - const BRepGraph_NodeId& anOldId = anIter.Key(); - const BRepGraph_NodeId& aNewId = anIter.Value(); - if (anOldId.NodeKind != BRepGraph_NodeId::Kind::Edge) - { - continue; - } - const BRepGraph_EdgeId anOldEdgeId(anOldId); - if (!anOldEdgeId.IsValidIn(myEdgeMeshes)) - { - continue; - } - const BRepGraph_MeshCache::EdgeMeshEntry& anOldEntry = - myEdgeMeshes.Value(static_cast(anOldEdgeId.Index)); - if (!anOldEntry.IsPresent()) - { - continue; - } - ensureSize(aNewEdges, static_cast(aNewId.Index)); - aNewEdges.ChangeValue(static_cast(aNewId.Index)) = anOldEntry; - } - myEdgeMeshes = std::move(aNewEdges); - } -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshCache.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshCache.hxx deleted file mode 100644 index 95f8c21316..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshCache.hxx +++ /dev/null @@ -1,188 +0,0 @@ -// 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. - -#ifndef _BRepGraph_MeshCache_HeaderFile -#define _BRepGraph_MeshCache_HeaderFile - -#include -#include - -#include -#include - -//! @brief Cached mesh data storage for BRepGraph. -//! -//! Stores mesh RepId references (triangulations for faces, polygons for edges -//! and coedges) separately from topology definitions. This cache holds -//! algorithm-derived mesh data written by BRepGraphMesh, as opposed to -//! persistent mesh data stored in definition structs (imported from STEP, etc.). -//! -//! Priority rule: cached mesh takes precedence over persistent mesh in -//! definitions. Persistent mesh is the fallback when no fresh cache exists. -//! -//! Freshness is validated by comparing StoredOwnGen against the entity's -//! current OwnGen. A mismatch means the geometry changed since meshing, -//! so the cached mesh is stale. -//! -//! Writing to the cache does NOT trigger markModified() or mutation tracking. -//! -//! ### Invalidation contract -//! The cache relies on the following invariants upheld by BRepGraph mutations: -//! 1. Any `Editor().Faces().Mut(FaceId)` guard bumps `FaceDef.OwnGen` on scope -//! exit, invalidating cached face mesh entries. -//! 2. `markRepModified(SurfaceRepId | TriangulationRepId)` iterates every Face -//! referencing the rep and calls `markModified(FaceId)`, so geometry edits -//! through `Editor().Reps().MutSurface/MutTriangulation()` also invalidate -//! cached face meshes. -//! 3. `markRepModified(TriangulationRepId)` additionally scans the cache itself -//! (not just persistent `FaceDef.TriangulationRepId`) so that cached-only -//! triangulations are bumped along with their owning Face's `OwnGen`. -//! Edge/CoEdge caches follow the analogous pattern for `EdgeDef`/`CoEdgeDef` -//! and the corresponding `Polygon3D`/`Polygon2D`/`PolygonOnTri` reps. -namespace BRepGraph_MeshCache -{ - -//! Cached mesh entry for a face: triangulation rep references. -struct FaceMeshEntry -{ - NCollection_DynamicArray TriangulationRepIds; - int ActiveTriangulationIndex = -1; - uint32_t StoredOwnGen = 0; //!< OwnGen of FaceDef at write time - - //! True if this entry contains mesh data. - [[nodiscard]] bool IsPresent() const { return !TriangulationRepIds.IsEmpty(); } - - //! Convenience: active triangulation rep id, or invalid. - [[nodiscard]] BRepGraph_TriangulationRepId ActiveTriangulationRepId() const - { - if (ActiveTriangulationIndex >= 0 && ActiveTriangulationIndex < TriangulationRepIds.Length()) - return TriangulationRepIds.Value(ActiveTriangulationIndex); - return BRepGraph_TriangulationRepId(); - } - - //! Reset all fields to default (absent) state. - void Reset() - { - TriangulationRepIds.Clear(); - ActiveTriangulationIndex = -1; - StoredOwnGen = 0; - } -}; - -//! Cached mesh entry for a coedge: polygon-on-triangulation and polygon-2D rep references. -struct CoEdgeMeshEntry -{ - BRepGraph_Polygon2DRepId Polygon2DRepId; - NCollection_DynamicArray PolygonOnTriRepIds; - uint32_t StoredOwnGen = 0; //!< OwnGen of CoEdgeDef at write time - - //! True if this entry contains mesh data. - [[nodiscard]] bool IsPresent() const - { - return Polygon2DRepId.IsValid() || !PolygonOnTriRepIds.IsEmpty(); - } - - //! Reset all fields to default (absent) state. - void Reset() - { - Polygon2DRepId = BRepGraph_Polygon2DRepId(); - PolygonOnTriRepIds.Clear(); - StoredOwnGen = 0; - } -}; - -//! Cached mesh entry for an edge: polygon-3D rep reference. -struct EdgeMeshEntry -{ - BRepGraph_Polygon3DRepId Polygon3DRepId; - uint32_t StoredOwnGen = 0; //!< OwnGen of EdgeDef at write time - - //! True if this entry contains mesh data. - [[nodiscard]] bool IsPresent() const { return Polygon3DRepId.IsValid(); } - - //! Reset all fields to default (absent) state. - void Reset() - { - Polygon3DRepId = BRepGraph_Polygon3DRepId(); - StoredOwnGen = 0; - } -}; - -} // namespace BRepGraph_MeshCache - -//! @brief Storage backend for cached mesh data. -//! -//! Dense vectors indexed by per-kind entity index (same pattern as DefStore). -//! Entries with StoredOwnGen == 0 are absent (no cached mesh data). -//! Thread safety: parallel writes to different indices are safe (no contention). -class BRepGraph_MeshCacheStorage -{ -public: - //! Check if a face has a cached mesh entry (StoredOwnGen != 0). - [[nodiscard]] bool HasFaceMesh(const BRepGraph_FaceId theFace) const; - - //! Find face mesh entry, or nullptr if absent. - [[nodiscard]] const BRepGraph_MeshCache::FaceMeshEntry* FindFaceMesh( - const BRepGraph_FaceId theFace) const; - - //! Get or create a face mesh entry. Creates with default values if absent. - [[nodiscard]] BRepGraph_MeshCache::FaceMeshEntry& ChangeFaceMesh(const BRepGraph_FaceId theFace); - - //! Clear the face mesh entry (reset to absent). - void ClearFaceMesh(const BRepGraph_FaceId theFace); - - //! Check if a coedge has a cached mesh entry. - [[nodiscard]] bool HasCoEdgeMesh(const BRepGraph_CoEdgeId theCoEdge) const; - - //! Find coedge mesh entry, or nullptr if absent. - [[nodiscard]] const BRepGraph_MeshCache::CoEdgeMeshEntry* FindCoEdgeMesh( - const BRepGraph_CoEdgeId theCoEdge) const; - - //! Get or create a coedge mesh entry. - [[nodiscard]] BRepGraph_MeshCache::CoEdgeMeshEntry& ChangeCoEdgeMesh( - const BRepGraph_CoEdgeId theCoEdge); - - //! Clear the coedge mesh entry. - void ClearCoEdgeMesh(const BRepGraph_CoEdgeId theCoEdge); - - //! Check if an edge has a cached mesh entry. - [[nodiscard]] bool HasEdgeMesh(const BRepGraph_EdgeId theEdge) const; - - //! Find edge mesh entry, or nullptr if absent. - [[nodiscard]] const BRepGraph_MeshCache::EdgeMeshEntry* FindEdgeMesh( - const BRepGraph_EdgeId theEdge) const; - - //! Get or create an edge mesh entry. - [[nodiscard]] BRepGraph_MeshCache::EdgeMeshEntry& ChangeEdgeMesh(const BRepGraph_EdgeId theEdge); - - //! Clear the edge mesh entry. - void ClearEdgeMesh(const BRepGraph_EdgeId theEdge); - - //! Clear all cached mesh data. - void Clear(); - - //! Remap cache entries after compaction. - //! @param[in] theNodeRemapMap old NodeId -> new NodeId mapping - void OnCompact(const NCollection_DataMap& theNodeRemapMap); - -private: - //! Ensure vector has at least theIndex+1 elements. - template - static void ensureSize(NCollection_DynamicArray& theVec, const size_t theIndex); - - NCollection_DynamicArray myFaceMeshes; - NCollection_DynamicArray myCoEdgeMeshes; - NCollection_DynamicArray myEdgeMeshes; -}; - -#endif // _BRepGraph_MeshCache_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshView.cxx index 89be1d1a21..a44bbc1f7e 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshView.cxx @@ -12,308 +12,648 @@ // commercial license or contractual agreement. #include +#include #include -#include +#include +#include +#include #include +namespace +{ +BRepGraph_CacheMesh& getCacheMesh(BRepGraph* theGraph) +{ + return *theGraph->CacheRegistry().Ensure(); +} +} // namespace + //================================================================================================= -// FaceOps +// PolyOps //================================================================================================= -bool BRepGraph::MeshView::FaceOps::isFresh(const BRepGraph_FaceId theFace, - const uint32_t theStoredGen) const +uint32_t BRepGraph::MeshView::PolyOps::NbFaceTriangulations() const { - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) - { - return false; - } - return theStoredGen == aStorage.Face(theFace).OwnGen; + return myGraph->myData->myIncStorage.NbFaceTriangulations(); } //================================================================================================= -bool BRepGraph::MeshView::FaceOps::HasTriangulation(const BRepGraph_FaceId theFace) const +uint32_t BRepGraph::MeshView::PolyOps::NbEdgePolygons3D() const { - const BRepGraph_MeshCacheStorage& aMeshCache = myGraph->myData->myMeshCache; - const BRepGraph_MeshCache::FaceMeshEntry* aCached = aMeshCache.FindFaceMesh(theFace); - if (aCached != nullptr && isFresh(theFace, aCached->StoredOwnGen)) - { - const BRepGraph_TriangulationRepId aRepId = aCached->ActiveTriangulationRepId(); - if (aRepId.IsValid()) - { - return true; - } - } - // Fallback to persistent mesh in definition. - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) - { - return false; - } - const BRepGraph_TriangulationRepId aRepId = aStorage.Face(theFace).TriangulationRepId; - return aRepId.IsValid(aStorage.NbTriangulations()) - && !aStorage.TriangulationRep(aRepId).IsRemoved; + return myGraph->myData->myIncStorage.NbEdgePolygons3D(); } //================================================================================================= -BRepGraph_TriangulationRepId BRepGraph::MeshView::FaceOps::ActiveTriangulationRepId( +uint32_t BRepGraph::MeshView::PolyOps::NbCoEdgePolygons2D() const +{ + return myGraph->myData->myIncStorage.NbCoEdgePolygons2D(); +} + +//================================================================================================= + +uint32_t BRepGraph::MeshView::PolyOps::NbCoEdgePolygonsOnTri() const +{ + return myGraph->myData->myIncStorage.NbCoEdgePolygonsOnTri(); +} + +//================================================================================================= + +uint32_t BRepGraph::MeshView::PolyOps::NbActiveTriangulations() const +{ + return myGraph->myData->myIncStorage.NbActiveFaceTriangulations(); +} + +//================================================================================================= + +uint32_t BRepGraph::MeshView::PolyOps::NbActivePolygons3D() const +{ + return myGraph->myData->myIncStorage.NbActiveEdgePolygons3D(); +} + +//================================================================================================= + +uint32_t BRepGraph::MeshView::PolyOps::NbActivePolygons2D() const +{ + return myGraph->myData->myIncStorage.NbActiveCoEdgePolygons2D(); +} + +//================================================================================================= + +uint32_t BRepGraph::MeshView::PolyOps::NbActivePolygonsOnTri() const +{ + return myGraph->myData->myIncStorage.NbActiveCoEdgePolygonsOnTri(); +} + +//================================================================================================= +// Cache.FaceOps +//================================================================================================= + +bool BRepGraph::MeshView::CacheView::FaceOps::Has(const BRepGraph_FaceId theFace) const +{ + return Entry(theFace) != nullptr; +} + +//================================================================================================= + +static const occ::handle THE_NULL_TRIANGULATION_MV; + +const occ::handle& BRepGraph::MeshView::CacheView::FaceOps::Triangulation( const BRepGraph_FaceId theFace) const { - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_MeshCacheStorage& aMeshCache = myGraph->myData->myMeshCache; - - // Cache-first. - const BRepGraph_MeshCache::FaceMeshEntry* aCached = aMeshCache.FindFaceMesh(theFace); - if (aCached != nullptr && isFresh(theFace, aCached->StoredOwnGen)) + const BRepGraph_CacheMesh::FaceMeshEntry* anEntry = Entry(theFace); + if (anEntry == nullptr || anEntry->Triangulation.IsNull()) { - const BRepGraph_TriangulationRepId aRepId = aCached->ActiveTriangulationRepId(); - if (aRepId.IsValid(aStorage.NbTriangulations()) && !aStorage.TriangulationRep(aRepId).IsRemoved) - { - return aRepId; - } + return THE_NULL_TRIANGULATION_MV; } - - // Fallback to persistent. - if (!theFace.IsValid(aStorage.NbFaces())) - { - return BRepGraph_TriangulationRepId(); - } - const BRepGraph_TriangulationRepId aRepId = aStorage.Face(theFace).TriangulationRepId; - if (!aRepId.IsValid(aStorage.NbTriangulations()) || aStorage.TriangulationRep(aRepId).IsRemoved) - { - return BRepGraph_TriangulationRepId(); - } - return aRepId; + return anEntry->Triangulation; } //================================================================================================= -const BRepGraph_MeshCache::FaceMeshEntry* BRepGraph::MeshView::FaceOps::CachedMesh( +const BRepGraph_CacheMesh::FaceMeshEntry* BRepGraph::MeshView::CacheView::FaceOps::Entry( const BRepGraph_FaceId theFace) const { - const BRepGraph_MeshCache::FaceMeshEntry* aCached = - myGraph->myData->myMeshCache.FindFaceMesh(theFace); - if (aCached != nullptr && isFresh(theFace, aCached->StoredOwnGen)) - { - return aCached; - } - return nullptr; + return getCacheMesh(myGraph).FindFaceMesh(theFace); } //================================================================================================= -// EdgeOps +// Cache.EdgeOps //================================================================================================= -bool BRepGraph::MeshView::EdgeOps::isFresh(const BRepGraph_EdgeId theEdge, - const uint32_t theStoredGen) const +bool BRepGraph::MeshView::CacheView::EdgeOps::Has(const BRepGraph_EdgeId theEdge) const +{ + const BRepGraph_CacheMesh::EdgeMeshEntry* anEntry = Entry(theEdge); + return anEntry != nullptr && !anEntry->Polygon3D.IsNull(); +} + +//================================================================================================= + +static const occ::handle THE_NULL_POLYGON3D_MV; + +const occ::handle& BRepGraph::MeshView::CacheView::EdgeOps::Polygon3D( + const BRepGraph_EdgeId theEdge) const +{ + const BRepGraph_CacheMesh::EdgeMeshEntry* anEntry = Entry(theEdge); + if (anEntry == nullptr || anEntry->Polygon3D.IsNull()) + { + return THE_NULL_POLYGON3D_MV; + } + return anEntry->Polygon3D; +} + +//================================================================================================= + +const BRepGraph_CacheMesh::EdgeMeshEntry* BRepGraph::MeshView::CacheView::EdgeOps::Entry( + const BRepGraph_EdgeId theEdge) const +{ + return getCacheMesh(myGraph).FindEdgeMesh(theEdge); +} + +//================================================================================================= +// Cache.CoEdgeOps +//================================================================================================= + +bool BRepGraph::MeshView::CacheView::CoEdgeOps::Has(const BRepGraph_CoEdgeId theCoEdge) const +{ + return getCacheMesh(myGraph).HasCoEdgeMesh(theCoEdge); +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph::MeshView::CacheView::CoEdgeOps:: + FindPolygon2D(const BRepGraph_CoEdgeId theCoEdge) const +{ + return getCacheMesh(myGraph).FindCoEdgePolygon2D(theCoEdge); +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph::MeshView::CacheView::CoEdgeOps:: + FindPolygonOnTri(const BRepGraph_CoEdgeId theCoEdge) const +{ + return getCacheMesh(myGraph).FindCoEdgePolygonOnTri(theCoEdge); +} + +//================================================================================================= + +const BRepGraph_CacheMesh::CoEdgeMeshEntry* BRepGraph::MeshView::CacheView::CoEdgeOps::FindRaw( + const BRepGraph_CoEdgeId theCoEdge) const +{ + return getCacheMesh(myGraph).findCoEdgeEntryRaw(BRepGraph_CacheMesh::DefaultDisplaySlot, + theCoEdge); +} + +//================================================================================================= +// Persistent.FaceOps +//================================================================================================= + +bool BRepGraph::MeshView::PersistentView::FaceOps::Has(const BRepGraph_FaceId theFace) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges())) + if (!theFace.IsValid(aStorage.NbFaces()) || aStorage.IsRemoved(theFace)) { return false; } - return theStoredGen == aStorage.Edge(theEdge).OwnGen; + const BRepGraphInc::FaceDef& aFace = aStorage.Face(theFace); + return aFace.TriangulationRepId.IsValid(aStorage.NbFaceTriangulations()) + && !aStorage.IsRemoved(aFace.TriangulationRepId); } //================================================================================================= -bool BRepGraph::MeshView::EdgeOps::HasPolygon3D(const BRepGraph_EdgeId theEdge) const +const occ::handle& BRepGraph::MeshView::PersistentView::FaceOps::Triangulation( + const BRepGraph_FaceId theFace) const { - const BRepGraph_MeshCacheStorage& aMeshCache = myGraph->myData->myMeshCache; - const BRepGraph_MeshCache::EdgeMeshEntry* aCached = aMeshCache.FindEdgeMesh(theEdge); - if (aCached != nullptr && isFresh(theEdge, aCached->StoredOwnGen)) - { - if (aCached->Polygon3DRepId.IsValid()) - { - return true; - } - } - // Fallback to persistent. const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges())) + if (!theFace.IsValid(aStorage.NbFaces()) || aStorage.IsRemoved(theFace)) + { + return THE_NULL_TRIANGULATION_MV; + } + const BRepGraphInc::FaceDef& aFace = aStorage.Face(theFace); + if (!aFace.TriangulationRepId.IsValid(aStorage.NbFaceTriangulations()) + || aStorage.IsRemoved(aFace.TriangulationRepId)) + { + return THE_NULL_TRIANGULATION_MV; + } + return aStorage.FaceTriangulationRep(aFace.TriangulationRepId).Triangulation; +} + +//================================================================================================= +// Persistent.EdgeOps +//================================================================================================= + +bool BRepGraph::MeshView::PersistentView::EdgeOps::Has(const BRepGraph_EdgeId theEdge) const +{ + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theEdge.IsValid(aStorage.NbEdges()) || aStorage.IsRemoved(theEdge)) { return false; } - const BRepGraph_Polygon3DRepId aRepId = aStorage.Edge(theEdge).Polygon3DRepId; - return aRepId.IsValid(aStorage.NbPolygons3D()) && !aStorage.Polygon3DRep(aRepId).IsRemoved; + const BRepGraph_EdgePolygon3DRepId aRepId = aStorage.Edge(theEdge).Polygon3DRepId; + return aRepId.IsValid(aStorage.NbEdgePolygons3D()) && !aStorage.IsRemoved(aRepId); } //================================================================================================= -BRepGraph_Polygon3DRepId BRepGraph::MeshView::EdgeOps::Polygon3DRepId( +const occ::handle& BRepGraph::MeshView::PersistentView::EdgeOps::Polygon3D( const BRepGraph_EdgeId theEdge) const { - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - const BRepGraph_MeshCacheStorage& aMeshCache = myGraph->myData->myMeshCache; - - // Cache-first. - const BRepGraph_MeshCache::EdgeMeshEntry* aCached = aMeshCache.FindEdgeMesh(theEdge); - if (aCached != nullptr && isFresh(theEdge, aCached->StoredOwnGen)) + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theEdge.IsValid(aStorage.NbEdges()) || aStorage.IsRemoved(theEdge)) { - if (aCached->Polygon3DRepId.IsValid(aStorage.NbPolygons3D()) - && !aStorage.Polygon3DRep(aCached->Polygon3DRepId).IsRemoved) - { - return aCached->Polygon3DRepId; - } + return THE_NULL_POLYGON3D_MV; } - - // Fallback to persistent. - if (!theEdge.IsValid(aStorage.NbEdges())) + const BRepGraph_EdgePolygon3DRepId aRepId = aStorage.Edge(theEdge).Polygon3DRepId; + if (!aRepId.IsValid(aStorage.NbEdgePolygons3D()) || aStorage.IsRemoved(aRepId)) { - return BRepGraph_Polygon3DRepId(); + return THE_NULL_POLYGON3D_MV; } - const BRepGraph_Polygon3DRepId aRepId = aStorage.Edge(theEdge).Polygon3DRepId; - if (!aRepId.IsValid(aStorage.NbPolygons3D()) || aStorage.Polygon3DRep(aRepId).IsRemoved) - { - return BRepGraph_Polygon3DRepId(); - } - return aRepId; + return aStorage.EdgePolygon3DRep(aRepId).Polygon; } //================================================================================================= -const BRepGraph_MeshCache::EdgeMeshEntry* BRepGraph::MeshView::EdgeOps::CachedMesh( - const BRepGraph_EdgeId theEdge) const +bool BRepGraph::MeshView::PersistentView::EdgeOps::HasPolygonOnTriangulation( + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace) const { - const BRepGraph_MeshCache::EdgeMeshEntry* aCached = - myGraph->myData->myMeshCache.FindEdgeMesh(theEdge); - if (aCached != nullptr && isFresh(theEdge, aCached->StoredOwnGen)) + const BRepGraph_CoEdgeId aCoEdgeId = + BRepGraph_Tool::Edge::FindCoEdgeId(*myGraph, theEdge, theFace); + if (!aCoEdgeId.IsValid()) { - return aCached; + return false; } - return nullptr; + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); + return aCoEdge.PolygonOnTriRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) + && !aStorage.IsRemoved(aCoEdge.PolygonOnTriRepId); } //================================================================================================= -// CoEdgeOps + +static const occ::handle THE_NULL_POLYGON_ON_TRI_MV; + +const occ::handle& BRepGraph::MeshView::PersistentView::EdgeOps:: + PolygonOnTriangulation(const BRepGraph_EdgeId theEdge, const BRepGraph_FaceId theFace) const +{ + const BRepGraph_CoEdgeId aCoEdgeId = + BRepGraph_Tool::Edge::FindCoEdgeId(*myGraph, theEdge, theFace); + if (!aCoEdgeId.IsValid()) + { + return THE_NULL_POLYGON_ON_TRI_MV; + } + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); + if (!aCoEdge.PolygonOnTriRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) + || aStorage.IsRemoved(aCoEdge.PolygonOnTriRepId)) + { + return THE_NULL_POLYGON_ON_TRI_MV; + } + return aStorage.CoEdgePolygonOnTriRep(aCoEdge.PolygonOnTriRepId).Polygon; +} + +//================================================================================================= +// Persistent.CoEdgeOps //================================================================================================= -bool BRepGraph::MeshView::CoEdgeOps::isFresh(const BRepGraph_CoEdgeId theCoEdge, - const uint32_t theStoredGen) const +bool BRepGraph::MeshView::PersistentView::CoEdgeOps::Has(const BRepGraph_CoEdgeId theCoEdge) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (!theCoEdge.IsValid(aStorage.NbCoEdges())) { return false; } - return theStoredGen == aStorage.CoEdge(theCoEdge).OwnGen; + const BRepGraph_CoEdgePolygon2DRepId aRepId = aStorage.CoEdge(theCoEdge).Polygon2DRepId; + return aRepId.IsValid(aStorage.NbCoEdgePolygons2D()) && !aStorage.IsRemoved(aRepId); } //================================================================================================= -bool BRepGraph::MeshView::CoEdgeOps::HasMesh(const BRepGraph_CoEdgeId theCoEdge) const +static const occ::handle THE_NULL_POLYGON2D_MV; + +const occ::handle& BRepGraph::MeshView::PersistentView::CoEdgeOps::PolygonOnSurface( + const BRepGraph_CoEdgeId theCoEdge) const { - const BRepGraph_MeshCache::CoEdgeMeshEntry* aCached = - myGraph->myData->myMeshCache.FindCoEdgeMesh(theCoEdge); - if (aCached != nullptr && isFresh(theCoEdge, aCached->StoredOwnGen)) + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theCoEdge.IsValid(aStorage.NbCoEdges())) + { + return THE_NULL_POLYGON2D_MV; + } + const BRepGraph_CoEdgePolygon2DRepId aRepId = aStorage.CoEdge(theCoEdge).Polygon2DRepId; + if (!aRepId.IsValid(aStorage.NbCoEdgePolygons2D()) || aStorage.IsRemoved(aRepId)) + { + return THE_NULL_POLYGON2D_MV; + } + return aStorage.CoEdgePolygon2DRep(aRepId).Polygon; +} + +//================================================================================================= + +bool BRepGraph::MeshView::PersistentView::CoEdgeOps::HasPolygonOnTriangulation( + const BRepGraph_CoEdgeId theCoEdge) const +{ + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theCoEdge.IsValid(aStorage.NbCoEdges())) + { + return false; + } + const BRepGraph_CoEdgePolygonOnTriRepId aRepId = aStorage.CoEdge(theCoEdge).PolygonOnTriRepId; + return aRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) && !aStorage.IsRemoved(aRepId); +} + +//================================================================================================= + +const occ::handle& BRepGraph::MeshView::PersistentView::CoEdgeOps:: + PolygonOnTriangulation(const BRepGraph_CoEdgeId theCoEdge) const +{ + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theCoEdge.IsValid(aStorage.NbCoEdges())) + { + return THE_NULL_POLYGON_ON_TRI_MV; + } + const BRepGraph_CoEdgePolygonOnTriRepId aRepId = aStorage.CoEdge(theCoEdge).PolygonOnTriRepId; + if (!aRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) || aStorage.IsRemoved(aRepId)) + { + return THE_NULL_POLYGON_ON_TRI_MV; + } + return aStorage.CoEdgePolygonOnTriRep(aRepId).Polygon; +} + +//================================================================================================= +// Effective.FaceOps (cache-first, persistent fallback) +//================================================================================================= + +bool BRepGraph::MeshView::EffectiveView::FaceOps::Has(const BRepGraph_FaceId theFace) const +{ + const BRepGraph_CacheMesh::SlotId aSlot = getCacheMesh(myGraph).ActiveDisplaySlot(); + return getCacheMesh(myGraph).FindFaceMesh(aSlot, theFace) != nullptr + || myGraph->Mesh().Persistent().Faces().Has(theFace); +} + +//================================================================================================= + +const occ::handle& BRepGraph::MeshView::EffectiveView::FaceOps::Triangulation( + const BRepGraph_FaceId theFace) const +{ + const BRepGraph_CacheMesh::SlotId aSlot = getCacheMesh(myGraph).ActiveDisplaySlot(); + const BRepGraph_CacheMesh::FaceMeshEntry* anEntry = + getCacheMesh(myGraph).FindFaceMesh(aSlot, theFace); + if (anEntry != nullptr && !anEntry->Triangulation.IsNull()) + { + return anEntry->Triangulation; + } + return myGraph->Mesh().Persistent().Faces().Triangulation(theFace); +} + +//================================================================================================= +// Effective.EdgeOps (cache-first, persistent fallback) +//================================================================================================= + +bool BRepGraph::MeshView::EffectiveView::EdgeOps::Has(const BRepGraph_EdgeId theEdge) const +{ + const BRepGraph_CacheMesh::SlotId aSlot = getCacheMesh(myGraph).ActiveDisplaySlot(); + return getCacheMesh(myGraph).FindEdgeMesh(aSlot, theEdge) != nullptr + || myGraph->Mesh().Persistent().Edges().Has(theEdge); +} + +//================================================================================================= + +const occ::handle& BRepGraph::MeshView::EffectiveView::EdgeOps::Polygon3D( + const BRepGraph_EdgeId theEdge) const +{ + const BRepGraph_CacheMesh::SlotId aSlot = getCacheMesh(myGraph).ActiveDisplaySlot(); + const BRepGraph_CacheMesh::EdgeMeshEntry* anEntry = + getCacheMesh(myGraph).FindEdgeMesh(aSlot, theEdge); + if (anEntry != nullptr && !anEntry->Polygon3D.IsNull()) + { + return anEntry->Polygon3D; + } + return myGraph->Mesh().Persistent().Edges().Polygon3D(theEdge); +} + +//================================================================================================= +// Effective.CoEdgeOps (cache-first, persistent fallback) +//================================================================================================= + +bool BRepGraph::MeshView::EffectiveView::CoEdgeOps::Has(const BRepGraph_CoEdgeId theCoEdge) const +{ + return HasPolygonOnSurface(theCoEdge) || HasPolygonOnTriangulation(theCoEdge); +} + +//================================================================================================= + +bool BRepGraph::MeshView::EffectiveView::CoEdgeOps::HasPolygonOnSurface( + const BRepGraph_CoEdgeId theCoEdge) const +{ + const BRepGraph_CacheMesh::SlotId aSlot = getCacheMesh(myGraph).ActiveDisplaySlot(); + const BRepGraph_CacheMesh::CoEdgeMeshEntry* anEntry = + getCacheMesh(myGraph).FindCoEdgePolygon2D(aSlot, theCoEdge); + if (anEntry != nullptr && !anEntry->Polygon2D.IsNull()) { return true; } - return false; + return myGraph->Mesh().Persistent().CoEdges().Has(theCoEdge); } //================================================================================================= -const BRepGraph_MeshCache::CoEdgeMeshEntry* BRepGraph::MeshView::CoEdgeOps::CachedMesh( +const occ::handle& BRepGraph::MeshView::EffectiveView::CoEdgeOps::PolygonOnSurface( const BRepGraph_CoEdgeId theCoEdge) const { - const BRepGraph_MeshCache::CoEdgeMeshEntry* aCached = - myGraph->myData->myMeshCache.FindCoEdgeMesh(theCoEdge); - if (aCached != nullptr && isFresh(theCoEdge, aCached->StoredOwnGen)) + const BRepGraph_CacheMesh::SlotId aSlot = getCacheMesh(myGraph).ActiveDisplaySlot(); + const BRepGraph_CacheMesh::CoEdgeMeshEntry* anEntry = + getCacheMesh(myGraph).FindCoEdgePolygon2D(aSlot, theCoEdge); + if (anEntry != nullptr && !anEntry->Polygon2D.IsNull()) { - return aCached; + return anEntry->Polygon2D; } - return nullptr; + return myGraph->Mesh().Persistent().CoEdges().PolygonOnSurface(theCoEdge); } -//================================================================================================= -// PolyOps //================================================================================================= -int BRepGraph::MeshView::PolyOps::NbTriangulations() const +bool BRepGraph::MeshView::EffectiveView::CoEdgeOps::HasPolygonOnTriangulation( + const BRepGraph_CoEdgeId theCoEdge) const { - return myGraph->myData->myIncStorage.NbTriangulations(); + const BRepGraph_CacheMesh::SlotId aSlot = getCacheMesh(myGraph).ActiveDisplaySlot(); + const BRepGraph_CacheMesh::CoEdgeMeshEntry* anEntry = + getCacheMesh(myGraph).FindCoEdgePolygonOnTri(aSlot, theCoEdge); + if (anEntry != nullptr && !anEntry->PolygonsOnTri.IsEmpty()) + { + return true; + } + return myGraph->Mesh().Persistent().CoEdges().HasPolygonOnTriangulation(theCoEdge); } //================================================================================================= -int BRepGraph::MeshView::PolyOps::NbPolygons3D() const +const occ::handle& BRepGraph::MeshView::EffectiveView::CoEdgeOps:: + PolygonOnTriangulation(const BRepGraph_CoEdgeId theCoEdge) const { - return myGraph->myData->myIncStorage.NbPolygons3D(); + const BRepGraph_CacheMesh::SlotId aSlot = getCacheMesh(myGraph).ActiveDisplaySlot(); + const BRepGraph_CacheMesh::CoEdgeMeshEntry* anEntry = + getCacheMesh(myGraph).FindCoEdgePolygonOnTri(aSlot, theCoEdge); + if (anEntry != nullptr && !anEntry->PolygonsOnTri.IsEmpty()) + { + const occ::handle& aPoly = anEntry->PolygonsOnTri.Value(0); + if (!aPoly.IsNull()) + { + return aPoly; + } + } + return myGraph->Mesh().Persistent().CoEdges().PolygonOnTriangulation(theCoEdge); +} + +//================================================================================================= +// Editor.FaceOps (cache writes) +//================================================================================================= + +void BRepGraph::MeshView::EditorView::FaceOps::SetCachedTriangulation( + const BRepGraph_FaceId theFace, + const occ::handle& theTriangulation) +{ + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theFace.IsValid(aStorage.NbFaces())) + { + return; + } + BRepGraph_CacheMesh& aCacheMesh = getCacheMesh(myGraph); + BRepGraph_CacheMesh::FaceMeshEntry& anEntry = aCacheMesh.ChangeFaceMesh(theFace); + anEntry.Triangulation = theTriangulation; + aCacheMesh.BindFresh(anEntry, theFace); + aCacheMesh.BumpFaceMeshGeneration(theFace); } //================================================================================================= -int BRepGraph::MeshView::PolyOps::NbPolygons2D() const +void BRepGraph::MeshView::EditorView::FaceOps::Clear(const BRepGraph_FaceId theFace) { - return myGraph->myData->myIncStorage.NbPolygons2D(); + BRepGraph_CacheMesh& aCacheMesh = getCacheMesh(myGraph); + + BRepGraph_CacheMesh::FaceMeshEntry& anEntry = aCacheMesh.ChangeFaceMesh(theFace); + anEntry.ClearRepresentation(); + aCacheMesh.BindFresh(anEntry, theFace); + aCacheMesh.BumpFaceMeshGeneration(theFace); + + // Do NOT touch coedge entries. Polygon2D stays valid. + // Coedge PolygonOnTri staleness happens naturally via FaceMeshGeneration mismatch. +} + +//================================================================================================= +// Editor.EdgeOps (cache writes) +//================================================================================================= + +void BRepGraph::MeshView::EditorView::EdgeOps::SetCachedPolygon3D( + const BRepGraph_EdgeId theEdge, + const occ::handle& thePolygon3D) +{ + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theEdge.IsValid(aStorage.NbEdges())) + { + return; + } + BRepGraph_CacheMesh& aCacheMesh = getCacheMesh(myGraph); + BRepGraph_CacheMesh::EdgeMeshEntry& anEntry = aCacheMesh.ChangeEdgeMesh(theEdge); + anEntry.Polygon3D = thePolygon3D; + aCacheMesh.BindFresh(anEntry, theEdge); } //================================================================================================= -int BRepGraph::MeshView::PolyOps::NbPolygonsOnTri() const +void BRepGraph::MeshView::EditorView::EdgeOps::Clear(const BRepGraph_EdgeId theEdge) { - return myGraph->myData->myIncStorage.NbPolygonsOnTri(); + getCacheMesh(myGraph).ClearEdgeMesh(theEdge); +} + +//================================================================================================= +// Editor.CoEdgeOps (cache writes) +//================================================================================================= + +void BRepGraph::MeshView::EditorView::CoEdgeOps::AppendCachedPolygonOnTri( + const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& thePolygonOnTri) +{ + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theCoEdge.IsValid(aStorage.NbCoEdges()) || thePolygonOnTri.IsNull()) + { + return; + } + BRepGraph_CacheMesh& aCacheMesh = getCacheMesh(myGraph); + BRepGraph_CacheMesh::CoEdgeMeshEntry& anEntry = aCacheMesh.ChangeCoEdgeMesh(theCoEdge); + anEntry.PolygonsOnTri.Append(thePolygonOnTri); + aCacheMesh.BindFresh(anEntry, theCoEdge); } //================================================================================================= -int BRepGraph::MeshView::PolyOps::NbActiveTriangulations() const +void BRepGraph::MeshView::EditorView::CoEdgeOps::SetCachedPolygon2D( + const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& thePolygon2D) { - return myGraph->myData->myIncStorage.NbActiveTriangulations(); + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + if (!theCoEdge.IsValid(aStorage.NbCoEdges())) + { + return; + } + BRepGraph_CacheMesh& aCacheMesh = getCacheMesh(myGraph); + BRepGraph_CacheMesh::CoEdgeMeshEntry& anEntry = aCacheMesh.ChangeCoEdgeMesh(theCoEdge); + anEntry.Polygon2D = thePolygon2D; + aCacheMesh.BindFresh(anEntry, theCoEdge); } //================================================================================================= -int BRepGraph::MeshView::PolyOps::NbActivePolygons3D() const +void BRepGraph::MeshView::EditorView::CoEdgeOps::Clear(const BRepGraph_CoEdgeId theCoEdge) { - return myGraph->myData->myIncStorage.NbActivePolygons3D(); + getCacheMesh(myGraph).ClearCoEdgeMesh(theCoEdge); } //================================================================================================= -int BRepGraph::MeshView::PolyOps::NbActivePolygons2D() const +void BRepGraph::MeshView::EditorView::PromoteToPersistent() { - return myGraph->myData->myIncStorage.NbActivePolygons2D(); -} - -//================================================================================================= - -int BRepGraph::MeshView::PolyOps::NbActivePolygonsOnTri() const -{ - return myGraph->myData->myIncStorage.NbActivePolygonsOnTri(); -} - -//================================================================================================= - -const BRepGraphInc::TriangulationRep& BRepGraph::MeshView::PolyOps::TriangulationRep( - const BRepGraph_TriangulationRepId theRep) const -{ - return myGraph->myData->myIncStorage.TriangulationRep(theRep); -} - -//================================================================================================= - -const BRepGraphInc::Polygon3DRep& BRepGraph::MeshView::PolyOps::Polygon3DRep( - const BRepGraph_Polygon3DRepId theRep) const -{ - return myGraph->myData->myIncStorage.Polygon3DRep(theRep); -} - -//================================================================================================= - -const BRepGraphInc::Polygon2DRep& BRepGraph::MeshView::PolyOps::Polygon2DRep( - const BRepGraph_Polygon2DRepId theRep) const -{ - return myGraph->myData->myIncStorage.Polygon2DRep(theRep); -} - -//================================================================================================= - -const BRepGraphInc::PolygonOnTriRep& BRepGraph::MeshView::PolyOps::PolygonOnTriRep( - const BRepGraph_PolygonOnTriRepId theRep) const -{ - return myGraph->myData->myIncStorage.PolygonOnTriRep(theRep); + for (BRepGraph_FaceId aFaceId = myGraph->Topo().Faces().StartId(); + aFaceId < myGraph->Topo().Faces().EndId(); + ++aFaceId) + { + if (aFaceId.IsRemoved(*myGraph)) + { + continue; + } + const BRepGraph_CacheMesh::FaceMeshEntry* anEntry = + myGraph->Mesh().Cache().Faces().Entry(aFaceId); + if (anEntry == nullptr) + { + continue; + } + const occ::handle& aTri = anEntry->Triangulation; + if (!aTri.IsNull()) + { + myGraph->Editor().Faces().SetPersistentTriangulation(aFaceId, aTri); + } + } + + for (BRepGraph_EdgeId anEdgeId = myGraph->Topo().Edges().StartId(); + anEdgeId < myGraph->Topo().Edges().EndId(); + ++anEdgeId) + { + if (anEdgeId.IsRemoved(*myGraph)) + { + continue; + } + const BRepGraph_CacheMesh::EdgeMeshEntry* anEntry = + myGraph->Mesh().Cache().Edges().Entry(anEdgeId); + if (anEntry != nullptr && !anEntry->Polygon3D.IsNull()) + { + myGraph->Editor().Edges().SetPersistentPolygon3D(anEdgeId, anEntry->Polygon3D); + } + } + + for (BRepGraph_CoEdgeId aCoEdgeId = myGraph->Topo().CoEdges().StartId(); + aCoEdgeId < myGraph->Topo().CoEdges().EndId(); + ++aCoEdgeId) + { + if (aCoEdgeId.IsRemoved(*myGraph)) + { + continue; + } + const BRepGraph_CacheMesh::CoEdgeMeshEntry* aPoly2DEntry = + getCacheMesh(myGraph).FindCoEdgePolygon2D(aCoEdgeId); + if (aPoly2DEntry != nullptr && !aPoly2DEntry->Polygon2D.IsNull()) + { + myGraph->Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, aPoly2DEntry->Polygon2D); + } + const BRepGraph_CacheMesh::CoEdgeMeshEntry* aPolyOnTriEntry = + getCacheMesh(myGraph).FindCoEdgePolygonOnTri(aCoEdgeId); + if (aPolyOnTriEntry != nullptr && !aPolyOnTriEntry->PolygonsOnTri.IsEmpty()) + { + const occ::handle& aPoly = + aPolyOnTriEntry->PolygonsOnTri.Value(0); + if (!aPoly.IsNull()) + { + myGraph->Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, aPoly); + } + } + } } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshView.hxx index c7961a4b59..c3d1eb2a4b 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MeshView.hxx @@ -15,182 +15,600 @@ #define _BRepGraph_MeshView_HeaderFile #include -#include -#include +#include +#include +#include +#include +#include -namespace BRepGraph_MeshCache -{ -struct FaceMeshEntry; -struct CoEdgeMeshEntry; -struct EdgeMeshEntry; -} // namespace BRepGraph_MeshCache - -namespace BRepGraphInc -{ -struct TriangulationRep; -struct Polygon3DRep; -struct Polygon2DRep; -struct PolygonOnTriRep; -} // namespace BRepGraphInc - -//! @brief Read-only view over mesh data with cache-first, persistent-fallback priority. +//! @brief Read/write view over mesh data. //! -//! Provides mesh queries that check the mesh cache (algorithm-derived mesh -//! from BRepGraphMesh) first, falling back to persistent mesh stored in -//! topology definitions (imported from STEP, etc.). +//! Splits mesh access into three explicit sub-views: +//! - `Cache()` - reads from the BRepGraphMesh cache only (algorithm-derived, +//! freshness-checked against the entity's OwnGen). +//! - `Persistent()` - reads from definition-resident mesh (FaceDef.TriangulationRepId, +//! EdgeDef.Polygon3DRepId, CoEdgeDef.Polygon2DRepId, +//! CoEdgeDef.PolygonOnTriRepId). +//! - `Editor()` - cache mutations (append/clear). Persistent rep creation lives on +//! `BRepGraph::Editor().Edges()`, `BRepGraph::Editor().CoEdges()`, +//! `BRepGraph::Editor().Faces()` since reps back the topology defs. +//! - `Poly()` - mesh element count queries (shared by all paths). //! -//! For mesh cache writes and rep creation, use BRepGraph_Tool::Mesh. +//! There is no fallback path that mixes cache and persistent - callers pick the +//! source explicitly. //! -//! Obtained via BRepGraph::Mesh(). +//! Obtained via `BRepGraph::Mesh()` (const) or `BRepGraph::Mesh()` (non-const for Editor). class BRepGraph::MeshView { public: - //! @brief Face mesh queries (cache-first, persistent fallback). - class FaceOps + //! Cache reads. Each accessor returns data only if a fresh cache entry exists + //! for the given entity (matched against its current OwnGen). + class CacheView { public: - //! Check if face has any mesh data (cached or persistent). - [[nodiscard]] Standard_EXPORT bool HasTriangulation(const BRepGraph_FaceId theFace) const; + class FaceOps + { + public: + //! True if a fresh cached triangulation is present. + //! @param[in] theFace typed face definition identifier + //! @return true if Entry() would return non-null + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_FaceId theFace) const; - //! Active triangulation rep id (cached if fresh, else persistent). - //! @return valid TriangulationRepId, or invalid if no mesh available - [[nodiscard]] Standard_EXPORT BRepGraph_TriangulationRepId - ActiveTriangulationRepId(const BRepGraph_FaceId theFace) const; + //! Cached triangulation handle. + //! @param[in] theFace typed face definition identifier + //! @return triangulation handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& Triangulation( + const BRepGraph_FaceId theFace) const; - //! Direct access to cached face mesh entry (null if absent or stale). - [[nodiscard]] Standard_EXPORT const BRepGraph_MeshCache::FaceMeshEntry* CachedMesh( - const BRepGraph_FaceId theFace) const; + //! Raw cached face mesh entry, or nullptr if absent or stale. + //! @param[in] theFace typed face definition identifier + //! @return cache entry pointer, or nullptr + [[nodiscard]] Standard_EXPORT const BRepGraph_CacheMesh::FaceMeshEntry* Entry( + const BRepGraph_FaceId theFace) const; + + private: + friend class CacheView; + + explicit FaceOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + class EdgeOps + { + public: + //! True if a fresh cached Polygon3D is bound to the edge. + //! @param[in] theEdge typed edge definition identifier + //! @return true if a fresh Polygon3D is present in cache + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_EdgeId theEdge) const; + + //! Cached Polygon3D handle. + //! @param[in] theEdge typed edge definition identifier + //! @return polygon-3D handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& Polygon3D( + const BRepGraph_EdgeId theEdge) const; + + //! Raw cached edge mesh entry, or nullptr if absent or stale. + //! @param[in] theEdge typed edge definition identifier + //! @return cache entry pointer, or nullptr + [[nodiscard]] Standard_EXPORT const BRepGraph_CacheMesh::EdgeMeshEntry* Entry( + const BRepGraph_EdgeId theEdge) const; + + private: + friend class CacheView; + + explicit EdgeOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + class CoEdgeOps + { + public: + //! True if a fresh cached entry exists (any of polygon-2D / polygon-on-tri). + //! @param[in] theCoEdge typed coedge definition identifier + //! @return true if a fresh cache entry exists + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_CoEdgeId theCoEdge) const; + + //! Return coedge entry if Polygon2D is fresh, nullptr otherwise. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return cache entry pointer, or nullptr + [[nodiscard]] Standard_EXPORT const BRepGraph_CacheMesh::CoEdgeMeshEntry* FindPolygon2D( + const BRepGraph_CoEdgeId theCoEdge) const; + + //! Return coedge entry if PolygonsOnTri is fresh, nullptr otherwise. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return cache entry pointer, or nullptr + [[nodiscard]] Standard_EXPORT const BRepGraph_CacheMesh::CoEdgeMeshEntry* FindPolygonOnTri( + const BRepGraph_CoEdgeId theCoEdge) const; + + //! Raw coedge entry access (no freshness filtering). Returns nullptr if + //! the entry has no representation. For internal/testing use. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return cache entry pointer, or nullptr if absent + [[nodiscard]] Standard_EXPORT const BRepGraph_CacheMesh::CoEdgeMeshEntry* FindRaw( + const BRepGraph_CoEdgeId theCoEdge) const; + + private: + friend class CacheView; + + explicit CoEdgeOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + //! Grouped face cache queries. + [[nodiscard]] const FaceOps& Faces() const { return myFaces; } + + //! Grouped edge cache queries. + [[nodiscard]] const EdgeOps& Edges() const { return myEdges; } + + //! Grouped coedge cache queries. + [[nodiscard]] const CoEdgeOps& CoEdges() const { return myCoEdges; } private: friend class MeshView; - explicit FaceOps(const BRepGraph* theGraph) - : myGraph(theGraph) + explicit CacheView(BRepGraph* theGraph) + : myFaces(theGraph), + myEdges(theGraph), + myCoEdges(theGraph) { } - [[nodiscard]] bool isFresh(const BRepGraph_FaceId theFace, const uint32_t theStoredGen) const; - - const BRepGraph* myGraph; + FaceOps myFaces; + EdgeOps myEdges; + CoEdgeOps myCoEdges; }; - //! @brief Edge mesh queries (cache-first, persistent fallback). - class EdgeOps + //! Persistent reads. Resolves data through the rep id stored on the entity's + //! definition (FaceDef / EdgeDef / CoEdgeDef). Independent of the cache. + class PersistentView { public: - //! Check if edge has polygon-3D mesh data (cached or persistent). - [[nodiscard]] Standard_EXPORT bool HasPolygon3D(const BRepGraph_EdgeId theEdge) const; + class FaceOps + { + public: + //! True if FaceDef.TriangulationRepId is valid and the rep is not removed. + //! @param[in] theFace typed face definition identifier + //! @return true if a persistent triangulation is bound + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_FaceId theFace) const; - //! Polygon3D rep id (cached if fresh, else persistent). - [[nodiscard]] Standard_EXPORT BRepGraph_Polygon3DRepId - Polygon3DRepId(const BRepGraph_EdgeId theEdge) const; + //! Persistent triangulation handle. + //! @param[in] theFace typed face definition identifier + //! @return triangulation handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& Triangulation( + const BRepGraph_FaceId theFace) const; - //! Direct access to cached edge mesh entry (null if absent or stale). - [[nodiscard]] Standard_EXPORT const BRepGraph_MeshCache::EdgeMeshEntry* CachedMesh( - const BRepGraph_EdgeId theEdge) const; + private: + friend class PersistentView; + + explicit FaceOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + class EdgeOps + { + public: + //! True if EdgeDef.Polygon3DRepId is bound (dominant kind on edges). + //! @param[in] theEdge typed edge definition identifier + //! @return true if a persistent Polygon3D is bound + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_EdgeId theEdge) const; + + //! Persistent Polygon3D handle. + //! @param[in] theEdge typed edge definition identifier + //! @return polygon-3D handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& Polygon3D( + const BRepGraph_EdgeId theEdge) const; + + //! True if the (edge, face) coedge has a polygon-on-triangulation. + //! @param[in] theEdge typed edge definition identifier + //! @param[in] theFace typed face definition identifier + //! @return true if persistent polygon-on-triangulation is bound + [[nodiscard]] Standard_EXPORT bool HasPolygonOnTriangulation( + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace) const; + + //! Polygon-on-triangulation for the (edge, face) coedge. + //! @param[in] theEdge typed edge definition identifier + //! @param[in] theFace typed face definition identifier + //! @return polygon-on-triangulation handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& + PolygonOnTriangulation(const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace) const; + + private: + friend class PersistentView; + + explicit EdgeOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + class CoEdgeOps + { + public: + //! True if CoEdgeDef.Polygon2DRepId is bound (dominant kind on coedges). + //! @param[in] theCoEdge typed coedge definition identifier + //! @return true if a persistent polygon-on-surface is bound + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_CoEdgeId theCoEdge) const; + + //! Persistent polygon-on-surface (2D polygon) bound to the coedge. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return polygon-2D handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& PolygonOnSurface( + const BRepGraph_CoEdgeId theCoEdge) const; + + //! True if CoEdgeDef.PolygonOnTriRepId is bound. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return true if persistent polygon-on-triangulation is bound + [[nodiscard]] Standard_EXPORT bool HasPolygonOnTriangulation( + const BRepGraph_CoEdgeId theCoEdge) const; + + //! Persistent polygon-on-triangulation bound to the coedge. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return polygon-on-triangulation handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& + PolygonOnTriangulation(const BRepGraph_CoEdgeId theCoEdge) const; + + private: + friend class PersistentView; + + explicit CoEdgeOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + //! Grouped face persistent queries. + [[nodiscard]] const FaceOps& Faces() const { return myFaces; } + + //! Grouped edge persistent queries. + [[nodiscard]] const EdgeOps& Edges() const { return myEdges; } + + //! Grouped coedge persistent queries. + [[nodiscard]] const CoEdgeOps& CoEdges() const { return myCoEdges; } private: friend class MeshView; - explicit EdgeOps(const BRepGraph* theGraph) - : myGraph(theGraph) + explicit PersistentView(BRepGraph* theGraph) + : myFaces(theGraph), + myEdges(theGraph), + myCoEdges(theGraph) { } - [[nodiscard]] bool isFresh(const BRepGraph_EdgeId theEdge, const uint32_t theStoredGen) const; - - const BRepGraph* myGraph; + FaceOps myFaces; + EdgeOps myEdges; + CoEdgeOps myCoEdges; }; - //! @brief CoEdge mesh queries (cache-first, persistent fallback). - class CoEdgeOps + //! Resolves mesh data by checking the cache first and the persistent (def-resident) + //! source second. Callers that do not care which source supplies the data go + //! through this view; callers that do care use Cache() or Persistent() directly. + class EffectiveView { public: - //! Check if coedge has cached mesh data (polygon-on-tri or polygon-2D). - [[nodiscard]] Standard_EXPORT bool HasMesh(const BRepGraph_CoEdgeId theCoEdge) const; + class FaceOps + { + public: + //! True if the face has a triangulation in either cache or persistent storage. + //! @param[in] theFace typed face definition identifier + //! @return true if any triangulation is reachable + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_FaceId theFace) const; - //! Direct access to cached coedge mesh entry (null if absent or stale). - [[nodiscard]] Standard_EXPORT const BRepGraph_MeshCache::CoEdgeMeshEntry* CachedMesh( - const BRepGraph_CoEdgeId theCoEdge) const; + //! Cached triangulation handle (cache first, persistent fallback). + //! @param[in] theFace typed face definition identifier + //! @return triangulation handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& Triangulation( + const BRepGraph_FaceId theFace) const; + + private: + friend class EffectiveView; + + explicit FaceOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + class EdgeOps + { + public: + //! True if the edge has a Polygon3D in either cache or persistent storage. + //! @param[in] theEdge typed edge definition identifier + //! @return true if any Polygon3D is reachable + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_EdgeId theEdge) const; + + //! Polygon3D handle (cache first, persistent fallback). + //! @param[in] theEdge typed edge definition identifier + //! @return polygon-3D handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& Polygon3D( + const BRepGraph_EdgeId theEdge) const; + + private: + friend class EffectiveView; + + explicit EdgeOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + class CoEdgeOps + { + public: + //! True if the coedge has any polygon-2D / polygon-on-tri in either source. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return true if any coedge mesh data is reachable + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_CoEdgeId theCoEdge) const; + + //! True if a polygon-on-surface (2D) is reachable in either source. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return true if a polygon-2D is bound on either side + [[nodiscard]] Standard_EXPORT bool HasPolygonOnSurface( + const BRepGraph_CoEdgeId theCoEdge) const; + + //! Polygon-on-surface (2D) handle (cache first, persistent fallback). + //! @param[in] theCoEdge typed coedge definition identifier + //! @return polygon-2D handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& PolygonOnSurface( + const BRepGraph_CoEdgeId theCoEdge) const; + + //! True if a polygon-on-triangulation is reachable in either source. + //! @param[in] theCoEdge typed coedge definition identifier + //! @return true if a polygon-on-tri is bound on either side + [[nodiscard]] Standard_EXPORT bool HasPolygonOnTriangulation( + const BRepGraph_CoEdgeId theCoEdge) const; + + //! Polygon-on-triangulation handle (cache first, persistent fallback). + //! @param[in] theCoEdge typed coedge definition identifier + //! @return polygon-on-tri handle, or null handle if absent + [[nodiscard]] Standard_EXPORT const occ::handle& + PolygonOnTriangulation(const BRepGraph_CoEdgeId theCoEdge) const; + + private: + friend class EffectiveView; + + explicit CoEdgeOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + //! Grouped face effective queries. + [[nodiscard]] const FaceOps& Faces() const { return myFaces; } + + //! Grouped edge effective queries. + [[nodiscard]] const EdgeOps& Edges() const { return myEdges; } + + //! Grouped coedge effective queries. + [[nodiscard]] const CoEdgeOps& CoEdges() const { return myCoEdges; } private: friend class MeshView; - explicit CoEdgeOps(const BRepGraph* theGraph) - : myGraph(theGraph) + explicit EffectiveView(BRepGraph* theGraph) + : myFaces(theGraph), + myEdges(theGraph), + myCoEdges(theGraph) { } - [[nodiscard]] bool isFresh(const BRepGraph_CoEdgeId theCoEdge, - const uint32_t theStoredGen) const; - - const BRepGraph* myGraph; + FaceOps myFaces; + EdgeOps myEdges; + CoEdgeOps myCoEdges; }; - //! @brief Polygonal and triangulation representation queries. + //! Cache mutation surface. Mutates the BRepGraphMesh cache only - does not + //! touch persistent definition data. Persistent rep creation/edit lives on + //! `BRepGraph::Editor().Edges()`, `BRepGraph::Editor().CoEdges()`, + //! `BRepGraph::Editor().Faces()`. + class EditorView + { + public: + class FaceOps + { + public: + //! Set the cached triangulation for a face. + //! @param[in] theFace typed face definition identifier + //! @param[in] theTriangulation triangulation to store (null clears) + Standard_EXPORT void SetCachedTriangulation( + const BRepGraph_FaceId theFace, + const occ::handle& theTriangulation); + + //! Clear the face's cached mesh entry (no effect if absent). + //! @param[in] theFace typed face definition identifier + Standard_EXPORT void Clear(const BRepGraph_FaceId theFace); + + private: + friend class EditorView; + + explicit FaceOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + class EdgeOps + { + public: + //! Bind a Polygon3D to the edge's cached entry. + //! @param[in] theEdge typed edge definition identifier + //! @param[in] thePolygon3D polygon-3D handle (null clears the cached binding) + Standard_EXPORT void SetCachedPolygon3D(const BRepGraph_EdgeId theEdge, + const occ::handle& thePolygon3D); + + //! Clear the edge's cached mesh entry. + //! @param[in] theEdge typed edge definition identifier + Standard_EXPORT void Clear(const BRepGraph_EdgeId theEdge); + + private: + friend class EditorView; + + explicit EdgeOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + class CoEdgeOps + { + public: + //! Append a polygon-on-triangulation to the coedge's cached list. + //! @param[in] theCoEdge typed coedge definition identifier + //! @param[in] thePolygonOnTri polygon-on-tri to append + Standard_EXPORT void AppendCachedPolygonOnTri( + const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& thePolygonOnTri); + + //! Bind a polygon-2D to the coedge's cached entry. + //! @param[in] theCoEdge typed coedge definition identifier + //! @param[in] thePolygon2D polygon-2D handle (null clears the cached binding) + Standard_EXPORT void SetCachedPolygon2D(const BRepGraph_CoEdgeId theCoEdge, + const occ::handle& thePolygon2D); + + //! Clear the coedge's cached mesh entry. + //! @param[in] theCoEdge typed coedge definition identifier + Standard_EXPORT void Clear(const BRepGraph_CoEdgeId theCoEdge); + + private: + friend class EditorView; + + explicit CoEdgeOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; + }; + + //! Grouped face cache mutations. + [[nodiscard]] FaceOps& Faces() { return myFaces; } + + //! Grouped edge cache mutations. + [[nodiscard]] EdgeOps& Edges() { return myEdges; } + + //! Grouped coedge cache mutations. + [[nodiscard]] CoEdgeOps& CoEdges() { return myCoEdges; } + + //! Promote all currently fresh default-slot cache mesh entries to persistent mesh reps. + Standard_EXPORT void PromoteToPersistent(); + + private: + friend class MeshView; + + explicit EditorView(BRepGraph* theGraph) + : myGraph(theGraph), + myFaces(theGraph), + myEdges(theGraph), + myCoEdges(theGraph) + { + } + + BRepGraph* myGraph; + FaceOps myFaces; + EdgeOps myEdges; + CoEdgeOps myCoEdges; + }; + + //! @brief Polygonal and triangulation count queries. class PolyOps { public: - [[nodiscard]] Standard_EXPORT int NbTriangulations() const; - [[nodiscard]] Standard_EXPORT int NbPolygons3D() const; - [[nodiscard]] Standard_EXPORT int NbPolygons2D() const; - [[nodiscard]] Standard_EXPORT int NbPolygonsOnTri() const; + //! Total number of face triangulation slots (including removed). + [[nodiscard]] Standard_EXPORT uint32_t NbFaceTriangulations() const; + //! Total number of edge polygon-3D slots (including removed). + [[nodiscard]] Standard_EXPORT uint32_t NbEdgePolygons3D() const; + //! Total number of coedge polygon-2D slots (including removed). + [[nodiscard]] Standard_EXPORT uint32_t NbCoEdgePolygons2D() const; + //! Total number of coedge polygon-on-triangulation slots (including removed). + [[nodiscard]] Standard_EXPORT uint32_t NbCoEdgePolygonsOnTri() const; - [[nodiscard]] Standard_EXPORT int NbActiveTriangulations() const; - [[nodiscard]] Standard_EXPORT int NbActivePolygons3D() const; - [[nodiscard]] Standard_EXPORT int NbActivePolygons2D() const; - [[nodiscard]] Standard_EXPORT int NbActivePolygonsOnTri() const; - - [[nodiscard]] Standard_EXPORT const BRepGraphInc::TriangulationRep& TriangulationRep( - const BRepGraph_TriangulationRepId theRep) const; - [[nodiscard]] Standard_EXPORT const BRepGraphInc::Polygon3DRep& Polygon3DRep( - const BRepGraph_Polygon3DRepId theRep) const; - [[nodiscard]] Standard_EXPORT const BRepGraphInc::Polygon2DRep& Polygon2DRep( - const BRepGraph_Polygon2DRepId theRep) const; - [[nodiscard]] Standard_EXPORT const BRepGraphInc::PolygonOnTriRep& PolygonOnTriRep( - const BRepGraph_PolygonOnTriRepId theRep) const; + //! Number of non-removed face triangulation entries. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveTriangulations() const; + //! Number of non-removed edge polygon-3D entries. + [[nodiscard]] Standard_EXPORT uint32_t NbActivePolygons3D() const; + //! Number of non-removed coedge polygon-2D entries. + [[nodiscard]] Standard_EXPORT uint32_t NbActivePolygons2D() const; + //! Number of non-removed coedge polygon-on-triangulation entries. + [[nodiscard]] Standard_EXPORT uint32_t NbActivePolygonsOnTri() const; private: friend class MeshView; - explicit PolyOps(const BRepGraph* theGraph) + explicit PolyOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; - //! Grouped face mesh queries. - [[nodiscard]] const FaceOps& Faces() const { return myFaces; } + //! Cache-only reads. + [[nodiscard]] const CacheView& Cache() const { return myCache; } - //! Grouped edge mesh queries. - [[nodiscard]] const EdgeOps& Edges() const { return myEdges; } + //! Persistent (definition-resident) reads. + [[nodiscard]] const PersistentView& Persistent() const { return myPersistent; } - //! Grouped coedge mesh queries. - [[nodiscard]] const CoEdgeOps& CoEdges() const { return myCoEdges; } + //! Effective reads - cache first, persistent fallback. Use when source is irrelevant. + [[nodiscard]] const EffectiveView& Effective() const { return myEffective; } - //! Grouped polygonal representation queries. + //! Cache mutations. + [[nodiscard]] EditorView& Editor() { return myEditor; } + + //! Polygon/triangulation count queries. [[nodiscard]] const PolyOps& Poly() const { return myPoly; } private: friend class BRepGraph; friend struct BRepGraph_Data; - explicit MeshView(const BRepGraph* theGraph) + explicit MeshView(BRepGraph* theGraph) : myGraph(theGraph), - myFaces(theGraph), - myEdges(theGraph), - myCoEdges(theGraph), - myPoly(theGraph) + myPoly(theGraph), + myCache(theGraph), + myPersistent(theGraph), + myEffective(theGraph), + myEditor(theGraph) { } - const BRepGraph* myGraph; - FaceOps myFaces; - EdgeOps myEdges; - CoEdgeOps myCoEdges; - PolyOps myPoly; + BRepGraph* myGraph; + PolyOps myPoly; + CacheView myCache; + PersistentView myPersistent; + EffectiveView myEffective; + EditorView myEditor; }; #endif // _BRepGraph_MeshView_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MutGuard.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MutGuard.hxx index 8857bddf89..4417a0455f 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MutGuard.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_MutGuard.hxx @@ -14,47 +14,46 @@ #ifndef _BRepGraph_MutGuard_HeaderFile #define _BRepGraph_MutGuard_HeaderFile +#include #include #include -#include +#include #include #include -class BRepGraph; - //! @brief RAII scope token batching mutation notifications for a single entity. //! //! Obtained via BRepGraph::Editor().().Mut() / MutRef() / MutSurface() etc. //! Reads via `operator->()` / `operator*()`; writes via Editor's typed setters //! (or `Internal()` for in-tree structural remaps). Any call to `Internal()` //! flags the guard dirty and the destructor fires `markModified` / -//! `markRefModified` / `markRepModified` once on scope exit. +//! `markRefModified` once on scope exit. //! -//! Move-only; non-copyable. After a move, the source guard becomes inert. +//! The guard registers itself as active on the guarded item at construction +//! and deregisters on destruction. This prevents double-mutation: attempting +//! to acquire a second guard on the same item while the first is still alive +//! will throw. Move-only; after a move, the source guard becomes inert and +//! does not deregister. //! //! Compile-time dispatch selects the ID type and notification method: //! - For types derived from BRepGraphInc::BaseDef: BRepGraph_NodeId + markModified() //! - For types derived from BRepGraphInc::BaseRef: BRepGraph_RefId + markRefModified() -//! - For types derived from BRepGraphInc::BaseRep: BRepGraph_RepId + markRepModified() //! //! @code //! { //! BRepGraph_MutGuard anEdge = //! theGraph.Editor().Edges().Mut(BRepGraph_EdgeId(42)); -//! theGraph.Editor().Edges().SetTolerance (anEdge, 0.5); -//! theGraph.Editor().Edges().SetSameParameter (anEdge, true); -//! } // markModified called once here +//! theGraph.Editor().Edges().SetTolerance(anEdge, 0.5); +//! } // markModified called once here, guard deregistered //! @endcode template class BRepGraph_MutGuard { static_assert(std::is_base_of_v - || std::is_base_of_v - || std::is_base_of_v, - "BRepGraph_MutGuard: T must derive from BaseDef, BaseRef, or BaseRep"); + || std::is_base_of_v, + "BRepGraph_MutGuard: T must derive from BaseDef or BaseRef"); - //! Entity-provided identifier alias. using TypeId = typename T::TypeId; //! Call the appropriate notification method on the graph. @@ -63,11 +62,13 @@ class BRepGraph_MutGuard try { if constexpr (std::is_base_of_v) + { myGraph->markModified(myId, *myEntity); + } else if constexpr (std::is_base_of_v) - myGraph->markRefModified(myId, *myEntity); - else - myGraph->markRepModified(myId); + { + myGraph->markRefModified(myId); + } } catch (...) { @@ -76,51 +77,81 @@ class BRepGraph_MutGuard public: //! Construct a guard over a mutable entity. - //! @param[in] theGraph owning graph (used for notification on destruction) - //! @param[in] theEntity pointer to the mutable entity - //! @param[in] theId identity for notification - BRepGraph_MutGuard(BRepGraph* theGraph, T* theEntity, const TypeId theId) - : myGraph(theGraph), + //! Registers the item via the storage bit-plane. The Mut() factory pre-validates + //! that no guard is active, so this assertion should never fire in normal use. + //! @param[in] theGraph owning graph (used for notification) + //! @param[in] theStorage storage instance (for bit-plane guard tracking) + //! @param[in] theEntity pointer to the mutable entity + //! @param[in] theId identity for notification and guard registration + BRepGraph_MutGuard(BRepGraph& theGraph, + BRepGraphInc_Storage& theStorage, + T* theEntity, + const TypeId theId) + : myGraph(&theGraph), + myStorage(&theStorage), myEntity(theEntity), myId(theId), myDirty(false) { + Standard_ProgramError_Raise_if(myStorage->IsGuarded(BRepGraph_ItemId(myId)), + "BRepGraph_MutGuard: guard already active on this item"); + myStorage->SetGuarded(BRepGraph_ItemId(myId)); } - //! Destructor: notifies the graph if the guard owns an entity AND - //! at least one setter (or `MarkDirty`) flagged it modified. + //! Destructor: clears the guard bit-plane and notifies the graph if the + //! guard owns an entity AND at least one setter (or `MarkDirty`) flagged it modified. + //! Guard clearance happens BEFORE notification so that markModified() propagation + //! does not see a stale guard registration on the same item. ~BRepGraph_MutGuard() { - if (myGraph != nullptr && myDirty) + if (myEntity != nullptr) { - notify(); + myStorage->ClearGuarded(BRepGraph_ItemId(myId)); + if (myDirty) + { + notify(); + } } } + //! Move constructor: transfers guard ownership. The source becomes inert + //! and will not deregister on its destruction. BRepGraph_MutGuard(BRepGraph_MutGuard&& theOther) noexcept : myGraph(theOther.myGraph), + myStorage(theOther.myStorage), myEntity(theOther.myEntity), myId(theOther.myId), myDirty(theOther.myDirty) { - theOther.myGraph = nullptr; - theOther.myEntity = nullptr; - theOther.myDirty = false; + theOther.myEntity = nullptr; + theOther.myDirty = false; + theOther.myGraph = nullptr; + theOther.myStorage = nullptr; } + //! Move assignment: deregisters current guard (if active), then transfers + //! ownership from the source. The source becomes inert. BRepGraph_MutGuard& operator=(BRepGraph_MutGuard&& theOther) noexcept { if (this != &theOther) { - if (myGraph != nullptr && myDirty) - notify(); - myGraph = theOther.myGraph; - myEntity = theOther.myEntity; - myId = theOther.myId; - myDirty = theOther.myDirty; - theOther.myGraph = nullptr; - theOther.myEntity = nullptr; - theOther.myDirty = false; + if (myEntity != nullptr) + { + myStorage->ClearGuarded(BRepGraph_ItemId(myId)); + if (myDirty) + { + notify(); + } + } + myGraph = theOther.myGraph; + myStorage = theOther.myStorage; + myEntity = theOther.myEntity; + myId = theOther.myId; + myDirty = theOther.myDirty; + theOther.myEntity = nullptr; + theOther.myDirty = false; + theOther.myGraph = nullptr; + theOther.myStorage = nullptr; } return *this; } @@ -153,8 +184,8 @@ public: //! Identity for notification. [[nodiscard]] TypeId Id() const noexcept { return myId; } - //! Owning graph pointer (nullptr after move). - [[nodiscard]] BRepGraph* Graph() const noexcept { return myGraph; } + //! Owning graph handle. + [[nodiscard]] BRepGraph& Graph() const { return *myGraph; } //! Flag the guarded entity as modified without writing through `Internal()`. //! Use when an external mutation (e.g. in-place geometry transform on a shared @@ -173,10 +204,11 @@ public: } private: - BRepGraph* myGraph; //!< Owning graph (nullptr after move). - T* myEntity; //!< Mutable entity pointer; access via Editor setters. - TypeId myId; //!< Identity for notification. - bool myDirty; //!< True once a setter has modified the entity in this scope. + BRepGraph* myGraph; //!< Owning graph, non-owning. + BRepGraphInc_Storage* myStorage; //!< Storage instance for bit-plane guard tracking, non-owning. + T* myEntity; //!< Mutable entity pointer; access via Editor setters. + TypeId myId; //!< Identity for notification and guard registration. + bool myDirty; //!< True once a setter has modified the entity in this scope. }; #endif // _BRepGraph_MutGuard_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_NodeId.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_NodeId.hxx index acc73de388..5fa40031a4 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_NodeId.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_NodeId.hxx @@ -23,6 +23,8 @@ #include #include +class BRepGraph; + //! Lightweight typed index into a per-kind node vector inside BRepGraph. //! //! The pair (NodeKind, Index) forms a unique node identifier within one graph @@ -54,11 +56,32 @@ struct BRepGraph_NodeId Occurrence = 11 //!< Placed instance of a product within a parent product }; + //! True if the kind value is one of the supported node kinds. + static bool IsValidKind(const Kind theKind) + { + switch (theKind) + { + case Kind::Solid: + case Kind::Shell: + case Kind::Face: + case Kind::Wire: + case Kind::Edge: + case Kind::Vertex: + case Kind::Compound: + case Kind::CompSolid: + case Kind::CoEdge: + case Kind::Product: + case Kind::Occurrence: + return true; + } + return false; + } + //! @brief Compile-time typed wrapper around BRepGraph_NodeId. //! //! Provides compile-time kind safety: a Typed //! cannot be accidentally used where a Typed is expected. - //! Implicitly converts to BRepGraph_NodeId for backward compatibility. + //! Implicitly converts to BRepGraph_NodeId for API continuity. //! //! @tparam TheKind the BRepGraph_NodeId::Kind this typed id represents template @@ -97,11 +120,17 @@ struct BRepGraph_NodeId [[nodiscard]] static Typed Invalid() { return Typed(); } //! True if this id points to an allocated node slot. - [[nodiscard]] bool IsValid() const { return Index != THE_INVALID_INDEX; } + [[nodiscard]] bool IsValid() const + { + return BRepGraph_NodeId::IsValidKind(TheKind) && Index != THE_INVALID_INDEX; + } //! True if this id points to an allocated slot within [0, theMaxCount). //! UINT32_MAX (invalid sentinel) always fails this check for any realistic count. - [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const { return Index < theMaxCount; } + [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const + { + return IsValid() && Index < theMaxCount; + } //! True if this id is within the dense range exposed by a provider with Nb(). template @@ -127,7 +156,11 @@ struct BRepGraph_NodeId //! @param[in] theId untyped NodeId to convert static Typed FromNodeId(const BRepGraph_NodeId theId) { - Standard_ASSERT_VOID(theId.NodeKind == TheKind, "NodeId kind mismatch"); + Standard_ASSERT_RETURN(theId.NodeKind == TheKind, "NodeId kind mismatch", Typed()); + if (!theId.IsValid()) + { + return Typed(); + } return Typed(theId.Index); } @@ -192,15 +225,30 @@ struct BRepGraph_NodeId { return theRhs != theLhs; } + + //! Return true if this node has been soft-removed in the given graph. + [[nodiscard]] bool IsRemoved(const BRepGraph& theGraph) const + { + return BRepGraph_NodeId(*this).IsRemoved(theGraph); + } + + //! Return true if this node has an active owner in the given graph. + [[nodiscard]] bool IsOwned(const BRepGraph& theGraph) const + { + return BRepGraph_NodeId(*this).IsOwned(theGraph); + } }; //! True if the kind is a core topology kind (Solid..CoEdge). - static bool IsTopologyKind(const Kind theKind) { return static_cast(theKind) <= 8; } + static bool IsTopologyKind(const Kind theKind) + { + return IsValidKind(theKind) && theKind >= Kind::Solid && theKind <= Kind::CoEdge; + } //! True if the kind is an assembly kind (Product or Occurrence). static bool IsAssemblyKind(const Kind theKind) { - return theKind == Kind::Product || theKind == Kind::Occurrence; + return IsValidKind(theKind) && (theKind == Kind::Product || theKind == Kind::Occurrence); } //! Total number of dense kind slots used by per-kind arrays. @@ -239,11 +287,14 @@ struct BRepGraph_NodeId } //! True if this id points to an allocated node slot. - [[nodiscard]] bool IsValid() const { return Index != THE_INVALID_INDEX; } + [[nodiscard]] bool IsValid() const { return IsValidKind(NodeKind) && Index != THE_INVALID_INDEX; } //! True if this id points to an allocated slot within [0, theMaxCount). //! UINT32_MAX (invalid sentinel) always fails this check for any realistic count. - [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const { return Index < theMaxCount; } + [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const + { + return IsValid() && Index < theMaxCount; + } //! True if this id is within the dense range exposed by a provider with Nb(). template @@ -271,7 +322,9 @@ struct BRepGraph_NodeId bool operator<(const BRepGraph_NodeId& theOther) const { if (NodeKind != theOther.NodeKind) + { return static_cast(NodeKind) < static_cast(theOther.NodeKind); + } return Index < theOther.Index; } @@ -340,6 +393,12 @@ struct BRepGraph_NodeId Standard_ASSERT_VOID(false, "BRepGraph_NodeId::Visit: unhandled Kind"); return std::forward(theFunc)(Typed()); } + + //! Return true if this node has been soft-removed in the given graph. + [[nodiscard]] Standard_EXPORT bool IsRemoved(const BRepGraph& theGraph) const; + + //! Return true if this node has an active owner in the given graph. + [[nodiscard]] Standard_EXPORT bool IsOwned(const BRepGraph& theGraph) const; }; // Convenience type aliases for typed NodeIds. diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParallelPolicy.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParallelPolicy.cxx new file mode 100644 index 0000000000..86866290c5 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParallelPolicy.cxx @@ -0,0 +1,45 @@ +// 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 + +//================================================================================================= + +bool BRepGraph_ParallelPolicy::ShouldRun(const bool theAllowParallel, + const int theWorkers, + const Workload& theWorkload) +{ + if (!theAllowParallel || theWorkers <= 1) + { + return false; + } + + const int aPrimaryItems = static_cast(std::max(theWorkload.PrimaryItems, 0u)); + if (aPrimaryItems <= 1) + { + return false; + } + + if (aPrimaryItems <= theWorkers) + { + return false; + } + + const int64_t aTotalWorkUnits = + static_cast(aPrimaryItems) + + static_cast(std::max(theWorkload.AuxiliaryItems, 0u)) + + static_cast(std::max(theWorkload.InteractionCount, 0u)); + const int64_t aRequiredWorkUnits = + static_cast(theWorkers) * static_cast(theWorkers); + return aTotalWorkUnits > aRequiredWorkUnits; +} \ No newline at end of file diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParallelPolicy.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParallelPolicy.hxx index 1523f4fbf1..56fb23ae5b 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParallelPolicy.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParallelPolicy.hxx @@ -31,9 +31,9 @@ public: //! Simple workload estimate for an execution phase. struct Workload { - int PrimaryItems = 0; //!< Main loop range. - int AuxiliaryItems = 0; //!< Additional independent items participating in the phase. - int InteractionCount = 0; //!< Pairwise or adjacency work discovered for the phase. + uint32_t PrimaryItems = 0; //!< Main loop range. + uint32_t AuxiliaryItems = 0; //!< Additional independent items participating in the phase. + uint32_t InteractionCount = 0; //!< Pairwise or adjacency work discovered for the phase. }; //! Return the effective logical worker count reported by OSD_Parallel. @@ -51,36 +51,18 @@ public: //! Decide whether the estimated workload is large enough to amortize //! thread-pool launch and synchronization overhead. - [[nodiscard]] static bool ShouldRun(const bool theAllowParallel, - const int theWorkers, - const Workload& theWorkload) - { - if (!theAllowParallel || theWorkers <= 1) - { - return false; - } - - const int aPrimaryItems = std::max(theWorkload.PrimaryItems, 0); - if (aPrimaryItems <= 1) - { - return false; - } - - if (aPrimaryItems <= theWorkers) - { - return false; - } - - const int64_t aTotalWorkUnits = - static_cast(aPrimaryItems) - + static_cast(std::max(theWorkload.AuxiliaryItems, 0)) - + static_cast(std::max(theWorkload.InteractionCount, 0)); - const int64_t aRequiredWorkUnits = - static_cast(theWorkers) * static_cast(theWorkers); - return aTotalWorkUnits > aRequiredWorkUnits; - } + //! @param[in] theAllowParallel whether parallel mode is allowed by the caller + //! @param[in] theWorkers effective logical worker count + //! @param[in] theWorkload estimated workload for the phase + //! @return true if parallel execution should be used + [[nodiscard]] Standard_EXPORT static bool ShouldRun(const bool theAllowParallel, + const int theWorkers, + const Workload& theWorkload); //! Overload that queries the active worker count lazily. + //! @param[in] theAllowParallel whether parallel mode is allowed by the caller + //! @param[in] theWorkload estimated workload for the phase + //! @return true if parallel execution should be used [[nodiscard]] static bool ShouldRun(const bool theAllowParallel, const Workload& theWorkload) { return ShouldRun(theAllowParallel, WorkerCount(), theWorkload); diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.cxx index d8d6736618..daa147c110 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.cxx @@ -18,30 +18,69 @@ #include #include #include - +#include #include #include namespace { -static int parentExplorerKindDepth(const BRepGraph_NodeId::Kind theKind) +static bool parentExplorerKindCanContainDescendant(const BRepGraph_NodeId::Kind theParentKind, + const BRepGraph_NodeId::Kind theTargetKind) { - static constexpr int THE_DEPTH[] = { - 2, // Kind::Solid=0 - 3, // Kind::Shell=1 - 4, // Kind::Face=2 - 5, // Kind::Wire=3 - 7, // Kind::Edge=4 - 8, // Kind::Vertex=5 - 0, // Kind::Compound=6 - 1, // Kind::CompSolid=7 - 6, // Kind::CoEdge=8 - 99, // gap=9 - 0, // Kind::Product=10 - 1, // Kind::Occurrence=11 - }; - return THE_DEPTH[static_cast(theKind)]; + using Kind = BRepGraph_NodeId::Kind; + switch (theParentKind) + { + case Kind::Product: + return theTargetKind == Kind::Occurrence || theTargetKind == Kind::Product + || BRepGraph_NodeId::IsTopologyKind(theTargetKind); + case Kind::Occurrence: + return theTargetKind == Kind::Occurrence || theTargetKind == Kind::Product + || BRepGraph_NodeId::IsTopologyKind(theTargetKind); + case Kind::Compound: + return BRepGraph_NodeId::IsTopologyKind(theTargetKind); + case Kind::CompSolid: + return theTargetKind == Kind::Solid || theTargetKind == Kind::Shell + || theTargetKind == Kind::Face || theTargetKind == Kind::Wire + || theTargetKind == Kind::CoEdge || theTargetKind == Kind::Edge + || theTargetKind == Kind::Vertex; + case Kind::Solid: + return theTargetKind == Kind::Shell || theTargetKind == Kind::Face + || theTargetKind == Kind::Wire || theTargetKind == Kind::CoEdge + || theTargetKind == Kind::Edge || theTargetKind == Kind::Vertex; + case Kind::Shell: + return theTargetKind == Kind::Face || theTargetKind == Kind::Wire + || theTargetKind == Kind::CoEdge || theTargetKind == Kind::Edge + || theTargetKind == Kind::Vertex; + case Kind::Face: + return theTargetKind == Kind::Wire || theTargetKind == Kind::CoEdge + || theTargetKind == Kind::Edge || theTargetKind == Kind::Vertex; + case Kind::Wire: + return theTargetKind == Kind::CoEdge || theTargetKind == Kind::Edge + || theTargetKind == Kind::Vertex; + case Kind::CoEdge: + return theTargetKind == Kind::Edge || theTargetKind == Kind::Vertex; + case Kind::Edge: + return theTargetKind == Kind::Vertex; + case Kind::Vertex: + return false; + } + return false; +} + +template +static bool nthParentId(IteratorT theIterator, uint32_t& theIndex, IdT& theId) +{ + for (; theIterator.More(); theIterator.Next()) + { + if (theIndex == 0u) + { + theId = theIterator.CurrentId(); + return true; + } + --theIndex; + } + return false; } } // namespace @@ -184,8 +223,18 @@ BRepGraph_RefId BRepGraph_ParentExplorer::CurrentRef() const return BRepGraph_RefId(); } - return myGraph->Refs().RefAtStep(myStack[myCurrentFrame].Node, - myStack[myCurrentFrame].StepToChild); + const StackFrame& aFrame = myStack[myCurrentFrame]; + if (aFrame.StepToChild < 0) + { + return BRepGraph_RefId(); + } + + if (aFrame.RefToChild.IsValid()) + { + return aFrame.RefToChild; + } + + return myGraph->Refs().Gen().RefAtStep(aFrame.Node, aFrame.StepToChild); } //================================================================================================= @@ -364,6 +413,11 @@ std::optional BRepGraph_ParentExplorer::normalizeAvoidKi return theAvoidKind; } + if (*theAvoidKind == *theTargetKind) + { + return std::nullopt; + } + if (!canContainTarget(*theTargetKind, *theAvoidKind)) { return std::nullopt; @@ -379,7 +433,7 @@ std::optional BRepGraph_ParentExplorer::normalizeAvoidKi bool BRepGraph_ParentExplorer::canContainTarget(const BRepGraph_NodeId::Kind theParentKind, const BRepGraph_NodeId::Kind theTargetKind) { - return parentExplorerKindDepth(theParentKind) < parentExplorerKindDepth(theTargetKind); + return parentExplorerKindCanContainDescendant(theParentKind, theTargetKind); } //================================================================================================= @@ -398,412 +452,176 @@ bool BRepGraph_ParentExplorer::nextParentFrame(StackFrame& theChild, StackFrame& { case Kind::Vertex: { const BRepGraph_VertexId aVertexId(theChild.Node); - const BRepGraph_EdgesOfVertex aParents(*myGraph, aTopo.Vertices().Edges(aVertexId)); - if (aParentIdx >= aParents.Size()) + const BRepGraph_EdgesOfVertex anEdges(*myGraph, aTopo.Vertices().Edges(aVertexId)); + const uint32_t aNbEdges = static_cast(anEdges.Size()); + if (aParentIdx < aNbEdges) { - return false; + const BRepGraph_EdgeId anEdgeId = anEdges.Value(aParentIdx); + if (anEdgeId.IsRemoved(*myGraph)) + { + continue; + } + const int aStepToChild = findEdgeVertexStep(anEdgeId, aVertexId); + if (aStepToChild < 0) + { + continue; + } + theParent.Node = anEdgeId; + theParent.NextParentIdx = 0; + theParent.StepToChild = aStepToChild; + return true; } - - const BRepGraph_EdgeId anEdgeId = aParents.Value(aParentIdx); - const BRepGraphInc::EdgeDef& anEdge = aTopo.Edges().Definition(anEdgeId); - if (anEdge.IsRemoved) - { - continue; - } - - const int aStepToChild = findEdgeVertexStep(anEdgeId, aVertexId); - if (aStepToChild < 0) - { - continue; - } - - theParent.Node = anEdgeId; - theParent.NextParentIdx = 0; - theParent.StepToChild = aStepToChild; - return true; + return nextCompoundOrOccurrenceParent(theChild.Node, aParentIdx - aNbEdges, theParent); } case Kind::Edge: { const BRepGraph_EdgeId aEdgeId(theChild.Node); - const BRepGraph_CoEdgesOfEdge aParents(*myGraph, aTopo.Edges().CoEdges(aEdgeId)); - if (aParentIdx >= static_cast(aParents.Size())) + const BRepGraph_CoEdgesOfEdge aCoEdges(*myGraph, aTopo.Edges().CoEdges(aEdgeId)); + const uint32_t aNbCoEdges = static_cast(aCoEdges.Size()); + if (aParentIdx < aNbCoEdges) { - BRepGraph_ProductId aProductId; - if (!findNthProductWrapper(theChild.Node, - aParentIdx - static_cast(aParents.Size()), - aProductId)) + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdges.Value(aParentIdx); + if (BRepGraph_NodeId(aCoEdgeId).IsRemoved(*myGraph)) { - return false; + continue; } - - theParent.Node = aProductId; + theParent.Node = aCoEdgeId; theParent.NextParentIdx = 0; theParent.StepToChild = -1; return true; } - - const BRepGraph_CoEdgeId aCoEdgeId = aParents.Value(aParentIdx); - const BRepGraphInc::CoEdgeDef& aCoEdge = aTopo.CoEdges().Definition(aCoEdgeId); - if (aCoEdge.IsRemoved) - { - continue; - } - - theParent.Node = aCoEdgeId; - theParent.NextParentIdx = 0; - theParent.StepToChild = -1; - return true; + return nextCompoundOrOccurrenceParent(theChild.Node, aParentIdx - aNbCoEdges, theParent); } case Kind::Wire: { const BRepGraph_WireId aWireId(theChild.Node); - const BRepGraph_FacesOfWire aParents(*myGraph, aTopo.Wires().Faces(aWireId)); - if (aParentIdx >= static_cast(aParents.Size())) + const BRepGraph_FacesOfWire aFaces(*myGraph, + aTopo.Wires().Relations(aWireId).ParentWireRefIds); + uint32_t aRemainingParentIdx = aParentIdx; + BRepGraph_FaceId aFaceId; + if (nthParentId(aFaces, aRemainingParentIdx, aFaceId)) { - BRepGraph_ProductId aProductId; - if (!findNthProductWrapper(theChild.Node, - aParentIdx - static_cast(aParents.Size()), - aProductId)) - { - return false; - } - - theParent.Node = aProductId; - theParent.NextParentIdx = 0; - theParent.StepToChild = -1; - return true; - } - - const BRepGraph_FaceId aFaceId = aParents.Value(aParentIdx); - const BRepGraphInc::FaceDef& aFace = aTopo.Faces().Definition(aFaceId); - if (aFace.IsRemoved) - { - continue; - } - - const int aStepToChild = findFaceChildStep(aFaceId, theChild.Node); - if (aStepToChild < 0) - { - continue; - } - - theParent.Node = aFaceId; - theParent.NextParentIdx = 0; - theParent.StepToChild = aStepToChild; - return true; - } - - case Kind::Face: { - const BRepGraph_FaceId aFaceId(theChild.Node); - const BRepGraph_ShellsOfFace aShells(*myGraph, aTopo.Faces().Shells(aFaceId)); - const BRepGraph_CompoundsOfFace aCompounds(*myGraph, aTopo.Faces().Compounds(aFaceId)); - const uint32_t aNbShells = static_cast(aShells.Size()); - const uint32_t aNbCompounds = static_cast(aCompounds.Size()); - if (aParentIdx < aNbShells) - { - const BRepGraph_ShellId aShellId = aShells.Value(aParentIdx); - const BRepGraphInc::ShellDef& aShell = aTopo.Shells().Definition(aShellId); - if (aShell.IsRemoved) + const int aStepToChild = findFaceChildStep(aFaceId, theChild.Node); + if (aStepToChild < 0) { continue; } + theParent.Node = aFaceId; + theParent.NextParentIdx = 0; + theParent.StepToChild = aStepToChild; + return true; + } + return nextCompoundOrOccurrenceParent(theChild.Node, aRemainingParentIdx, theParent); + } + case Kind::Face: { + const BRepGraph_FaceId aFaceId(theChild.Node); + const BRepGraph_ShellsOfFace aShells(*myGraph, + aTopo.Faces().Relations(aFaceId).ParentFaceRefIds); + uint32_t aRemainingParentIdx = aParentIdx; + BRepGraph_ShellId aShellId; + if (nthParentId(aShells, aRemainingParentIdx, aShellId)) + { const int aStepToChild = findShellChildStep(aShellId, theChild.Node); if (aStepToChild < 0) { continue; } - theParent.Node = aShellId; theParent.NextParentIdx = 0; theParent.StepToChild = aStepToChild; return true; } - if (aParentIdx < aNbShells + aNbCompounds) - { - const BRepGraph_CompoundId aCompoundId = aCompounds.Value(aParentIdx - aNbShells); - const BRepGraphInc::CompoundDef& aCompound = aTopo.Compounds().Definition(aCompoundId); - if (aCompound.IsRemoved) - { - continue; - } - - const int aStepToChild = findCompoundChildStep(aCompoundId, theChild.Node); - if (aStepToChild < 0) - { - continue; - } - - theParent.Node = aCompoundId; - theParent.NextParentIdx = 0; - theParent.StepToChild = aStepToChild; - return true; - } - - BRepGraph_ProductId aProductId; - if (!findNthProductWrapper(theChild.Node, - aParentIdx - aNbShells - aNbCompounds, - aProductId)) - { - return false; - } - - theParent.Node = aProductId; - theParent.NextParentIdx = 0; - theParent.StepToChild = -1; - return true; + return nextCompoundOrOccurrenceParent(theChild.Node, aRemainingParentIdx, theParent); } case Kind::Shell: { - const BRepGraph_ShellId aShellId = BRepGraph_ShellId(theChild.Node); - const BRepGraph_SolidsOfShell aSolids(*myGraph, aTopo.Shells().Solids(aShellId)); - const BRepGraph_CompoundsOfShell aCompounds(*myGraph, aTopo.Shells().Compounds(aShellId)); - const uint32_t aNbSolids = static_cast(aSolids.Size()); - const uint32_t aNbCompounds = static_cast(aCompounds.Size()); - if (aParentIdx < aNbSolids) + const BRepGraph_ShellId aShellId = BRepGraph_ShellId(theChild.Node); + const BRepGraph_SolidsOfShell aSolids(*myGraph, + aTopo.Shells().Relations(aShellId).ParentShellRefIds); + uint32_t aRemainingParentIdx = aParentIdx; + BRepGraph_SolidId aSolidId; + if (nthParentId(aSolids, aRemainingParentIdx, aSolidId)) { - const BRepGraph_SolidId aSolidId = aSolids.Value(aParentIdx); - const BRepGraphInc::SolidDef& aSolid = aTopo.Solids().Definition(aSolidId); - if (aSolid.IsRemoved) - { - continue; - } - const int aStepToChild = findSolidChildStep(aSolidId, theChild.Node); if (aStepToChild < 0) { continue; } - theParent.Node = aSolidId; theParent.NextParentIdx = 0; theParent.StepToChild = aStepToChild; return true; } - if (aParentIdx < aNbSolids + aNbCompounds) - { - const BRepGraph_CompoundId aCompoundId = aCompounds.Value(aParentIdx - aNbSolids); - const BRepGraphInc::CompoundDef& aCompound = aTopo.Compounds().Definition(aCompoundId); - if (aCompound.IsRemoved) - { - continue; - } - - const int aStepToChild = findCompoundChildStep(aCompoundId, theChild.Node); - if (aStepToChild < 0) - { - continue; - } - - theParent.Node = aCompoundId; - theParent.NextParentIdx = 0; - theParent.StepToChild = aStepToChild; - return true; - } - - BRepGraph_ProductId aProductId; - if (!findNthProductWrapper(theChild.Node, - aParentIdx - aNbSolids - aNbCompounds, - aProductId)) - { - return false; - } - - theParent.Node = aProductId; - theParent.NextParentIdx = 0; - theParent.StepToChild = -1; - return true; + return nextCompoundOrOccurrenceParent(theChild.Node, aRemainingParentIdx, theParent); } case Kind::Solid: { const BRepGraph_SolidId aSolidId = BRepGraph_SolidId(theChild.Node); - const BRepGraph_CompSolidsOfSolid aCompSolids(*myGraph, - aTopo.Solids().CompSolids(aSolidId)); - const BRepGraph_CompoundsOfSolid aCompounds(*myGraph, aTopo.Solids().Compounds(aSolidId)); - const uint32_t aNbCompSolids = static_cast(aCompSolids.Size()); - const uint32_t aNbCompounds = static_cast(aCompounds.Size()); - if (aParentIdx < aNbCompSolids) + const BRepGraph_CompSolidsOfSolid aCompSolids( + *myGraph, + aTopo.Solids().Relations(aSolidId).ParentSolidRefIds); + uint32_t aRemainingParentIdx = aParentIdx; + BRepGraph_CompSolidId aCompSolidId; + if (nthParentId(aCompSolids, aRemainingParentIdx, aCompSolidId)) { - const BRepGraph_CompSolidId aCompSolidId = aCompSolids.Value(aParentIdx); - const BRepGraphInc::CompSolidDef& aCompSolid = - aTopo.CompSolids().Definition(aCompSolidId); - if (aCompSolid.IsRemoved) - { - continue; - } - const int aStepToChild = findCompSolidSolidStep(aCompSolidId, aSolidId); if (aStepToChild < 0) { continue; } - theParent.Node = aCompSolidId; theParent.NextParentIdx = 0; theParent.StepToChild = aStepToChild; return true; } - if (aParentIdx < aNbCompSolids + aNbCompounds) - { - const BRepGraph_CompoundId aCompoundId = aCompounds.Value(aParentIdx - aNbCompSolids); - const BRepGraphInc::CompoundDef& aCompound = aTopo.Compounds().Definition(aCompoundId); - if (aCompound.IsRemoved) - { - continue; - } - - const int aStepToChild = findCompoundChildStep(aCompoundId, theChild.Node); - if (aStepToChild < 0) - { - continue; - } - - theParent.Node = aCompoundId; - theParent.NextParentIdx = 0; - theParent.StepToChild = aStepToChild; - return true; - } - - BRepGraph_ProductId aProductId; - if (!findNthProductWrapper(theChild.Node, - aParentIdx - aNbCompSolids - aNbCompounds, - aProductId)) - { - return false; - } - - theParent.Node = aProductId; - theParent.NextParentIdx = 0; - theParent.StepToChild = -1; - return true; + return nextCompoundOrOccurrenceParent(theChild.Node, aRemainingParentIdx, theParent); } - case Kind::Compound: { - const BRepGraph_CompoundId aCompoundId(theChild.Node); - const BRepGraph_CompoundsOfCompound aParents( - *myGraph, - aTopo.Compounds().ParentCompounds(aCompoundId)); - const uint32_t aNbParents = static_cast(aParents.Size()); - if (aParentIdx < aNbParents) - { - const BRepGraph_CompoundId aParentCompoundId = aParents.Value(aParentIdx); - const BRepGraphInc::CompoundDef& aParentCompound = - aTopo.Compounds().Definition(aParentCompoundId); - if (aParentCompound.IsRemoved) - { - continue; - } + case Kind::Compound: + return nextCompoundOrOccurrenceParent(theChild.Node, aParentIdx, theParent); - const int aStepToChild = findCompoundChildStep(aParentCompoundId, theChild.Node); - if (aStepToChild < 0) - { - continue; - } - - theParent.Node = aParentCompoundId; - theParent.NextParentIdx = 0; - theParent.StepToChild = aStepToChild; - return true; - } - - BRepGraph_ProductId aProductId; - if (!findNthProductWrapper(theChild.Node, aParentIdx - aNbParents, aProductId)) - { - return false; - } - - theParent.Node = aProductId; - theParent.NextParentIdx = 0; - theParent.StepToChild = -1; - return true; - } - - case Kind::CompSolid: { - const BRepGraph_CompSolidId aCompSolidId(theChild.Node); - const BRepGraph_CompoundsOfCompSolid aParents(*myGraph, - aTopo.CompSolids().Compounds(aCompSolidId)); - const uint32_t aNbParents = static_cast(aParents.Size()); - if (aParentIdx < aNbParents) - { - const BRepGraph_CompoundId aParentCompoundId = aParents.Value(aParentIdx); - const BRepGraphInc::CompoundDef& aParentCompound = - aTopo.Compounds().Definition(aParentCompoundId); - if (aParentCompound.IsRemoved) - { - continue; - } - - const int aStepToChild = findCompoundChildStep(aParentCompoundId, theChild.Node); - if (aStepToChild < 0) - { - continue; - } - - theParent.Node = aParentCompoundId; - theParent.NextParentIdx = 0; - theParent.StepToChild = aStepToChild; - return true; - } - - BRepGraph_ProductId aProductId; - if (!findNthProductWrapper(theChild.Node, aParentIdx - aNbParents, aProductId)) - { - return false; - } - - theParent.Node = aProductId; - theParent.NextParentIdx = 0; - theParent.StepToChild = -1; - return true; - } + case Kind::CompSolid: + return nextCompoundOrOccurrenceParent(theChild.Node, aParentIdx, theParent); case Kind::CoEdge: { - const BRepGraph_CoEdgeId aCoEdgeId(theChild.Node); - const BRepGraph_WiresOfCoEdge aWires(*myGraph, aTopo.CoEdges().Wires(aCoEdgeId)); - const uint32_t aNbWires = static_cast(aWires.Size()); - if (aParentIdx >= aNbWires) + const BRepGraph_CoEdgeId aCoEdgeId(theChild.Node); + const BRepGraph_WireId aWireId = aTopo.CoEdges().Wire(aCoEdgeId); + const uint32_t aNbWires = aWireId.IsValid() ? 1u : 0u; + if (aParentIdx < aNbWires) { - return false; + if (aWireId.IsRemoved(*myGraph)) + { + continue; + } + const int aStepToChild = findWireCoEdgeStep(aWireId, aCoEdgeId); + if (aStepToChild < 0) + { + continue; + } + theParent.Node = aWireId; + theParent.NextParentIdx = 0; + theParent.StepToChild = -1; + return true; } - - const BRepGraph_WireId aWireId = aWires.Value(aParentIdx); - const BRepGraphInc::WireDef& aWire = aTopo.Wires().Definition(aWireId); - if (aWire.IsRemoved) - { - continue; - } - - const int aStepToChild = findWireCoEdgeStep(aWireId, aCoEdgeId); - if (aStepToChild < 0) - { - continue; - } - - theParent.Node = aWireId; - theParent.NextParentIdx = 0; - theParent.StepToChild = aStepToChild; - return true; + return nextCompoundOrOccurrenceParent(theChild.Node, aParentIdx - aNbWires, theParent); } case Kind::Product: { - const BRepGraph_ProductId aProductId(theChild.Node); - const NCollection_DynamicArray& anOccurrences = - aTopo.Products().Instances(aProductId); - const uint32_t aNbOccurrences = static_cast(anOccurrences.Size()); - if (aParentIdx >= aNbOccurrences) + const BRepGraph_ProductId aProductId(theChild.Node); + if (!aTopo.Gen().HasOccurrenceParents(BRepGraph_NodeId(aProductId))) { return false; } - - const BRepGraph_OccurrenceId anOccurrenceId = - anOccurrences.Value(static_cast(aParentIdx)); - const BRepGraphInc::OccurrenceDef& anOccurrence = - aTopo.Occurrences().Definition(anOccurrenceId); - if (anOccurrence.IsRemoved) + const BRepGraph_OccurrencesOfProduct anOccurrences( + *myGraph, + aTopo.Gen().OccurrenceRefIds(BRepGraph_NodeId(aProductId))); + uint32_t aRemainingParentIdx = aParentIdx; + BRepGraph_OccurrenceId anOccurrenceId; + if (!nthParentId(anOccurrences, aRemainingParentIdx, anOccurrenceId)) { - continue; + return false; } - theParent.Node = anOccurrenceId; theParent.NextParentIdx = 0; theParent.StepToChild = -1; @@ -811,34 +629,26 @@ bool BRepGraph_ParentExplorer::nextParentFrame(StackFrame& theChild, StackFrame& } case Kind::Occurrence: { - if (aParentIdx > 0) + const BRepGraph_OccurrenceId aOccurrenceId(theChild.Node); + const BRepGraph_ProductsOfOccurrence aParents( + *myGraph, + aTopo.Occurrences().Relations(aOccurrenceId).ParentOccurrenceRefIds); + uint32_t aRemainingParentIdx = aParentIdx; + BRepGraph_ProductId aProductId; + if (!nthParentId(aParents, aRemainingParentIdx, aProductId)) { return false; } - - const BRepGraph_OccurrenceId anOccurrenceId(theChild.Node); - const BRepGraphInc::OccurrenceDef& anOccurrence = - aTopo.Occurrences().Definition(anOccurrenceId); - if (anOccurrence.IsRemoved) - { - return false; - } - - BRepGraph_ProductId aParentProductId; - if (!findParentProduct(anOccurrenceId, aParentProductId)) - { - return false; - } - - const int aStepToChild = findOccurrenceStep(aParentProductId, anOccurrenceId); + BRepGraph_OccurrenceRefId anOccurrenceRefId; + const int aStepToChild = findOccurrenceStep(aProductId, aOccurrenceId, &anOccurrenceRefId); if (aStepToChild < 0) { - return false; + continue; } - - theParent.Node = aParentProductId; + theParent.Node = aProductId; theParent.NextParentIdx = 0; theParent.StepToChild = aStepToChild; + theParent.RefToChild = anOccurrenceRefId; return true; } @@ -866,6 +676,7 @@ void BRepGraph_ParentExplorer::prepareCurrentBranch() applyTransition(myStack[aFrameIdx + 1].Node, myStack[aFrameIdx].Node, myStack[aFrameIdx + 1].StepToChild, + myStack[aFrameIdx + 1].RefToChild, myStack[aFrameIdx].AccLocation, myStack[aFrameIdx].AccOrientation); } @@ -876,9 +687,21 @@ void BRepGraph_ParentExplorer::prepareCurrentBranch() void BRepGraph_ParentExplorer::applyTransition(const BRepGraph_NodeId theParent, const BRepGraph_NodeId theChild, const int theStepToChild, + const BRepGraph_RefId theRefToChild, TopLoc_Location& theLocation, TopAbs_Orientation& theOrientation) const { + if (theRefToChild.IsValid()) + { + const BRepGraph::RefsView& aRefs = myGraph->Refs(); + theLocation = theLocation * aRefs.Gen().LocalLocation(theRefToChild); + if (theRefToChild.RefKind != BRepGraph_RefId::Kind::Occurrence) + { + theOrientation = TopAbs::Compose(theOrientation, aRefs.Gen().Orientation(theRefToChild)); + } + return; + } + if (theStepToChild >= 0) { theLocation = theLocation * stepLocation(theParent, theStepToChild); @@ -890,65 +713,40 @@ void BRepGraph_ParentExplorer::applyTransition(const BRepGraph_NodeId theParent, switch (theParent.NodeKind) { case BRepGraph_NodeId::Kind::Occurrence: { - const BRepGraph_OccurrenceId anOccId(theParent); - const BRepGraphInc::OccurrenceDef& anOcc = aTopo.Occurrences().Definition(anOccId); - if (anOcc.IsRemoved) + if (theChild.NodeKind != BRepGraph_NodeId::Kind::Product + && !BRepGraph_NodeId::IsTopologyKind(theChild.NodeKind)) { - return; + Standard_ASSERT_VOID(false, "ParentExplorer: invalid Occurrence structural child"); } - BRepGraph_ProductId aParentProductId; - if (!findParentProduct(anOccId, aParentProductId)) + // The placement is owned by the parent Product -> OccurrenceRef edge. + // Occurrence -> Product is structural and must not compose it again. + return; + } + + case BRepGraph_NodeId::Kind::Wire: { + if (theChild.NodeKind != BRepGraph_NodeId::Kind::CoEdge) { - return; - } - const BRepGraph::RefsView& aRefs = myGraph->Refs(); - for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*myGraph, aParentProductId); anOccIt.More(); - anOccIt.Next()) - { - const BRepGraphInc::OccurrenceRef& aRef = aRefs.Occurrences().Entry(anOccIt.CurrentId()); - if (aRef.OccurrenceDefId == anOccId) - { - theLocation = theLocation * aRef.LocalLocation; - return; - } + Standard_ASSERT_VOID(false, "ParentExplorer: invalid Wire structural child"); } return; } case BRepGraph_NodeId::Kind::CoEdge: { + if (theChild.NodeKind != BRepGraph_NodeId::Kind::Edge) + { + Standard_ASSERT_VOID(false, "ParentExplorer: invalid CoEdge structural child"); + } const BRepGraphInc::CoEdgeDef& aCoEdge = aTopo.CoEdges().Definition(BRepGraph_CoEdgeId(theParent)); - if (!aCoEdge.IsRemoved) + if (!BRepGraph_CoEdgeId(theParent).IsRemoved(*myGraph)) { theOrientation = TopAbs::Compose(theOrientation, aCoEdge.Orientation); } return; } - case BRepGraph_NodeId::Kind::Product: { - const BRepGraph_ProductId aProductId(theParent); - const BRepGraphInc::ProductDef& aProduct = aTopo.Products().Definition(aProductId); - if (aProduct.IsRemoved) - { - return; - } - const BRepGraph::RefsView& aRefs = myGraph->Refs(); - for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*myGraph, aProductId); anOccIt.More(); - anOccIt.Next()) - { - const BRepGraphInc::OccurrenceRef& aRef = aRefs.Occurrences().Entry(anOccIt.CurrentId()); - const BRepGraphInc::OccurrenceDef& anOccDef = - aTopo.Occurrences().Definition(aRef.OccurrenceDefId); - if (anOccDef.ChildDefId == theChild) - { - theLocation = theLocation * aRef.LocalLocation; - return; - } - } - return; - } - default: + Standard_ASSERT_VOID(false, "ParentExplorer: missing structural transition"); return; } } @@ -972,88 +770,117 @@ int BRepGraph_ParentExplorer::branchRootFrame() const //================================================================================================= -bool BRepGraph_ParentExplorer::findNthProductWrapper(const BRepGraph_NodeId theNode, - const uint32_t theOrdinal, - BRepGraph_ProductId& theProduct) const +bool BRepGraph_ParentExplorer::findNthOccurrenceWrapper( + const BRepGraph_NodeId theNode, + const uint32_t theOrdinal, + BRepGraph_OccurrenceId& theOccurrence, + BRepGraph_OccurrenceRefId& theOccurrenceRef) const { - const BRepGraph::TopoView& aTopo = myGraph->Topo(); - const BRepGraph::RefsView& aRefs = myGraph->Refs(); - uint32_t aCount = 0; - for (int aPass = 0; aPass < 2; ++aPass) + const BRepGraph::TopoView& aTopo = myGraph->Topo(); + const BRepGraph::RefsView& aRefs = myGraph->Refs(); + uint32_t aCount = 0; + const NCollection_LinearVector& anOccurrenceRefs = + aTopo.Gen().OccurrenceRefIds(theNode); + for (const BRepGraph_OccurrenceRefId& anOccurrenceRefId : anOccurrenceRefs) { - for (BRepGraph_ProductIterator aProdIt(*myGraph); aProdIt.More(); aProdIt.Next()) + if (anOccurrenceRefId.IsRemoved(*myGraph)) { - const BRepGraph_ProductId aProductId = aProdIt.CurrentId(); - - bool aHasChild = false; - for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*myGraph, aProductId); anOccIt.More(); - anOccIt.Next()) - { - const BRepGraphInc::OccurrenceRef& aRef = aRefs.Occurrences().Entry(anOccIt.CurrentId()); - const BRepGraphInc::OccurrenceDef& anOccDef = - aTopo.Occurrences().Definition(aRef.OccurrenceDefId); - if (anOccDef.ChildDefId == theNode) - { - aHasChild = true; - break; - } - } - if (!aHasChild) - { - continue; - } - - const bool hasInstances = !aTopo.Products().Instances(aProductId).IsEmpty(); - if ((aPass == 0 && !hasInstances) || (aPass == 1 && hasInstances)) - { - continue; - } - - if (aCount == theOrdinal) - { - theProduct = aProductId; - return true; - } - ++aCount; + continue; } + const BRepGraph_OccurrenceId anOccurrenceId = + aRefs.Occurrences().Entry(anOccurrenceRefId).ChildOccurrenceId; + const BRepGraphInc::OccurrenceDef& anOccDef = aTopo.Occurrences().Definition(anOccurrenceId); + if (anOccurrenceId.IsRemoved(*myGraph) || anOccDef.ChildNodeId != theNode) + { + continue; + } + + if (aCount == theOrdinal) + { + theOccurrence = anOccurrenceId; + theOccurrenceRef = anOccurrenceRefId; + return true; + } + ++aCount; } return false; } //================================================================================================= -bool BRepGraph_ParentExplorer::findParentProduct(const BRepGraph_OccurrenceId theOccurrence, - BRepGraph_ProductId& theProduct) const +bool BRepGraph_ParentExplorer::nextCompoundOrOccurrenceParent(const BRepGraph_NodeId theNode, + const uint32_t theRemainingIdx, + StackFrame& theParent) const { - const BRepGraph::RefsView& aRefs = myGraph->Refs(); - for (BRepGraph_ProductIterator aProdIt(*myGraph); aProdIt.More(); aProdIt.Next()) + const BRepGraph::TopoView& aTopo = myGraph->Topo(); + + uint32_t aConsumedIdx = 0; + if (aTopo.Gen().HasCompoundParents(theNode)) { - const BRepGraph_ProductId aProductId = aProdIt.CurrentId(); - for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*myGraph, aProductId); anOccIt.More(); - anOccIt.Next()) + for (BRepGraph_ReverseIterator::IdsOfRefs + aCompounds(*myGraph, aTopo.Gen().CompoundRefIds(theNode)); + aCompounds.More(); + aCompounds.Next()) { - const BRepGraphInc::OccurrenceRef& aRef = aRefs.Occurrences().Entry(anOccIt.CurrentId()); - if (aRef.OccurrenceDefId == theOccurrence) + const BRepGraph_CompoundId aCompoundId = aCompounds.CurrentId(); + if (aCompoundId.IsRemoved(*myGraph)) { - theProduct = aProductId; - return true; + continue; } + if (aConsumedIdx < theRemainingIdx) + { + ++aConsumedIdx; + continue; + } + const int aStepToChild = findCompoundChildStep(aCompoundId, theNode); + if (aStepToChild < 0) + { + continue; + } + theParent.Node = aCompoundId; + theParent.NextParentIdx = 0; + theParent.StepToChild = aStepToChild; + return true; } } - return false; + + if (!aTopo.Gen().HasOccurrenceParents(theNode)) + { + return false; + } + BRepGraph_OccurrenceId anOccurrenceId; + BRepGraph_OccurrenceRefId anOccurrenceRefId; + if (!findNthOccurrenceWrapper(theNode, + theRemainingIdx - aConsumedIdx, + anOccurrenceId, + anOccurrenceRefId)) + { + return false; + } + + theParent.Node = anOccurrenceId; + theParent.NextParentIdx = 0; + theParent.StepToChild = -1; + theParent.RefToChild = anOccurrenceRefId; + return true; } //================================================================================================= int BRepGraph_ParentExplorer::findOccurrenceStep(const BRepGraph_ProductId theParentProduct, - const BRepGraph_OccurrenceId theOccurrence) const + const BRepGraph_OccurrenceId theOccurrence, + BRepGraph_OccurrenceRefId* theOccurrenceRef) const { int aStep = 0; for (BRepGraph_RefsOccurrenceOfProduct aRefIt(*myGraph, theParentProduct); aRefIt.More(); aRefIt.Next()) { - if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theOccurrence)) + if (myGraph->Refs().Gen().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theOccurrence)) { + if (theOccurrenceRef != nullptr) + { + *theOccurrenceRef = aRefIt.CurrentId(); + } return aStep; } ++aStep; @@ -1069,7 +896,7 @@ int BRepGraph_ParentExplorer::findCompoundChildStep(const BRepGraph_CompoundId t int aStep = 0; for (BRepGraph_RefsChildOfCompound aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == theChild) + if (myGraph->Refs().Gen().ChildNode(aRefIt.CurrentId()) == theChild) { return aStep; } @@ -1086,7 +913,7 @@ int BRepGraph_ParentExplorer::findCompSolidSolidStep(const BRepGraph_CompSolidId int aStep = 0; for (BRepGraph_RefsSolidOfCompSolid aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theChild)) + if (myGraph->Refs().Gen().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theChild)) { return aStep; } @@ -1100,15 +927,13 @@ int BRepGraph_ParentExplorer::findCompSolidSolidStep(const BRepGraph_CompSolidId int BRepGraph_ParentExplorer::findSolidChildStep(const BRepGraph_SolidId theParent, const BRepGraph_NodeId theChild) const { - const BRepGraph::TopoView& aTopo = myGraph->Topo(); - const BRepGraph::RefsView& aRefs = myGraph->Refs(); - const BRepGraphInc::SolidDef& aSolid = aTopo.Solids().Definition(theParent); + const BRepGraph::RefsView& aRefs = myGraph->Refs(); if (theChild.NodeKind == BRepGraph_NodeId::Kind::Shell) { int aStep = 0; for (BRepGraph_RefsShellOfSolid aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - if (aRefs.ChildNode(aRefIt.CurrentId()) == theChild) + if (aRefs.Gen().ChildNode(aRefIt.CurrentId()) == theChild) { return aStep; } @@ -1116,18 +941,6 @@ int BRepGraph_ParentExplorer::findSolidChildStep(const BRepGraph_SolidId thePare } return -1; } - - { - uint32_t aRefIdx = 0; - for (const BRepGraph_ChildRefId& aRefId : aSolid.AuxChildRefIds) - { - if (!aRefs.IsRemoved(aRefId) && aRefs.ChildNode(aRefId) == theChild) - { - return aSolid.ShellRefIds.Length() + static_cast(aRefIdx); - } - ++aRefIdx; - } - } return -1; } @@ -1136,15 +949,13 @@ int BRepGraph_ParentExplorer::findSolidChildStep(const BRepGraph_SolidId thePare int BRepGraph_ParentExplorer::findShellChildStep(const BRepGraph_ShellId theParent, const BRepGraph_NodeId theChild) const { - const BRepGraph::TopoView& aTopo = myGraph->Topo(); - const BRepGraph::RefsView& aRefs = myGraph->Refs(); - const BRepGraphInc::ShellDef& aShell = aTopo.Shells().Definition(theParent); + const BRepGraph::RefsView& aRefs = myGraph->Refs(); if (theChild.NodeKind == BRepGraph_NodeId::Kind::Face) { int aStep = 0; for (BRepGraph_RefsFaceOfShell aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - if (aRefs.ChildNode(aRefIt.CurrentId()) == theChild) + if (aRefs.Gen().ChildNode(aRefIt.CurrentId()) == theChild) { return aStep; } @@ -1152,18 +963,6 @@ int BRepGraph_ParentExplorer::findShellChildStep(const BRepGraph_ShellId thePare } return -1; } - - { - uint32_t aRefIdx = 0; - for (const BRepGraph_ChildRefId& aRefId : aShell.AuxChildRefIds) - { - if (!aRefs.IsRemoved(aRefId) && aRefs.ChildNode(aRefId) == theChild) - { - return aShell.FaceRefIds.Length() + static_cast(aRefIdx); - } - ++aRefIdx; - } - } return -1; } @@ -1172,15 +971,13 @@ int BRepGraph_ParentExplorer::findShellChildStep(const BRepGraph_ShellId thePare int BRepGraph_ParentExplorer::findFaceChildStep(const BRepGraph_FaceId theParent, const BRepGraph_NodeId theChild) const { - const BRepGraph::TopoView& aTopo = myGraph->Topo(); - const BRepGraph::RefsView& aRefs = myGraph->Refs(); - const BRepGraphInc::FaceDef& aFace = aTopo.Faces().Definition(theParent); + const BRepGraph::RefsView& aRefs = myGraph->Refs(); if (theChild.NodeKind == BRepGraph_NodeId::Kind::Wire) { int aStep = 0; for (BRepGraph_RefsWireOfFace aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - if (aRefs.ChildNode(aRefIt.CurrentId()) == theChild) + if (aRefs.Gen().ChildNode(aRefIt.CurrentId()) == theChild) { return aStep; } @@ -1188,19 +985,6 @@ int BRepGraph_ParentExplorer::findFaceChildStep(const BRepGraph_FaceId theParent } return -1; } - - if (theChild.NodeKind == BRepGraph_NodeId::Kind::Vertex) - { - int aStep = 0; - for (BRepGraph_RefsVertexOfFace aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) - { - if (aRefs.ChildNode(aRefIt.CurrentId()) == theChild) - { - return aFace.WireRefIds.Length() + aStep; - } - ++aStep; - } - } return -1; } @@ -1210,9 +994,9 @@ int BRepGraph_ParentExplorer::findWireCoEdgeStep(const BRepGraph_WireId thePar const BRepGraph_CoEdgeId theChild) const { int aStep = 0; - for (BRepGraph_RefsCoEdgeOfWire aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) + for (BRepGraph_CoEdgesOfWire aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) { - if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theChild)) + if (aRefIt.CurrentId() == theChild) { return aStep; } @@ -1226,14 +1010,16 @@ int BRepGraph_ParentExplorer::findWireCoEdgeStep(const BRepGraph_WireId thePar int BRepGraph_ParentExplorer::findEdgeVertexStep(const BRepGraph_EdgeId theParent, const BRepGraph_VertexId theChild) const { - int aStep = 0; - for (BRepGraph_RefsVertexOfEdge aRefIt(*myGraph, theParent); aRefIt.More(); aRefIt.Next()) + const BRepGraphInc::EdgeDef& anEdge = myGraph->Topo().Edges().Definition(theParent); + if (anEdge.StartVertexRefId.IsValid() + && myGraph->Refs().Gen().ChildNode(anEdge.StartVertexRefId) == BRepGraph_NodeId(theChild)) { - if (myGraph->Refs().ChildNode(aRefIt.CurrentId()) == BRepGraph_NodeId(theChild)) - { - return aStep; - } - ++aStep; + return 0; + } + if (anEdge.EndVertexRefId.IsValid() + && myGraph->Refs().Gen().ChildNode(anEdge.EndVertexRefId) == BRepGraph_NodeId(theChild)) + { + return 1; } return -1; } @@ -1243,11 +1029,11 @@ int BRepGraph_ParentExplorer::findEdgeVertexStep(const BRepGraph_EdgeId thePar void BRepGraph_ParentExplorer::pushFrame(const StackFrame& theFrame) { const BRepGraph::TopoView& aTopo = myGraph->Topo(); - const int aMaxDepth = aTopo.Compounds().Nb() + aTopo.CompSolids().Nb() + aTopo.Solids().Nb() - + aTopo.Shells().Nb() + aTopo.Faces().Nb() + aTopo.Wires().Nb() - + aTopo.Edges().Nb() + aTopo.Vertices().Nb() + aTopo.Products().Nb() - + aTopo.Occurrences().Nb() + aTopo.CoEdges().Nb(); - if (myStackTop >= aMaxDepth) + const uint32_t aMaxDepth = aTopo.Compounds().Nb() + aTopo.CompSolids().Nb() + aTopo.Solids().Nb() + + aTopo.Shells().Nb() + aTopo.Faces().Nb() + aTopo.Wires().Nb() + + aTopo.Edges().Nb() + aTopo.Vertices().Nb() + aTopo.Products().Nb() + + aTopo.Occurrences().Nb() + aTopo.CoEdges().Nb(); + if (myStackTop >= 0 && static_cast(myStackTop) >= aMaxDepth) { return; } @@ -1278,7 +1064,7 @@ TopLoc_Location BRepGraph_ParentExplorer::stepLocation(const BRepGraph_NodeId th const int theRefIdx) const { const BRepGraph::RefsView& aRefs = myGraph->Refs(); - return aRefs.LocalLocation(aRefs.RefAtStep(theParent, theRefIdx)); + return aRefs.Gen().LocalLocation(aRefs.Gen().RefAtStep(theParent, theRefIdx)); } //================================================================================================= @@ -1287,5 +1073,5 @@ TopAbs_Orientation BRepGraph_ParentExplorer::stepOrientation(const BRepGraph_Nod const int theRefIdx) const { const BRepGraph::RefsView& aRefs = myGraph->Refs(); - return aRefs.Orientation(aRefs.RefAtStep(theParent, theRefIdx)); + return aRefs.Gen().Orientation(aRefs.Gen().RefAtStep(theParent, theRefIdx)); } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.hxx index c356806b2f..97a90cdaa7 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ParentExplorer.hxx @@ -18,11 +18,9 @@ #include #include #include - #include #include #include - #include #include @@ -40,7 +38,8 @@ //! kind is visited as a distinct entity (no hidden collapses): //! Vertex -> Edge, Edge -> CoEdge, CoEdge -> Wire, Wire -> Face, //! Face -> Shell, Shell -> Solid, Solid -> CompSolid/Compound, -//! Product -> Occurrence, Occurrence -> Product (parent assembly). +//! topology root -> Occurrence, Product child -> Occurrence, +//! Occurrence -> parent Product. //! //! ## Traversal modes //! - **Recursive**: walks the full ancestor chain to the graph roots. @@ -72,9 +71,8 @@ public: //! Consolidated configuration for the explorer. //! - //! Prefer this struct over the historical constructor family. The overloads - //! remain supported but the `Config`-based constructor is the long-term - //! idiom: new options can be added as fields without another ctor overload. + //! The `Config`-based constructor is the preferred idiom: new options can be + //! added as fields without additional constructor overloads. //! //! @code //! BRepGraph_ParentExplorer::Config aConfig; @@ -100,15 +98,25 @@ public: const Config& theConfig); //! Explore all parents of the starting node. + //! @param[in] theGraph graph to walk + //! @param[in] theNode starting node whose ancestors are explored Standard_EXPORT BRepGraph_ParentExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theNode); //! Explore parents of the starting node using the given traversal mode. + //! @param[in] theGraph graph to walk + //! @param[in] theNode starting node whose ancestors are explored + //! @param[in] theMode traversal strategy (recursive or direct parents) Standard_EXPORT BRepGraph_ParentExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theNode, TraversalMode theMode); //! Explore all parents while pruning branches at the avoid kind. + //! @param[in] theGraph graph to walk + //! @param[in] theNode starting node whose ancestors are explored + //! @param[in] theAvoidKind node kind to avoid ascending through + //! @param[in] theEmitAvoidKind if true, emit matching avoid-kind ancestors once + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ParentExplorer( const BRepGraph& theGraph, const BRepGraph_NodeId theNode, @@ -117,17 +125,30 @@ public: TraversalMode theMode = TraversalMode::Recursive); //! Explore only parents of the given kind. + //! @param[in] theGraph graph to walk + //! @param[in] theNode starting node whose ancestors are explored + //! @param[in] theTargetKind kind of nodes to emit Standard_EXPORT BRepGraph_ParentExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theNode, BRepGraph_NodeId::Kind theTargetKind); //! Explore only parents of the given kind using the given traversal mode. + //! @param[in] theGraph graph to walk + //! @param[in] theNode starting node whose ancestors are explored + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ParentExplorer(const BRepGraph& theGraph, const BRepGraph_NodeId theNode, BRepGraph_NodeId::Kind theTargetKind, TraversalMode theMode); //! Explore parents of the given kind while pruning branches at the avoid kind. + //! @param[in] theGraph graph to walk + //! @param[in] theNode starting node whose ancestors are explored + //! @param[in] theTargetKind kind of nodes to emit + //! @param[in] theAvoidKind node kind to avoid ascending through + //! @param[in] theEmitAvoidKind if true, emit matching avoid-kind ancestors once + //! @param[in] theMode traversal strategy Standard_EXPORT BRepGraph_ParentExplorer( const BRepGraph& theGraph, const BRepGraph_NodeId theNode, @@ -169,7 +190,7 @@ public: //! Some upward steps are structural and therefore have no parent-owned ref //! entry even though the parent itself is still emitted by the explorer. //! In those cases this method returns an invalid RefId, for example for - //! CoEdge->Edge, Product(part)->ShapeRoot and Occurrence->Product. + //! CoEdge->Edge and Occurrence->Product/topology-root. [[nodiscard]] Standard_EXPORT BRepGraph_RefId CurrentRef() const; //! Accumulated location at the starting node of the current branch. @@ -196,6 +217,7 @@ private: BRepGraph_NodeId Node; uint32_t NextParentIdx = 0; int StepToChild = -1; + BRepGraph_RefId RefToChild; TopLoc_Location AccLocation; TopAbs_Orientation AccOrientation = TopAbs_FORWARD; }; @@ -209,33 +231,42 @@ private: Standard_EXPORT void applyTransition(const BRepGraph_NodeId theParent, const BRepGraph_NodeId theChild, const int theStepToChild, + const BRepGraph_RefId theRefToChild, TopLoc_Location& theLocation, TopAbs_Orientation& theOrientation) const; [[nodiscard]] Standard_EXPORT int branchRootFrame() const; - Standard_EXPORT bool findNthProductWrapper(const BRepGraph_NodeId theNode, - const uint32_t theOrdinal, - BRepGraph_ProductId& theProduct) const; + Standard_EXPORT bool findNthOccurrenceWrapper(const BRepGraph_NodeId theNode, + const uint32_t theOrdinal, + BRepGraph_OccurrenceId& theOccurrence, + BRepGraph_OccurrenceRefId& theOccurrenceRef) const; - Standard_EXPORT bool findParentProduct(const BRepGraph_OccurrenceId theOccurrence, - BRepGraph_ProductId& theProduct) const; - Standard_EXPORT int findOccurrenceStep(const BRepGraph_ProductId theParentProduct, - const BRepGraph_OccurrenceId theOccurrence) const; - Standard_EXPORT int findCompoundChildStep(const BRepGraph_CompoundId theParent, - const BRepGraph_NodeId theChild) const; - Standard_EXPORT int findCompSolidSolidStep(const BRepGraph_CompSolidId theParent, - const BRepGraph_SolidId theChild) const; - Standard_EXPORT int findSolidChildStep(const BRepGraph_SolidId theParent, - const BRepGraph_NodeId theChild) const; - Standard_EXPORT int findShellChildStep(const BRepGraph_ShellId theParent, - const BRepGraph_NodeId theChild) const; - Standard_EXPORT int findFaceChildStep(const BRepGraph_FaceId theParent, - const BRepGraph_NodeId theChild) const; - Standard_EXPORT int findWireCoEdgeStep(const BRepGraph_WireId theParent, - const BRepGraph_CoEdgeId theChild) const; - Standard_EXPORT int findEdgeVertexStep(const BRepGraph_EdgeId theParent, - const BRepGraph_VertexId theChild) const; + Standard_EXPORT int findOccurrenceStep( + const BRepGraph_ProductId theParentProduct, + const BRepGraph_OccurrenceId theOccurrence, + BRepGraph_OccurrenceRefId* theOccurrenceRef = nullptr) const; + Standard_EXPORT int findCompoundChildStep(const BRepGraph_CompoundId theParent, + const BRepGraph_NodeId theChild) const; + Standard_EXPORT int findCompSolidSolidStep(const BRepGraph_CompSolidId theParent, + const BRepGraph_SolidId theChild) const; + Standard_EXPORT int findSolidChildStep(const BRepGraph_SolidId theParent, + const BRepGraph_NodeId theChild) const; + Standard_EXPORT int findShellChildStep(const BRepGraph_ShellId theParent, + const BRepGraph_NodeId theChild) const; + Standard_EXPORT int findFaceChildStep(const BRepGraph_FaceId theParent, + const BRepGraph_NodeId theChild) const; + Standard_EXPORT int findWireCoEdgeStep(const BRepGraph_WireId theParent, + const BRepGraph_CoEdgeId theChild) const; + Standard_EXPORT int findEdgeVertexStep(const BRepGraph_EdgeId theParent, + const BRepGraph_VertexId theChild) const; + + //! Try compound parents, then occurrence parents for the given node. + //! Returns true and fills theParent if a match is found at theRemainingIdx. + //! Returns false if no compound/occurrence parent exists at that index. + Standard_EXPORT bool nextCompoundOrOccurrenceParent(BRepGraph_NodeId theNode, + uint32_t theRemainingIdx, + StackFrame& theParent) const; static std::optional normalizeAvoidKind( const BRepGraph_NodeId theNode, @@ -288,4 +319,4 @@ private: bool myHasMore = false; }; -#endif // _BRepGraph_ParentExplorer_HeaderFile \ No newline at end of file +#endif // _BRepGraph_ParentExplorer_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefId.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefId.hxx index 3c7c34f559..354f7b3a49 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefId.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefId.hxx @@ -23,6 +23,8 @@ #include #include +class BRepGraph; + //! Lightweight typed index into a per-kind reference vector inside BRepGraph. //! //! The pair (Kind, Index) forms a unique reference identifier within one graph @@ -35,13 +37,29 @@ struct BRepGraph_RefId Shell = 0, //!< Shell reference entries (usage of shell definitions) Face = 1, //!< Face reference entries (usage of face definitions) Wire = 2, //!< Wire reference entries (usage of wire definitions) - CoEdge = 3, //!< CoEdge reference entries (usage of coedge definitions) - Vertex = 4, //!< Vertex reference entries (usage of vertex definitions) - Solid = 5, //!< Solid reference entries (usage of solid definitions) - Child = 6, //!< Generic child references (usage of mixed node definitions) - Occurrence = 7 //!< Occurrence references (usage of occurrence definitions) + Vertex = 3, //!< Vertex reference entries (usage of vertex definitions) + Solid = 4, //!< Solid reference entries (usage of solid definitions) + Child = 5, //!< Generic child references (usage of mixed node definitions) + Occurrence = 6 //!< Occurrence references (usage of occurrence definitions) }; + //! True if the kind value is one of the supported reference kinds. + static bool IsValidKind(const Kind theKind) + { + switch (theKind) + { + case Kind::Shell: + case Kind::Face: + case Kind::Wire: + case Kind::Vertex: + case Kind::Solid: + case Kind::Child: + case Kind::Occurrence: + return true; + } + return false; + } + //! @brief Compile-time typed wrapper around BRepGraph_RefId. //! //! Provides compile-time kind safety similarly to BRepGraph_NodeId::Typed. @@ -78,11 +96,17 @@ struct BRepGraph_RefId //! Invalid sentinel id. [[nodiscard]] static Typed Invalid() { return Typed(); } - [[nodiscard]] bool IsValid() const { return Index != THE_INVALID_INDEX; } + [[nodiscard]] bool IsValid() const + { + return BRepGraph_RefId::IsValidKind(TheKind) && Index != THE_INVALID_INDEX; + } //! True if this id points to an allocated slot within [0, theMaxCount). //! UINT32_MAX (invalid sentinel) always fails this check for any realistic count. - [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const { return Index < theMaxCount; } + [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const + { + return IsValid() && Index < theMaxCount; + } template [[nodiscard]] auto IsValidIn(const CountProviderT& theProvider) const @@ -102,7 +126,11 @@ struct BRepGraph_RefId static Typed FromRefId(const BRepGraph_RefId theRefId) { - Standard_ASSERT_VOID(theRefId.RefKind == TheKind, "RefId kind mismatch"); + Standard_ASSERT_RETURN(theRefId.RefKind == TheKind, "RefId kind mismatch", Typed()); + if (!theRefId.IsValid()) + { + return Typed(); + } return Typed(theRefId.Index); } @@ -165,12 +193,23 @@ struct BRepGraph_RefId { return theRhs != theLhs; } + + //! Return true if this reference entry has been soft-removed in the given graph. + [[nodiscard]] bool IsRemoved(const BRepGraph& theGraph) const + { + return BRepGraph_RefId(*this).IsRemoved(theGraph); + } + + //! Return true if this reference entry has an active owner in the given graph. + [[nodiscard]] bool IsOwned(const BRepGraph& theGraph) const + { + return BRepGraph_RefId(*this).IsOwned(theGraph); + } }; static bool IsTopologyRefKind(const Kind theKind) { - return static_cast(theKind) >= static_cast(Kind::Shell) - && static_cast(theKind) <= static_cast(Kind::Child); + return IsValidKind(theKind) && theKind >= Kind::Shell && theKind <= Kind::Child; } static constexpr uint32_t THE_START_INDEX = 0u; @@ -203,11 +242,14 @@ struct BRepGraph_RefId return BRepGraph_RefId(theKind, THE_INVALID_INDEX); } - [[nodiscard]] bool IsValid() const { return Index != THE_INVALID_INDEX; } + [[nodiscard]] bool IsValid() const { return IsValidKind(RefKind) && Index != THE_INVALID_INDEX; } //! True if this id points to an allocated slot within [0, theMaxCount). //! UINT32_MAX (invalid sentinel) always fails this check for any realistic count. - [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const { return Index < theMaxCount; } + [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const + { + return IsValid() && Index < theMaxCount; + } template [[nodiscard]] auto IsValidIn(const CountProviderT& theProvider) const @@ -233,7 +275,9 @@ struct BRepGraph_RefId bool operator<(const BRepGraph_RefId& theOther) const { if (RefKind != theOther.RefKind) + { return static_cast(RefKind) < static_cast(theOther.RefKind); + } return Index < theOther.Index; } @@ -281,8 +325,6 @@ struct BRepGraph_RefId return std::forward(theFunc)(Typed::FromRefId(theRefId)); case Kind::Wire: return std::forward(theFunc)(Typed::FromRefId(theRefId)); - case Kind::CoEdge: - return std::forward(theFunc)(Typed::FromRefId(theRefId)); case Kind::Vertex: return std::forward(theFunc)(Typed::FromRefId(theRefId)); case Kind::Solid: @@ -296,12 +338,17 @@ struct BRepGraph_RefId Standard_ASSERT_VOID(false, "BRepGraph_RefId::Visit: unhandled Kind"); return std::forward(theFunc)(Typed()); } + + //! Return true if this reference entry has been soft-removed in the given graph. + [[nodiscard]] Standard_EXPORT bool IsRemoved(const BRepGraph& theGraph) const; + + //! Return true if this reference entry has an active owner in the given graph. + [[nodiscard]] Standard_EXPORT bool IsOwned(const BRepGraph& theGraph) const; }; using BRepGraph_ShellRefId = BRepGraph_RefId::Typed; using BRepGraph_FaceRefId = BRepGraph_RefId::Typed; using BRepGraph_WireRefId = BRepGraph_RefId::Typed; -using BRepGraph_CoEdgeRefId = BRepGraph_RefId::Typed; using BRepGraph_VertexRefId = BRepGraph_RefId::Typed; using BRepGraph_SolidRefId = BRepGraph_RefId::Typed; using BRepGraph_ChildRefId = BRepGraph_RefId::Typed; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.cxx deleted file mode 100644 index 579b6f0a6d..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.cxx +++ /dev/null @@ -1,297 +0,0 @@ -// 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 - -//================================================================================================= - -void BRepGraph_RefTransientCache::ensureKind(const int theKindSlot) -{ - if (theKindSlot >= myKinds.Length()) - { - myKinds.SetValue(theKindSlot, CacheKindSlot()); - } -} - -//================================================================================================= - -BRepGraph_RefTransientCache::CacheSlot& BRepGraph_RefTransientCache::changeSlot( - const BRepGraph_RefId theRef, - const int theKindSlot) -{ - ensureKind(theKindSlot); - const int aRefKindIdx = static_cast(theRef.RefKind); - Standard_ASSERT_VOID(aRefKindIdx >= 0 && aRefKindIdx < THE_REF_KIND_COUNT, - "BRepGraph_RefTransientCache: RefKind out of range"); - NCollection_DynamicArray& aVec = - myKinds.ChangeValue(theKindSlot).myRefKinds[aRefKindIdx].mySlots; - if (theRef.Index >= aVec.Size()) - { - return aVec.SetValue(static_cast(theRef.Index), CacheSlot()); - } - return aVec.ChangeValue(static_cast(theRef.Index)); -} - -//================================================================================================= - -const BRepGraph_RefTransientCache::CacheSlot* BRepGraph_RefTransientCache::seekSlot( - const BRepGraph_RefId theRef, - const int theKindSlot) const -{ - if (theKindSlot < 0 || theKindSlot >= myKinds.Length()) - { - return nullptr; - } - - const int aRefKindIdx = static_cast(theRef.RefKind); - Standard_ASSERT_VOID(aRefKindIdx >= 0 && aRefKindIdx < THE_REF_KIND_COUNT, - "BRepGraph_RefTransientCache: RefKind out of range"); - const NCollection_DynamicArray& aVec = - myKinds.Value(theKindSlot).myRefKinds[aRefKindIdx].mySlots; - if (!theRef.IsValidIn(aVec)) - { - return nullptr; - } - return &aVec.Value(static_cast(theRef.Index)); -} - -//================================================================================================= - -void BRepGraph_RefTransientCache::Reserve(const int theKindCount, - const int theCounts[THE_REF_KIND_COUNT]) -{ - std::unique_lock aLock(myMutex); - - const int aKindCount = theKindCount > 0 ? theKindCount : 0; - if (aKindCount > 0) - { - ensureKind(aKindCount - 1); - } - - for (int aKindSlot = 0; aKindSlot < aKindCount; ++aKindSlot) - { - CacheKindSlot& aKindSlotData = myKinds.ChangeValue(aKindSlot); - for (int aRefKindIdx = 0; aRefKindIdx < THE_REF_KIND_COUNT; ++aRefKindIdx) - { - const int aCount = theCounts[aRefKindIdx]; - if (aCount > 0 && aCount > aKindSlotData.myRefKinds[aRefKindIdx].mySlots.Length()) - { - aKindSlotData.myRefKinds[aRefKindIdx].mySlots.SetValue(aCount - 1, CacheSlot()); - } - } - } - - myIsReserved.store(true, std::memory_order_release); -} - -//================================================================================================= - -void BRepGraph_RefTransientCache::Set(const BRepGraph_RefId theRef, - const occ::handle& theKind, - const occ::handle& theValue, - const uint32_t theCurrentOwnGen) -{ - if (!theRef.IsValid() || theKind.IsNull()) - { - return; - } - - const int aKindSlot = BRepGraph_CacheKindRegistry::Register(theKind); - if (aKindSlot < 0) - { - return; - } - - Set(theRef, aKindSlot, theValue, theCurrentOwnGen); -} - -//================================================================================================= - -void BRepGraph_RefTransientCache::Set(const BRepGraph_RefId theRef, - const int theKindSlot, - const occ::handle& theValue, - const uint32_t theCurrentOwnGen) -{ - if (!theRef.IsValid() || theKindSlot < 0) - { - return; - } - - if (myIsReserved.load(std::memory_order_acquire) && theKindSlot < myKinds.Length()) - { - const int aRefKindIdx = static_cast(theRef.RefKind); - NCollection_DynamicArray& aVec = - myKinds.ChangeValue(theKindSlot).myRefKinds[aRefKindIdx].mySlots; - if (theRef.Index < aVec.Size()) - { - CacheSlot& aSlot = aVec.ChangeValue(static_cast(theRef.Index)); - aSlot.Value = theValue; - aSlot.StoredOwnGen = theCurrentOwnGen; - return; - } - } - - std::unique_lock aLock(myMutex); - CacheSlot& aSlot = changeSlot(theRef, theKindSlot); - aSlot.Value = theValue; - aSlot.StoredOwnGen = theCurrentOwnGen; -} - -//================================================================================================= - -occ::handle BRepGraph_RefTransientCache::Get( - const BRepGraph_RefId theRef, - const occ::handle& theKind, - const uint32_t theCurrentOwnGen) const -{ - if (!theRef.IsValid() || theKind.IsNull()) - { - return occ::handle(); - } - - const int aKindSlot = BRepGraph_CacheKindRegistry::FindSlot(theKind->ID()); - if (aKindSlot < 0) - { - return occ::handle(); - } - - return Get(theRef, aKindSlot, theCurrentOwnGen); -} - -//================================================================================================= - -occ::handle BRepGraph_RefTransientCache::Get( - const BRepGraph_RefId theRef, - const int theKindSlot, - const uint32_t theCurrentOwnGen) const -{ - if (!theRef.IsValid() || theKindSlot < 0) - { - return occ::handle(); - } - - if (myIsReserved.load(std::memory_order_acquire) && theKindSlot < myKinds.Length()) - { - const int aRefKindIdx = static_cast(theRef.RefKind); - const NCollection_DynamicArray& aVec = - myKinds.Value(theKindSlot).myRefKinds[aRefKindIdx].mySlots; - if (theRef.Index < aVec.Size()) - { - const CacheSlot& aSlot = aVec.Value(static_cast(theRef.Index)); - if (aSlot.Value.IsNull()) - { - return occ::handle(); - } - if (aSlot.StoredOwnGen != theCurrentOwnGen) - { - return occ::handle(); - } - return aSlot.Value; - } - } - - std::shared_lock aLock(myMutex); - const CacheSlot* aSlot = seekSlot(theRef, theKindSlot); - if (aSlot == nullptr || aSlot->Value.IsNull()) - { - return occ::handle(); - } - if (aSlot->StoredOwnGen != theCurrentOwnGen) - { - return occ::handle(); - } - return aSlot->Value; -} - -//================================================================================================= - -bool BRepGraph_RefTransientCache::Remove(const BRepGraph_RefId theRef, - const occ::handle& theKind) -{ - if (!theRef.IsValid() || theKind.IsNull()) - { - return false; - } - - const int aKindSlot = BRepGraph_CacheKindRegistry::FindSlot(theKind->ID()); - if (aKindSlot < 0) - { - return false; - } - - return Remove(theRef, aKindSlot); -} - -//================================================================================================= - -bool BRepGraph_RefTransientCache::Remove(const BRepGraph_RefId theRef, const int theKindSlot) -{ - if (!theRef.IsValid() || theKindSlot < 0) - { - return false; - } - - std::unique_lock aLock(myMutex); - if (theKindSlot >= myKinds.Length()) - { - return false; - } - - const int aRefKindIdx = static_cast(theRef.RefKind); - NCollection_DynamicArray& aVec = - myKinds.ChangeValue(theKindSlot).myRefKinds[aRefKindIdx].mySlots; - if (theRef.Index >= aVec.Size()) - { - return false; - } - - CacheSlot& aSlot = aVec.ChangeValue(static_cast(theRef.Index)); - if (aSlot.Value.IsNull()) - { - return false; - } - aSlot.Value.Nullify(); - aSlot.StoredOwnGen = 0; - return true; -} - -//================================================================================================= - -int BRepGraph_RefTransientCache::CollectCacheKindSlots(const BRepGraph_RefId theRef, - const uint32_t theCurrentOwnGen, - int theSlots[]) const -{ - int aCount = 0; - if (!theRef.IsValid()) - { - return aCount; - } - - for (int aKindSlot = 0; aKindSlot < myKinds.Length(); ++aKindSlot) - { - const CacheSlot* aSlot = seekSlot(theRef, aKindSlot); - if (aSlot != nullptr && !aSlot->Value.IsNull() && aSlot->StoredOwnGen == theCurrentOwnGen) - { - theSlots[aCount++] = aKindSlot; - } - } - return aCount; -} - -//================================================================================================= - -void BRepGraph_RefTransientCache::Clear() noexcept -{ - myKinds.Clear(); - myIsReserved.store(false, std::memory_order_relaxed); -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.hxx deleted file mode 100644 index d887281068..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefTransientCache.hxx +++ /dev/null @@ -1,177 +0,0 @@ -// 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. - -#ifndef _BRepGraph_RefTransientCache_HeaderFile -#define _BRepGraph_RefTransientCache_HeaderFile - -#include -#include - -#include - -#include -#include - -//! @brief Centralized transient cache for algorithm-computed per-reference values. -//! -//! Symmetric counterpart of BRepGraph_TransientCache, keyed by BRepGraph_RefId -//! instead of BRepGraph_NodeId. Freshness is tracked via BaseRef::OwnGen rather -//! than BaseDef::SubtreeGen, because references do not own subtrees. -//! -//! Shares the same BRepGraph_CacheKind descriptors and BRepGraph_CacheKindRegistry -//! as the node cache; the same kind GUID can address values in both caches. -//! -//! ## OwnGen-based freshness -//! Each stored slot records OwnGen at write time. On read, if the stored OwnGen -//! differs from the reference's current OwnGen the cached value is considered stale. -//! -//! ## Lifecycle -//! NOT a Layer. Cleared on BRepGraph_Builder::Add() and Compact(). No explicit removal callback -//! - stale data is auto-detected by OwnGen mismatch. -//! -//! ## Thread safety -//! After Reserve(), Get() and Set() for in-range indices bypass the mutex entirely. -//! Out-of-range access falls back to mutex-protected vector growth. -class BRepGraph_RefTransientCache -{ -public: - //! Number of BRepGraph_RefId::Kind enum values (Shell..Occurrence = 0..7). - static constexpr int THE_REF_KIND_COUNT = 8; - - //! Default number of cache-kind slots reserved after BRepGraph_Builder::Add(). - static constexpr int THE_DEFAULT_RESERVED_KIND_COUNT = 16; - - //! Per-slot storage: cached value handle + OwnGen stamp. - struct CacheSlot - { - occ::handle Value; - uint32_t StoredOwnGen = 0; - }; - - //! Store a cached value for a reference and cache kind. - //! @pre Reserve() must have been called for lock-free parallel access - //! on in-range entity indices; out-of-range access falls back to mutex. - Standard_EXPORT void Set(const BRepGraph_RefId theRef, - const occ::handle& theKind, - const occ::handle& theValue, - const uint32_t theCurrentOwnGen); - - //! Store a cached value using a pre-resolved kind slot index. - //! Bypasses BRepGraph_CacheKindRegistry lookup - use in hot parallel paths. - //! @param[in] theKindSlot slot from BRepGraph_CacheKindRegistry::Register() - Standard_EXPORT void Set(const BRepGraph_RefId theRef, - const int theKindSlot, - const occ::handle& theValue, - const uint32_t theCurrentOwnGen); - - //! Retrieve a cached value for a reference and cache kind. - //! Returns null handle if no value is stored or if OwnGen has changed. - [[nodiscard]] Standard_EXPORT occ::handle Get( - const BRepGraph_RefId theRef, - const occ::handle& theKind, - const uint32_t theCurrentOwnGen) const; - - //! Retrieve a cached value using a pre-resolved kind slot index. - [[nodiscard]] Standard_EXPORT occ::handle Get( - const BRepGraph_RefId theRef, - const int theKindSlot, - const uint32_t theCurrentOwnGen) const; - - //! Remove a cached value for a reference and cache kind. - [[nodiscard]] Standard_EXPORT bool Remove(const BRepGraph_RefId theRef, - const occ::handle& theKind); - - //! Remove a cached value using a pre-resolved cache-kind slot. - [[nodiscard]] Standard_EXPORT bool Remove(const BRepGraph_RefId theRef, const int theKindSlot); - - //! Collect fresh cache-kind slot indices for a reference (zero heap allocation). - //! Used internally by CacheView::CacheKindIterator. - //! @param[in] theRef reference to query - //! @param[in] theCurrentOwnGen freshness stamp to match - //! @param[out] theSlots output array (caller-allocated, must hold - //! THE_DEFAULT_RESERVED_KIND_COUNT) - //! @return number of populated slots written to theSlots - Standard_EXPORT int CollectCacheKindSlots(const BRepGraph_RefId theRef, - const uint32_t theCurrentOwnGen, - int theSlots[]) const; - - //! Pre-allocate storage for lock-free parallel access. - Standard_EXPORT void Reserve(const int theKindCount, const int theCounts[THE_REF_KIND_COUNT]); - - //! True if Reserve() has been called and storage is pre-allocated. - [[nodiscard]] bool IsReserved() const noexcept - { - return myIsReserved.load(std::memory_order_acquire); - } - - //! Clear all cached data. Called on BRepGraph_Builder::Add() and Compact(). - Standard_EXPORT void Clear() noexcept; - - //! Move constructor: transfers data, creates fresh mutex. - BRepGraph_RefTransientCache(BRepGraph_RefTransientCache&& theOther) noexcept - : myKinds(std::move(theOther.myKinds)), - myIsReserved(theOther.myIsReserved.load(std::memory_order_relaxed)) - { - theOther.myIsReserved.store(false, std::memory_order_relaxed); - } - - //! Move assignment: transfers data, mutex stays local. - BRepGraph_RefTransientCache& operator=(BRepGraph_RefTransientCache&& theOther) noexcept - { - if (this != &theOther) - { - myKinds = std::move(theOther.myKinds); - myIsReserved.store(theOther.myIsReserved.load(std::memory_order_relaxed), - std::memory_order_relaxed); - theOther.myIsReserved.store(false, std::memory_order_relaxed); - } - return *this; - } - - BRepGraph_RefTransientCache() = default; - BRepGraph_RefTransientCache(const BRepGraph_RefTransientCache&) = delete; - BRepGraph_RefTransientCache& operator=(const BRepGraph_RefTransientCache&) = delete; - -private: - //! Per-ref-kind dense vector of cache slots. - struct RefKindStore - { - NCollection_DynamicArray mySlots; - }; - - //! Per-cache-kind storage: one ref-kind store per reference kind. - struct CacheKindSlot - { - RefKindStore myRefKinds[THE_REF_KIND_COUNT]; - }; - - //! Ensure myKinds has capacity for the given cache-kind slot. - void ensureKind(const int theKindSlot); - - //! Access slot (mutable) - grows vector if needed. - CacheSlot& changeSlot(const BRepGraph_RefId theRef, const int theKindSlot); - - //! Access slot (const) - returns nullptr if out of range. - const CacheSlot* seekSlot(const BRepGraph_RefId theRef, const int theKindSlot) const; - - //! Outer vector indexed by cache-kind slot. - NCollection_DynamicArray myKinds; - - //! True after Reserve() - enables lock-free access for in-range slots. - std::atomic myIsReserved{false}; - - //! Protects structural modifications (vector growth) during concurrent access. - mutable std::shared_mutex myMutex; -}; - -#endif // _BRepGraph_RefTransientCache_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefUID.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefUID.hxx index c19c57631a..9056e87a31 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefUID.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefUID.hxx @@ -15,87 +15,73 @@ #define _BRepGraph_RefUID_HeaderFile #include +#include +#include #include #include #include +#include -//! Unique reference identifier within a BRepGraph. +//! Unique reference-entry identifier within a BRepGraph. //! -//! Identity = (RefKind, Counter). Generation is excluded from equality/hash. -//! Counter 0 is an invalid sentinel. -//! -//! ## Serialization Contract -//! -//! Entity UIDs (BRepGraph_UID) and reference UIDs (BRepGraph_RefUID) share -//! a single monotonic counter (BRepGraph_Data::myNextUIDCounter). -//! To persist a BRepGraph across sessions: -//! 1. Write: for each reference entry, serialize (RefKind, Counter, OwnGen). -//! 2. Read: reconstruct reference entries, populate RefUID vectors with -//! deserialized (RefKind, Counter) values, set myNextUIDCounter to -//! max(all_entity_counters, all_ref_counters) + 1. -//! 3. myGeneration resets to 0 on load (session-scoped). -//! 4. VersionStamps from a previous session will correctly detect staleness -//! via Generation mismatch. +//! Identity = (RefKind, Counter). Counter 0 is an invalid sentinel. struct BRepGraph_RefUID { - BRepGraph_RefUID() - : myCounter(0), - myKind(BRepGraph_RefId::Kind::Shell), - myGeneration(0) - { - } + BRepGraph_RefId::Kind Kind = BRepGraph_RefId::Kind::Shell; + uint32_t Counter = 0; - BRepGraph_RefUID(const BRepGraph_RefId::Kind theKind, - const size_t theCounter, - const uint32_t theGeneration) - : myCounter(theCounter), - myKind(theKind), - myGeneration(theGeneration) + BRepGraph_RefUID() = default; + + BRepGraph_RefUID(const BRepGraph_RefId::Kind theKind, const uint32_t theCounter) + : Kind(theKind), + Counter(theCounter) { - Standard_ASSERT_VOID(theCounter > 0, "BRepGraph_RefUID: counter must be > 0 for valid UIDs"); } static BRepGraph_RefUID Invalid() { return BRepGraph_RefUID(); } - [[nodiscard]] bool IsValid() const { return myCounter > 0; } + //! True if this UID has a valid kind and a non-zero counter. + [[nodiscard]] bool IsValid() const { return Counter > 0 && BRepGraph_RefId::IsValidKind(Kind); } - [[nodiscard]] BRepGraph_RefId::Kind Kind() const { return myKind; } - - [[nodiscard]] size_t Counter() const { return myCounter; } - - [[nodiscard]] uint32_t Generation() const { return myGeneration; } - - bool operator==(const BRepGraph_RefUID& theOther) const + friend bool operator==(const BRepGraph_RefUID& theLeft, const BRepGraph_RefUID& theRight) noexcept { - if (myCounter == 0 || theOther.myCounter == 0) - return (myCounter == 0) == (theOther.myCounter == 0); - return myKind == theOther.myKind && myCounter == theOther.myCounter; + if (theLeft.Counter == 0 || theRight.Counter == 0) + { + return (theLeft.Counter == 0) == (theRight.Counter == 0); + } + return theLeft.Kind == theRight.Kind && theLeft.Counter == theRight.Counter; } - bool operator!=(const BRepGraph_RefUID& theOther) const { return !(*this == theOther); } - - bool operator<(const BRepGraph_RefUID& theOther) const + friend bool operator!=(const BRepGraph_RefUID& theLeft, const BRepGraph_RefUID& theRight) noexcept { - if (myKind != theOther.myKind) - return static_cast(myKind) < static_cast(theOther.myKind); - return myCounter < theOther.myCounter; + return !(theLeft == theRight); } - [[nodiscard]] size_t HashValue() const + friend bool operator<(const BRepGraph_RefUID& theLeft, const BRepGraph_RefUID& theRight) noexcept { + if (theLeft.Kind != theRight.Kind) + { + return static_cast(theLeft.Kind) < static_cast(theRight.Kind); + } + return theLeft.Counter < theRight.Counter; + } + + [[nodiscard]] size_t HashValue() const noexcept + { + if (Counter == 0) + { + return opencascade::hash(0); + } size_t aCombination[2]; - aCombination[0] = opencascade::hash(static_cast(myKind)); - aCombination[1] = opencascade::hash(myCounter); + aCombination[0] = opencascade::hash(static_cast(Kind)); + aCombination[1] = opencascade::hash(Counter); return opencascade::hashBytes(aCombination, sizeof(aCombination)); } - -private: - size_t myCounter; - BRepGraph_RefId::Kind myKind; - uint32_t myGeneration; }; +static_assert(sizeof(BRepGraph_RefUID) <= 8, "BRepGraph_RefUID must stay compact"); + template <> struct std::hash { diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsIterator.hxx index e259343ab5..62de003ee0 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsIterator.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsIterator.hxx @@ -17,8 +17,10 @@ #include #include #include - +#include #include +#include +#include //! @brief Single-level typed iterators over active child reference ids. //! @@ -37,7 +39,7 @@ struct RefTraits { using RefId = BRepGraph_ShellRefId; - static int Count(const BRepGraph& theGraph) { return theGraph.Refs().Shells().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Refs().Shells().Nb(); } static const BRepGraphInc::ShellRef& Get(const BRepGraph& theGraph, const RefId theRefId) { @@ -50,7 +52,7 @@ struct RefTraits { using RefId = BRepGraph_FaceRefId; - static int Count(const BRepGraph& theGraph) { return theGraph.Refs().Faces().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Refs().Faces().Nb(); } static const BRepGraphInc::FaceRef& Get(const BRepGraph& theGraph, const RefId theRefId) { @@ -63,7 +65,7 @@ struct RefTraits { using RefId = BRepGraph_WireRefId; - static int Count(const BRepGraph& theGraph) { return theGraph.Refs().Wires().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Refs().Wires().Nb(); } static const BRepGraphInc::WireRef& Get(const BRepGraph& theGraph, const RefId theRefId) { @@ -71,25 +73,12 @@ struct RefTraits } }; -template <> -struct RefTraits -{ - using RefId = BRepGraph_CoEdgeRefId; - - static int Count(const BRepGraph& theGraph) { return theGraph.Refs().CoEdges().Nb(); } - - static const BRepGraphInc::CoEdgeRef& Get(const BRepGraph& theGraph, const RefId theRefId) - { - return theGraph.Refs().CoEdges().Entry(theRefId); - } -}; - template <> struct RefTraits { using RefId = BRepGraph_VertexRefId; - static int Count(const BRepGraph& theGraph) { return theGraph.Refs().Vertices().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Refs().Vertices().Nb(); } static const BRepGraphInc::VertexRef& Get(const BRepGraph& theGraph, const RefId theRefId) { @@ -102,7 +91,7 @@ struct RefTraits { using RefId = BRepGraph_SolidRefId; - static int Count(const BRepGraph& theGraph) { return theGraph.Refs().Solids().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Refs().Solids().Nb(); } static const BRepGraphInc::SolidRef& Get(const BRepGraph& theGraph, const RefId theRefId) { @@ -115,7 +104,7 @@ struct RefTraits { using RefId = BRepGraph_ChildRefId; - static int Count(const BRepGraph& theGraph) { return theGraph.Refs().Children().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Refs().Children().Nb(); } static const BRepGraphInc::ChildRef& Get(const BRepGraph& theGraph, const RefId theRefId) { @@ -128,7 +117,7 @@ struct RefTraits { using RefId = BRepGraph_OccurrenceRefId; - static int Count(const BRepGraph& theGraph) { return theGraph.Refs().Occurrences().Nb(); } + static uint32_t Count(const BRepGraph& theGraph) { return theGraph.Refs().Occurrences().Nb(); } static const BRepGraphInc::OccurrenceRef& Get(const BRepGraph& theGraph, const RefId theRefId) { @@ -187,7 +176,7 @@ private: { if constexpr (!TheFullTraverse) { - while (myCurrent < myLength && Current().IsRemoved) + while (myCurrent < myLength && myCurrent.IsRemoved(myGraph)) { ++myCurrent; } @@ -205,34 +194,24 @@ struct BaseTraits using ParentId = ParentIdT; using RefId = RefIdT; using RefEntry = RefEntryT; + + static constexpr bool THE_IS_DIRECT = false; }; -template -inline const BRepGraphInc::BaseDef* childBaseDef(const BRepGraph& theGraph, - const ChildIdT theChildId) -{ - return theGraph.Topo().Gen().TopoEntity(BRepGraph_NodeId(theChildId)); -} - -inline const BRepGraphInc::BaseDef* childBaseDef(const BRepGraph& theGraph, - const BRepGraph_NodeId theChildId) -{ - return theGraph.Topo().Gen().TopoEntity(theChildId); -} - struct ShellOfSolidTraits : public BaseTraits { + using ChildId = BRepGraph_ShellId; + static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().Solids().Nb()) - && !theGraph.Topo().Solids().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().Solids().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Solids().Definition(theParent).ShellRefIds; + return theGraph.Topo().Solids().Relations(theParent).ShellRefIds; } static const BRepGraphInc::ShellRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -242,23 +221,24 @@ struct ShellOfSolidTraits static BRepGraph_ShellId ChildIdOf(const BRepGraph&, const BRepGraphInc::ShellRef& theRef) { - return theRef.ShellDefId; + return theRef.ChildShellId; } }; struct FaceOfShellTraits : public BaseTraits { + using ChildId = BRepGraph_FaceId; + static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().Shells().Nb()) - && !theGraph.Topo().Shells().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().Shells().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Shells().Definition(theParent).FaceRefIds; + return theGraph.Topo().Shells().Relations(theParent).FaceRefIds; } static const BRepGraphInc::FaceRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -268,49 +248,24 @@ struct FaceOfShellTraits static BRepGraph_FaceId ChildIdOf(const BRepGraph&, const BRepGraphInc::FaceRef& theRef) { - return theRef.FaceDefId; - } -}; - -struct ChildOfShellTraits - : public BaseTraits -{ - static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) - { - return theParent.IsValid(theGraph.Topo().Shells().Nb()) - && !theGraph.Topo().Shells().Definition(theParent).IsRemoved; - } - - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, - const ParentId theParent) - { - return theGraph.Topo().Shells().Definition(theParent).AuxChildRefIds; - } - - static const BRepGraphInc::ChildRef& Ref(const BRepGraph& theGraph, const RefId theRefId) - { - return theGraph.Refs().Children().Entry(theRefId); - } - - static BRepGraph_NodeId ChildIdOf(const BRepGraph&, const BRepGraphInc::ChildRef& theRef) - { - return theRef.ChildDefId; + return theRef.ChildFaceId; } }; struct WireOfFaceTraits : public BaseTraits { + using ChildId = BRepGraph_WireId; + static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().Faces().Nb()) - && !theGraph.Topo().Faces().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().Faces().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Faces().Definition(theParent).WireRefIds; + return theGraph.Topo().Faces().Relations(theParent).WireRefIds; } static const BRepGraphInc::WireRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -320,75 +275,48 @@ struct WireOfFaceTraits static BRepGraph_WireId ChildIdOf(const BRepGraph&, const BRepGraphInc::WireRef& theRef) { - return theRef.WireDefId; - } -}; - -struct VertexOfFaceTraits - : public BaseTraits -{ - static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) - { - return theParent.IsValid(theGraph.Topo().Faces().Nb()) - && !theGraph.Topo().Faces().Definition(theParent).IsRemoved; - } - - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, - const ParentId theParent) - { - return theGraph.Topo().Faces().Definition(theParent).VertexRefIds; - } - - static const BRepGraphInc::VertexRef& Ref(const BRepGraph& theGraph, const RefId theRefId) - { - return theGraph.Refs().Vertices().Entry(theRefId); - } - - static BRepGraph_VertexId ChildIdOf(const BRepGraph&, const BRepGraphInc::VertexRef& theRef) - { - return theRef.VertexDefId; + return theRef.ChildWireId; } }; struct CoEdgeOfWireTraits - : public BaseTraits + : public BaseTraits { + using ChildId = BRepGraph_CoEdgeId; + + static constexpr bool THE_IS_DIRECT = true; + static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().Wires().Nb()) - && !theGraph.Topo().Wires().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().Wires().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Wires().Definition(theParent).CoEdgeRefIds; + return theGraph.Topo().Wires().Relations(theParent).CoEdgeIds; } - static const BRepGraphInc::CoEdgeRef& Ref(const BRepGraph& theGraph, const RefId theRefId) + static const BRepGraphInc::CoEdgeDef& Ref(const BRepGraph& theGraph, const RefId theRefId) { - return theGraph.Refs().CoEdges().Entry(theRefId); - } - - static BRepGraph_CoEdgeId ChildIdOf(const BRepGraph&, const BRepGraphInc::CoEdgeRef& theRef) - { - return theRef.CoEdgeDefId; + return theGraph.Topo().CoEdges().Definition(theRefId); } }; struct SolidOfCompSolidTraits : public BaseTraits { + using ChildId = BRepGraph_SolidId; + static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().CompSolids().Nb()) - && !theGraph.Topo().CompSolids().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().CompSolids().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().CompSolids().Definition(theParent).SolidRefIds; + return theGraph.Topo().CompSolids().Relations(theParent).SolidRefIds; } static const BRepGraphInc::SolidRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -398,49 +326,24 @@ struct SolidOfCompSolidTraits static BRepGraph_SolidId ChildIdOf(const BRepGraph&, const BRepGraphInc::SolidRef& theRef) { - return theRef.SolidDefId; - } -}; - -struct ChildOfSolidTraits - : public BaseTraits -{ - static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) - { - return theParent.IsValid(theGraph.Topo().Solids().Nb()) - && !theGraph.Topo().Solids().Definition(theParent).IsRemoved; - } - - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, - const ParentId theParent) - { - return theGraph.Topo().Solids().Definition(theParent).AuxChildRefIds; - } - - static const BRepGraphInc::ChildRef& Ref(const BRepGraph& theGraph, const RefId theRefId) - { - return theGraph.Refs().Children().Entry(theRefId); - } - - static BRepGraph_NodeId ChildIdOf(const BRepGraph&, const BRepGraphInc::ChildRef& theRef) - { - return theRef.ChildDefId; + return theRef.ChildSolidId; } }; struct ChildOfCompoundTraits : public BaseTraits { + using ChildId = BRepGraph_NodeId; + static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().Compounds().Nb()) - && !theGraph.Topo().Compounds().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().Compounds().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Compounds().Definition(theParent).ChildRefIds; + return theGraph.Topo().Compounds().Relations(theParent).ChildRefIds; } static const BRepGraphInc::ChildRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -450,23 +353,24 @@ struct ChildOfCompoundTraits static BRepGraph_NodeId ChildIdOf(const BRepGraph&, const BRepGraphInc::ChildRef& theRef) { - return theRef.ChildDefId; + return theRef.ChildNodeId; } }; struct OccurrenceOfProductTraits : public BaseTraits { + using ChildId = BRepGraph_OccurrenceId; + static bool IsParentValid(const BRepGraph& theGraph, const ParentId theParent) { - return theParent.IsValid(theGraph.Topo().Products().Nb()) - && !theGraph.Topo().Products().Definition(theParent).IsRemoved; + return theParent.IsValid(theGraph.Topo().Products().Nb()) && !theParent.IsRemoved(theGraph); } - static const NCollection_DynamicArray& RefIds(const BRepGraph& theGraph, + static const NCollection_LinearVector& RefIds(const BRepGraph& theGraph, const ParentId theParent) { - return theGraph.Topo().Products().Definition(theParent).OccurrenceRefIds; + return theGraph.Topo().Products().Relations(theParent).OccurrenceRefIds; } static const BRepGraphInc::OccurrenceRef& Ref(const BRepGraph& theGraph, const RefId theRefId) @@ -477,7 +381,7 @@ struct OccurrenceOfProductTraits static BRepGraph_OccurrenceId ChildIdOf(const BRepGraph&, const BRepGraphInc::OccurrenceRef& theRef) { - return theRef.OccurrenceDefId; + return theRef.ChildOccurrenceId; } }; @@ -487,6 +391,7 @@ class RefsOfParent public: using ParentId = typename TraitsT::ParentId; using RefId = typename TraitsT::RefId; + using ChildId = typename TraitsT::ChildId; RefsOfParent(const BRepGraph& theGraph, const ParentId theParent) : myGraph(theGraph) @@ -498,6 +403,26 @@ public: myRefIds = &TraitsT::RefIds(theGraph, theParent); myLength = static_cast(myRefIds->Size()); + if constexpr (std::is_convertible_v) + { + myNbRefs = theGraph.Topo().Gen().Nb(BRepGraph_NodeId(RefId()).NodeKind); + } + else + { + myNbRefs = theGraph.Refs().Gen().Nb(BRepGraph_RefId(RefId()).RefKind); + } + + if constexpr (TraitsT::THE_IS_DIRECT) + { + myNbChildren = myNbRefs; + } + else + { + if constexpr (!std::is_same_v) + { + myNbChildren = theGraph.Topo().Gen().Nb(BRepGraph_NodeId(ChildId()).NodeKind); + } + } skipRemoved(); } @@ -527,13 +452,28 @@ private: { while (myRefIds != nullptr && myIndex < myLength) { - const typename TraitsT::RefEntry& aRef = - TraitsT::Ref(myGraph, myRefIds->Value(static_cast(myIndex))); - if (!aRef.IsRemoved) + const RefId aRefId = myRefIds->Value(static_cast(myIndex)); + if (aRefId.IsValid(myNbRefs) && !aRefId.IsRemoved(myGraph)) { - const BRepGraphInc::BaseDef* aChildDef = - childBaseDef(myGraph, TraitsT::ChildIdOf(myGraph, aRef)); - if (aChildDef != nullptr && !aChildDef->IsRemoved) + const auto aChildId = [&]() { + if constexpr (TraitsT::THE_IS_DIRECT) + { + return aRefId; + } + else + { + const typename TraitsT::RefEntry& aRef = TraitsT::Ref(myGraph, aRefId); + return TraitsT::ChildIdOf(myGraph, aRef); + } + }(); + if constexpr (std::is_same_v) + { + if (myGraph.Topo().Gen().IsActive(aChildId)) + { + return; + } + } + else if (aChildId.IsValid(myNbChildren) && !aChildId.IsRemoved(myGraph)) { return; } @@ -543,14 +483,16 @@ private: } const BRepGraph& myGraph; - const NCollection_DynamicArray* myRefIds = nullptr; - uint32_t myIndex = 0; - uint32_t myLength = 0; + const NCollection_LinearVector* myRefIds = nullptr; + uint32_t myIndex = 0; + uint32_t myLength = 0; + uint32_t myNbRefs = 0; + uint32_t myNbChildren = 0; }; -//! @brief Direct active vertex reference ids of an edge. +//! @brief Direct active boundary vertex reference ids of an edge. //! -//! Iteration order is start vertex, end vertex, then internal/external vertices. +//! Iteration order is start vertex, then end vertex. class RefsVertexOfEdge { public: @@ -559,14 +501,15 @@ public: RefsVertexOfEdge(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdgeId) : myGraph(theGraph) { - if (!theEdgeId.IsValid(theGraph.Topo().Edges().Nb()) - || theGraph.Topo().Edges().Definition(theEdgeId).IsRemoved) + if (!theEdgeId.IsValid(theGraph.Topo().Edges().Nb()) || theEdgeId.IsRemoved(theGraph)) { return; } - myEdge = &theGraph.Topo().Edges().Definition(theEdgeId); - myLength = 2u + static_cast(myEdge->InternalVertexRefIds.Size()); + myEdge = &theGraph.Topo().Edges().Definition(theEdgeId); + myLength = 2u; + myNbVertexRefs = theGraph.Refs().Vertices().Nb(); + myNbVertices = theGraph.Topo().Vertices().Nb(); skipRemoved(); } @@ -598,11 +541,7 @@ private: { return myEdge->StartVertexRefId; } - if (theIndex == 1) - { - return myEdge->EndVertexRefId; - } - return myEdge->InternalVertexRefIds.Value(static_cast(theIndex - 2)); + return myEdge->EndVertexRefId; } void skipRemoved() @@ -610,27 +549,26 @@ private: while (myEdge != nullptr && myIndex < myLength) { const RefId aRefId = refIdAt(myIndex); - if (aRefId.IsValid()) + if (aRefId.IsValid(myNbVertexRefs) && !myGraph.Refs().Gen().IsRemoved(aRefId)) { const BRepGraphInc::VertexRef& aRef = myGraph.Refs().Vertices().Entry(aRefId); - if (!aRef.IsRemoved) + if (!aRef.ChildVertexId.IsValid(myNbVertices) || aRef.ChildVertexId.IsRemoved(myGraph)) { - const BRepGraphInc::BaseDef* aChildDef = - myGraph.Topo().Gen().TopoEntity(BRepGraph_NodeId(aRef.VertexDefId)); - if (aChildDef != nullptr && !aChildDef->IsRemoved) - { - return; - } + ++myIndex; + continue; } + return; } ++myIndex; } } const BRepGraph& myGraph; - const BRepGraphInc::EdgeDef* myEdge = nullptr; - uint32_t myIndex = 0; - uint32_t myLength = 0; + const BRepGraphInc::EdgeDef* myEdge = nullptr; + uint32_t myIndex = 0; + uint32_t myLength = 0; + uint32_t myNbVertexRefs = 0; + uint32_t myNbVertices = 0; }; } // namespace BRepGraph_RefsIterator @@ -639,16 +577,10 @@ using BRepGraph_RefsShellOfSolid = BRepGraph_RefsIterator::RefsOfParent; using BRepGraph_RefsFaceOfShell = BRepGraph_RefsIterator::RefsOfParent; -using BRepGraph_RefsChildOfShell = - BRepGraph_RefsIterator::RefsOfParent; using BRepGraph_RefsWireOfFace = BRepGraph_RefsIterator::RefsOfParent; -using BRepGraph_RefsVertexOfFace = - BRepGraph_RefsIterator::RefsOfParent; -using BRepGraph_RefsCoEdgeOfWire = +using BRepGraph_CoEdgesOfWire = BRepGraph_RefsIterator::RefsOfParent; -using BRepGraph_RefsChildOfSolid = - BRepGraph_RefsIterator::RefsOfParent; using BRepGraph_RefsSolidOfCompSolid = BRepGraph_RefsIterator::RefsOfParent; using BRepGraph_RefsChildOfCompound = @@ -660,7 +592,6 @@ using BRepGraph_RefsVertexOfEdge = BRepGraph_RefsIterator::RefsVertexOfEdge; using BRepGraph_ShellRefIterator = BRepGraph_RefsIterator::RefIterator; using BRepGraph_FaceRefIterator = BRepGraph_RefsIterator::RefIterator; using BRepGraph_WireRefIterator = BRepGraph_RefsIterator::RefIterator; -using BRepGraph_CoEdgeRefIterator = BRepGraph_RefsIterator::RefIterator; using BRepGraph_VertexRefIterator = BRepGraph_RefsIterator::RefIterator; using BRepGraph_SolidRefIterator = BRepGraph_RefsIterator::RefIterator; using BRepGraph_ChildRefIterator = BRepGraph_RefsIterator::RefIterator; @@ -673,8 +604,6 @@ using BRepGraph_FullFaceRefIterator = BRepGraph_RefsIterator::RefIterator; using BRepGraph_FullWireRefIterator = BRepGraph_RefsIterator::RefIterator; -using BRepGraph_FullCoEdgeRefIterator = - BRepGraph_RefsIterator::RefIterator; using BRepGraph_FullVertexRefIterator = BRepGraph_RefsIterator::RefIterator; using BRepGraph_FullSolidRefIterator = @@ -684,4 +613,4 @@ using BRepGraph_FullChildRefIterator = using BRepGraph_FullOccurrenceRefIterator = BRepGraph_RefsIterator::RefIterator; -#endif // _BRepGraph_RefsIterator_HeaderFile \ No newline at end of file +#endif // _BRepGraph_RefsIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.cxx index 6e8d6ebc07..26b43b019f 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.cxx @@ -19,112 +19,98 @@ //================================================================================================= -int BRepGraph::RefsView::ShellOps::Nb() const +uint32_t BRepGraph::RefsView::ShellOps::Nb() const { return myGraph->myData->myIncStorage.NbShellRefs(); } //================================================================================================= -int BRepGraph::RefsView::ShellOps::NbActive() const +uint32_t BRepGraph::RefsView::ShellOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveShellRefs(); } //================================================================================================= -int BRepGraph::RefsView::FaceOps::Nb() const +uint32_t BRepGraph::RefsView::FaceOps::Nb() const { return myGraph->myData->myIncStorage.NbFaceRefs(); } //================================================================================================= -int BRepGraph::RefsView::FaceOps::NbActive() const +uint32_t BRepGraph::RefsView::FaceOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveFaceRefs(); } //================================================================================================= -int BRepGraph::RefsView::WireOps::Nb() const +uint32_t BRepGraph::RefsView::WireOps::Nb() const { return myGraph->myData->myIncStorage.NbWireRefs(); } //================================================================================================= -int BRepGraph::RefsView::WireOps::NbActive() const +uint32_t BRepGraph::RefsView::WireOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveWireRefs(); } //================================================================================================= -int BRepGraph::RefsView::CoEdgeOps::Nb() const -{ - return myGraph->myData->myIncStorage.NbCoEdgeRefs(); -} - -//================================================================================================= - -int BRepGraph::RefsView::CoEdgeOps::NbActive() const -{ - return myGraph->myData->myIncStorage.NbActiveCoEdgeRefs(); -} - -//================================================================================================= - -int BRepGraph::RefsView::VertexOps::Nb() const +uint32_t BRepGraph::RefsView::VertexOps::Nb() const { return myGraph->myData->myIncStorage.NbVertexRefs(); } //================================================================================================= -int BRepGraph::RefsView::VertexOps::NbActive() const +uint32_t BRepGraph::RefsView::VertexOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveVertexRefs(); } //================================================================================================= -int BRepGraph::RefsView::SolidOps::Nb() const +uint32_t BRepGraph::RefsView::SolidOps::Nb() const { return myGraph->myData->myIncStorage.NbSolidRefs(); } //================================================================================================= -int BRepGraph::RefsView::SolidOps::NbActive() const +uint32_t BRepGraph::RefsView::SolidOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveSolidRefs(); } //================================================================================================= -int BRepGraph::RefsView::ChildOps::Nb() const +uint32_t BRepGraph::RefsView::ChildOps::Nb() const { return myGraph->myData->myIncStorage.NbChildRefs(); } //================================================================================================= -int BRepGraph::RefsView::ChildOps::NbActive() const +uint32_t BRepGraph::RefsView::ChildOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveChildRefs(); } //================================================================================================= -int BRepGraph::RefsView::OccurrenceOps::Nb() const +uint32_t BRepGraph::RefsView::OccurrenceOps::Nb() const { return myGraph->myData->myIncStorage.NbOccurrenceRefs(); } //================================================================================================= -int BRepGraph::RefsView::OccurrenceOps::NbActive() const +uint32_t BRepGraph::RefsView::OccurrenceOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveOccurrenceRefs(); } @@ -155,14 +141,6 @@ const BRepGraphInc::WireRef& BRepGraph::RefsView::WireOps::Entry( //================================================================================================= -const BRepGraphInc::CoEdgeRef& BRepGraph::RefsView::CoEdgeOps::Entry( - const BRepGraph_CoEdgeRefId theRefId) const -{ - return myGraph->myData->myIncStorage.CoEdgeRef(theRefId); -} - -//================================================================================================= - const BRepGraphInc::VertexRef& BRepGraph::RefsView::VertexOps::Entry( const BRepGraph_VertexRefId theRefId) const { @@ -195,88 +173,135 @@ const BRepGraphInc::OccurrenceRef& BRepGraph::RefsView::OccurrenceOps::Entry( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::RefsView::FaceOps::IdsOf( +const NCollection_LinearVector& BRepGraph::RefsView::FaceOps::IdsOf( const BRepGraph_ShellId theShell) const { - static const NCollection_DynamicArray anEmpty; + static const NCollection_LinearVector anEmpty; if (!theShell.IsValid(myGraph->myData->myIncStorage.NbShells())) { return anEmpty; } - return myGraph->myData->myIncStorage.Shell(theShell).FaceRefIds; + return myGraph->Topo().Shells().Relations(theShell).FaceRefIds; } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::RefsView::WireOps::IdsOf( +const NCollection_LinearVector& BRepGraph::RefsView::WireOps::IdsOf( const BRepGraph_FaceId theFace) const { - static const NCollection_DynamicArray anEmpty; + static const NCollection_LinearVector anEmpty; if (!theFace.IsValid(myGraph->myData->myIncStorage.NbFaces())) { return anEmpty; } - return myGraph->myData->myIncStorage.Face(theFace).WireRefIds; + return myGraph->Topo().Faces().Relations(theFace).WireRefIds; } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::RefsView::CoEdgeOps::IdsOf( - const BRepGraph_WireId theWire) const -{ - static const NCollection_DynamicArray anEmpty; - if (!theWire.IsValid(myGraph->myData->myIncStorage.NbWires())) - { - return anEmpty; - } - return myGraph->myData->myIncStorage.Wire(theWire).CoEdgeRefIds; -} - -//================================================================================================= - -const NCollection_DynamicArray& BRepGraph::RefsView::ShellOps::IdsOf( +const NCollection_LinearVector& BRepGraph::RefsView::ShellOps::IdsOf( const BRepGraph_SolidId theSolid) const { - static const NCollection_DynamicArray anEmpty; + static const NCollection_LinearVector anEmpty; if (!theSolid.IsValid(myGraph->myData->myIncStorage.NbSolids())) { return anEmpty; } - return myGraph->myData->myIncStorage.Solid(theSolid).ShellRefIds; + return myGraph->Topo().Solids().Relations(theSolid).ShellRefIds; } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::RefsView::ChildOps::IdsOf( +const NCollection_LinearVector& BRepGraph::RefsView::ChildOps::IdsOf( const BRepGraph_CompoundId theCompound) const { - static const NCollection_DynamicArray anEmpty; + static const NCollection_LinearVector anEmpty; if (!theCompound.IsValid(myGraph->myData->myIncStorage.NbCompounds())) { return anEmpty; } - return myGraph->myData->myIncStorage.Compound(theCompound).ChildRefIds; + return myGraph->Topo().Compounds().Relations(theCompound).ChildRefIds; } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::RefsView::OccurrenceOps:: +const NCollection_LinearVector& BRepGraph::RefsView::ChildOps::IdsReferencing( + const BRepGraph_NodeId theChild) const +{ + return myGraph->myData->myIncStorage.CompoundRefsOfNode(theChild); +} + +//================================================================================================= + +const NCollection_LinearVector& BRepGraph::RefsView::OccurrenceOps:: IdsOf(const BRepGraph_ProductId theProduct) const { - static const NCollection_DynamicArray anEmpty; + static const NCollection_LinearVector anEmpty; if (!theProduct.IsValid(myGraph->myData->myIncStorage.NbProducts())) { return anEmpty; } - return myGraph->myData->myIncStorage.Product(theProduct).OccurrenceRefIds; + return myGraph->Topo().Products().Relations(theProduct).OccurrenceRefIds; } //================================================================================================= -BRepGraph_RefId BRepGraph::RefsView::RefAtStep(const BRepGraph_NodeId theParent, - const int theStep) const +const NCollection_LinearVector& BRepGraph::RefsView::OccurrenceOps:: + IdsReferencing(const BRepGraph_NodeId theChild) const { - if (!theParent.IsValid() || theStep < 0) + return myGraph->myData->myIncStorage.OccurrenceRefsOfNode(theChild); +} + +//================================================================================================= + +uint32_t BRepGraph::RefsView::GenOps::Nb(const BRepGraph_RefId::Kind theKind) const +{ + const BRepGraphInc_Storage& aS = myGraph->myData->myIncStorage; + switch (theKind) + { + case BRepGraph_RefId::Kind::Shell: + return aS.NbShellRefs(); + case BRepGraph_RefId::Kind::Face: + return aS.NbFaceRefs(); + case BRepGraph_RefId::Kind::Wire: + return aS.NbWireRefs(); + case BRepGraph_RefId::Kind::Vertex: + return aS.NbVertexRefs(); + case BRepGraph_RefId::Kind::Solid: + return aS.NbSolidRefs(); + case BRepGraph_RefId::Kind::Child: + return aS.NbChildRefs(); + case BRepGraph_RefId::Kind::Occurrence: + return aS.NbOccurrenceRefs(); + } + + return 0; +} + +//================================================================================================= + +bool BRepGraph::RefsView::GenOps::IsValid(const BRepGraph_RefId theRef) const +{ + if (!theRef.IsValid()) + { + return false; + } + return theRef.Index < Nb(theRef.RefKind); +} + +//================================================================================================= + +bool BRepGraph::RefsView::GenOps::IsActive(const BRepGraph_RefId theRef) const +{ + return IsValid(theRef) && !theRef.IsRemoved(*myGraph); +} + +//================================================================================================= + +BRepGraph_RefId BRepGraph::RefsView::GenOps::RefAtStep(const BRepGraph_NodeId theParent, + const int theStep) const +{ + if (!myGraph->Topo().Gen().IsActive(theParent) || theStep < 0) { return BRepGraph_RefId(); } @@ -284,58 +309,39 @@ BRepGraph_RefId BRepGraph::RefsView::RefAtStep(const BRepGraph_NodeId theParent, switch (theParent.NodeKind) { case BRepGraph_NodeId::Kind::Compound: { - const BRepGraphInc::CompoundDef& aCompound = - myGraph->Topo().Compounds().Definition(BRepGraph_CompoundId::FromNodeId(theParent)); - return theStep < aCompound.ChildRefIds.Length() ? aCompound.ChildRefIds.Value(theStep) - : BRepGraph_RefId(); + const BRepGraphInc::CompoundRelations& aRel = + myGraph->Topo().Compounds().Relations(BRepGraph_CompoundId::FromNodeId(theParent)); + return static_cast(theStep) < aRel.ChildRefIds.Size() + ? aRel.ChildRefIds.Value(static_cast(theStep)) + : BRepGraph_RefId(); } case BRepGraph_NodeId::Kind::CompSolid: { - const BRepGraphInc::CompSolidDef& aCompSolid = - myGraph->Topo().CompSolids().Definition(BRepGraph_CompSolidId::FromNodeId(theParent)); - return theStep < aCompSolid.SolidRefIds.Length() ? aCompSolid.SolidRefIds.Value(theStep) - : BRepGraph_RefId(); + const BRepGraphInc::CompSolidRelations& aRel = + myGraph->Topo().CompSolids().Relations(BRepGraph_CompSolidId::FromNodeId(theParent)); + return static_cast(theStep) < aRel.SolidRefIds.Size() + ? aRel.SolidRefIds.Value(static_cast(theStep)) + : BRepGraph_RefId(); } case BRepGraph_NodeId::Kind::Solid: { - const BRepGraphInc::SolidDef& aSolid = - myGraph->Topo().Solids().Definition(BRepGraph_SolidId::FromNodeId(theParent)); - if (theStep < aSolid.ShellRefIds.Length()) - { - return aSolid.ShellRefIds.Value(theStep); - } - - const int aFreeIdx = theStep - aSolid.ShellRefIds.Length(); - return aFreeIdx < aSolid.AuxChildRefIds.Length() ? aSolid.AuxChildRefIds.Value(aFreeIdx) - : BRepGraph_RefId(); + const BRepGraphInc::SolidRelations& aRel = + myGraph->Topo().Solids().Relations(BRepGraph_SolidId::FromNodeId(theParent)); + return static_cast(theStep) < aRel.ShellRefIds.Size() + ? aRel.ShellRefIds.Value(static_cast(theStep)) + : BRepGraph_RefId(); } case BRepGraph_NodeId::Kind::Shell: { - const BRepGraphInc::ShellDef& aShell = - myGraph->Topo().Shells().Definition(BRepGraph_ShellId::FromNodeId(theParent)); - if (theStep < aShell.FaceRefIds.Length()) - { - return aShell.FaceRefIds.Value(theStep); - } - - const int aFreeIdx = theStep - aShell.FaceRefIds.Length(); - return aFreeIdx < aShell.AuxChildRefIds.Length() ? aShell.AuxChildRefIds.Value(aFreeIdx) - : BRepGraph_RefId(); + const BRepGraphInc::ShellRelations& aRel = + myGraph->Topo().Shells().Relations(BRepGraph_ShellId::FromNodeId(theParent)); + return static_cast(theStep) < aRel.FaceRefIds.Size() + ? aRel.FaceRefIds.Value(static_cast(theStep)) + : BRepGraph_RefId(); } case BRepGraph_NodeId::Kind::Face: { - const BRepGraphInc::FaceDef& aFace = - myGraph->Topo().Faces().Definition(BRepGraph_FaceId::FromNodeId(theParent)); - if (theStep < aFace.WireRefIds.Length()) - { - return aFace.WireRefIds.Value(theStep); - } - - const int aVertexIdx = theStep - aFace.WireRefIds.Length(); - return aVertexIdx < aFace.VertexRefIds.Length() ? aFace.VertexRefIds.Value(aVertexIdx) - : BRepGraph_RefId(); - } - case BRepGraph_NodeId::Kind::Wire: { - const BRepGraphInc::WireDef& aWire = - myGraph->Topo().Wires().Definition(BRepGraph_WireId::FromNodeId(theParent)); - return theStep < aWire.CoEdgeRefIds.Length() ? aWire.CoEdgeRefIds.Value(theStep) - : BRepGraph_RefId(); + const BRepGraphInc::FaceRelations& aRel = + myGraph->Topo().Faces().Relations(BRepGraph_FaceId::FromNodeId(theParent)); + return static_cast(theStep) < aRel.WireRefIds.Size() + ? aRel.WireRefIds.Value(static_cast(theStep)) + : BRepGraph_RefId(); } case BRepGraph_NodeId::Kind::Edge: { const BRepGraphInc::EdgeDef& anEdge = @@ -348,18 +354,16 @@ BRepGraph_RefId BRepGraph::RefsView::RefAtStep(const BRepGraph_NodeId theParent, { return anEdge.EndVertexRefId; } - - const int anInternalIdx = theStep - 2; - return anInternalIdx < anEdge.InternalVertexRefIds.Length() - ? anEdge.InternalVertexRefIds.Value(anInternalIdx) - : BRepGraph_RefId(); + return BRepGraph_RefId(); } case BRepGraph_NodeId::Kind::Product: { - const BRepGraphInc::ProductDef& aProduct = - myGraph->Topo().Products().Definition(BRepGraph_ProductId::FromNodeId(theParent)); - return theStep < aProduct.OccurrenceRefIds.Length() ? aProduct.OccurrenceRefIds.Value(theStep) - : BRepGraph_RefId(); + const BRepGraphInc::ProductRelations& aRel = + myGraph->Topo().Products().Relations(BRepGraph_ProductId::FromNodeId(theParent)); + return static_cast(theStep) < aRel.OccurrenceRefIds.Size() + ? aRel.OccurrenceRefIds.Value(static_cast(theStep)) + : BRepGraph_RefId(); } + case BRepGraph_NodeId::Kind::Wire: case BRepGraph_NodeId::Kind::CoEdge: case BRepGraph_NodeId::Kind::Occurrence: case BRepGraph_NodeId::Kind::Vertex: @@ -371,9 +375,9 @@ BRepGraph_RefId BRepGraph::RefsView::RefAtStep(const BRepGraph_NodeId theParent, //================================================================================================= -BRepGraph_NodeId BRepGraph::RefsView::ChildNode(const BRepGraph_RefId theRef) const +BRepGraph_NodeId BRepGraph::RefsView::GenOps::ChildNode(const BRepGraph_RefId theRef) const { - if (!theRef.IsValid()) + if (!IsValid(theRef)) { return BRepGraph_NodeId(); } @@ -381,21 +385,25 @@ BRepGraph_NodeId BRepGraph::RefsView::ChildNode(const BRepGraph_RefId theRef) co switch (theRef.RefKind) { case BRepGraph_RefId::Kind::Shell: - return Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).ShellDefId; + return myGraph->Refs().Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).ChildShellId; case BRepGraph_RefId::Kind::Face: - return Faces().Entry(BRepGraph_FaceRefId::FromRefId(theRef)).FaceDefId; + return myGraph->Refs().Faces().Entry(BRepGraph_FaceRefId::FromRefId(theRef)).ChildFaceId; case BRepGraph_RefId::Kind::Wire: - return Wires().Entry(BRepGraph_WireRefId::FromRefId(theRef)).WireDefId; - case BRepGraph_RefId::Kind::CoEdge: - return CoEdges().Entry(BRepGraph_CoEdgeRefId::FromRefId(theRef)).CoEdgeDefId; + return myGraph->Refs().Wires().Entry(BRepGraph_WireRefId::FromRefId(theRef)).ChildWireId; case BRepGraph_RefId::Kind::Vertex: - return Vertices().Entry(BRepGraph_VertexRefId::FromRefId(theRef)).VertexDefId; + return myGraph->Refs() + .Vertices() + .Entry(BRepGraph_VertexRefId::FromRefId(theRef)) + .ChildVertexId; case BRepGraph_RefId::Kind::Solid: - return Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).SolidDefId; + return myGraph->Refs().Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).ChildSolidId; case BRepGraph_RefId::Kind::Child: - return Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).ChildDefId; + return myGraph->Refs().Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).ChildNodeId; case BRepGraph_RefId::Kind::Occurrence: - return Occurrences().Entry(BRepGraph_OccurrenceRefId::FromRefId(theRef)).OccurrenceDefId; + return myGraph->Refs() + .Occurrences() + .Entry(BRepGraph_OccurrenceRefId::FromRefId(theRef)) + .ChildOccurrenceId; } return BRepGraph_NodeId(); @@ -403,9 +411,9 @@ BRepGraph_NodeId BRepGraph::RefsView::ChildNode(const BRepGraph_RefId theRef) co //================================================================================================= -bool BRepGraph::RefsView::IsRemoved(const BRepGraph_RefId theRef) const +bool BRepGraph::RefsView::GenOps::IsRemoved(const BRepGraph_RefId theRef) const { - if (!theRef.IsValid()) + if (!IsValid(theRef)) { return true; } @@ -413,21 +421,19 @@ bool BRepGraph::RefsView::IsRemoved(const BRepGraph_RefId theRef) const switch (theRef.RefKind) { case BRepGraph_RefId::Kind::Shell: - return Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).IsRemoved; + return myGraph->incStorage().IsRemoved(BRepGraph_ShellRefId::FromRefId(theRef)); case BRepGraph_RefId::Kind::Face: - return Faces().Entry(BRepGraph_FaceRefId::FromRefId(theRef)).IsRemoved; + return myGraph->incStorage().IsRemoved(BRepGraph_FaceRefId::FromRefId(theRef)); case BRepGraph_RefId::Kind::Wire: - return Wires().Entry(BRepGraph_WireRefId::FromRefId(theRef)).IsRemoved; - case BRepGraph_RefId::Kind::CoEdge: - return CoEdges().Entry(BRepGraph_CoEdgeRefId::FromRefId(theRef)).IsRemoved; + return myGraph->incStorage().IsRemoved(BRepGraph_WireRefId::FromRefId(theRef)); case BRepGraph_RefId::Kind::Vertex: - return Vertices().Entry(BRepGraph_VertexRefId::FromRefId(theRef)).IsRemoved; + return myGraph->incStorage().IsRemoved(BRepGraph_VertexRefId::FromRefId(theRef)); case BRepGraph_RefId::Kind::Solid: - return Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).IsRemoved; + return myGraph->incStorage().IsRemoved(BRepGraph_SolidRefId::FromRefId(theRef)); case BRepGraph_RefId::Kind::Child: - return Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).IsRemoved; + return myGraph->incStorage().IsRemoved(BRepGraph_ChildRefId::FromRefId(theRef)); case BRepGraph_RefId::Kind::Occurrence: - return Occurrences().Entry(BRepGraph_OccurrenceRefId::FromRefId(theRef)).IsRemoved; + return myGraph->incStorage().IsRemoved(BRepGraph_OccurrenceRefId::FromRefId(theRef)); } return true; @@ -435,9 +441,9 @@ bool BRepGraph::RefsView::IsRemoved(const BRepGraph_RefId theRef) const //================================================================================================= -TopLoc_Location BRepGraph::RefsView::LocalLocation(const BRepGraph_RefId theRef) const +TopLoc_Location BRepGraph::RefsView::GenOps::LocalLocation(const BRepGraph_RefId theRef) const { - if (!theRef.IsValid()) + if (!IsValid(theRef)) { return TopLoc_Location(); } @@ -445,21 +451,21 @@ TopLoc_Location BRepGraph::RefsView::LocalLocation(const BRepGraph_RefId theRef) switch (theRef.RefKind) { case BRepGraph_RefId::Kind::Shell: - return Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).LocalLocation; case BRepGraph_RefId::Kind::Face: - return Faces().Entry(BRepGraph_FaceRefId::FromRefId(theRef)).LocalLocation; case BRepGraph_RefId::Kind::Wire: - return Wires().Entry(BRepGraph_WireRefId::FromRefId(theRef)).LocalLocation; - case BRepGraph_RefId::Kind::CoEdge: - return CoEdges().Entry(BRepGraph_CoEdgeRefId::FromRefId(theRef)).LocalLocation; case BRepGraph_RefId::Kind::Vertex: - return Vertices().Entry(BRepGraph_VertexRefId::FromRefId(theRef)).LocalLocation; case BRepGraph_RefId::Kind::Solid: - return Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).LocalLocation; - case BRepGraph_RefId::Kind::Child: - return Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).LocalLocation; - case BRepGraph_RefId::Kind::Occurrence: return TopLoc_Location(); + case BRepGraph_RefId::Kind::Child: + return myGraph->Refs() + .Children() + .Entry(BRepGraph_ChildRefId::FromRefId(theRef)) + .LocalLocation; + case BRepGraph_RefId::Kind::Occurrence: + return myGraph->Refs() + .Occurrences() + .Entry(BRepGraph_OccurrenceRefId::FromRefId(theRef)) + .LocalLocation; } return TopLoc_Location(); @@ -467,9 +473,9 @@ TopLoc_Location BRepGraph::RefsView::LocalLocation(const BRepGraph_RefId theRef) //================================================================================================= -TopAbs_Orientation BRepGraph::RefsView::Orientation(const BRepGraph_RefId theRef) const +TopAbs_Orientation BRepGraph::RefsView::GenOps::Orientation(const BRepGraph_RefId theRef) const { - if (!theRef.IsValid()) + if (!IsValid(theRef)) { return TopAbs_FORWARD; } @@ -477,19 +483,17 @@ TopAbs_Orientation BRepGraph::RefsView::Orientation(const BRepGraph_RefId theRef switch (theRef.RefKind) { case BRepGraph_RefId::Kind::Shell: - return Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).Orientation; + return myGraph->Refs().Shells().Entry(BRepGraph_ShellRefId::FromRefId(theRef)).Orientation; case BRepGraph_RefId::Kind::Face: - return Faces().Entry(BRepGraph_FaceRefId::FromRefId(theRef)).Orientation; + return myGraph->Refs().Faces().Entry(BRepGraph_FaceRefId::FromRefId(theRef)).Orientation; case BRepGraph_RefId::Kind::Wire: - return Wires().Entry(BRepGraph_WireRefId::FromRefId(theRef)).Orientation; - case BRepGraph_RefId::Kind::CoEdge: - return TopAbs_FORWARD; + return myGraph->Refs().Wires().Entry(BRepGraph_WireRefId::FromRefId(theRef)).Orientation; case BRepGraph_RefId::Kind::Vertex: - return Vertices().Entry(BRepGraph_VertexRefId::FromRefId(theRef)).Orientation; + return myGraph->Refs().Vertices().Entry(BRepGraph_VertexRefId::FromRefId(theRef)).Orientation; case BRepGraph_RefId::Kind::Solid: - return Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).Orientation; + return myGraph->Refs().Solids().Entry(BRepGraph_SolidRefId::FromRefId(theRef)).Orientation; case BRepGraph_RefId::Kind::Child: - return Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).Orientation; + return myGraph->Refs().Children().Entry(BRepGraph_ChildRefId::FromRefId(theRef)).Orientation; case BRepGraph_RefId::Kind::Occurrence: return TopAbs_FORWARD; } @@ -499,15 +503,15 @@ TopAbs_Orientation BRepGraph::RefsView::Orientation(const BRepGraph_RefId theRef //================================================================================================= -const NCollection_DynamicArray& BRepGraph::RefsView::SolidOps::IdsOf( +const NCollection_LinearVector& BRepGraph::RefsView::SolidOps::IdsOf( const BRepGraph_CompSolidId theCompSolid) const { - static const NCollection_DynamicArray anEmpty; + static const NCollection_LinearVector anEmpty; if (!theCompSolid.IsValid(myGraph->myData->myIncStorage.NbCompSolids())) { return anEmpty; } - return myGraph->myData->myIncStorage.CompSolid(theCompSolid).SolidRefIds; + return myGraph->Topo().CompSolids().Relations(theCompSolid).SolidRefIds; } //================================================================================================= diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.hxx index dc9d323e3b..83b72526ce 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RefsView.hxx @@ -17,6 +17,7 @@ #include #include #include +#include //! @brief Read-only view for RefId/RefUID-based reference storage. //! @@ -24,13 +25,14 @@ //! - typed reference entry access (Shell, Face, ...) //! - reference counts //! - RefUID lookup and reverse lookup through BRepGraph::UIDs() -//! - stale tracking via BRepGraph_VersionStamp through BRepGraph::UIDs() +//! - freshness checks via BRepGraph_VersionStamp through BRepGraph::UIDs() //! //! Identity semantics: //! - RefId (kind + index) is graph-local and may change after Compact(). //! Use it for in-graph traversal and short-lived mutation logic. -//! - RefUID (kind + counter + generation) is stable across index remapping -//! and intended for longer-lived identity tracking. +//! - RefUID (kind + counter) is stable across index remapping and intended +//! for longer-lived identity tracking. Graph generation is carried by +//! BRepGraph_VersionStamp when freshness checks are needed. //! //! ## RefsView vs TopoView naming //! RefsView accessors take reference IDs (BRepGraph_ShellRefId, BRepGraph_FaceRefId) @@ -52,7 +54,7 @@ //! const BRepGraphInc::FaceRef& aFR = aRefs.Faces().Entry(aFaceRefId); //! if (aFR.IsRemoved) //! continue; -//! // use aFR.FaceDefId, aFR.Orientation, aFR.Location ... +//! // use aFR.FaceId, aFR.Orientation, aFR.Location ... //! } //! @endcode //! @@ -72,8 +74,8 @@ public: class ShellOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; [[nodiscard]] BRepGraph_ShellRefId StartId() const { return BRepGraph_ShellRefId::Start(); } @@ -81,26 +83,26 @@ public: [[nodiscard]] Standard_EXPORT const BRepGraphInc::ShellRef& Entry( const BRepGraph_ShellRefId theRefId) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& IdsOf( + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& IdsOf( const BRepGraph_SolidId theSolid) const; private: friend class RefsView; - explicit ShellOps(const BRepGraph* theGraph) + explicit ShellOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Face reference queries. class FaceOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; [[nodiscard]] BRepGraph_FaceRefId StartId() const { return BRepGraph_FaceRefId::Start(); } @@ -108,26 +110,26 @@ public: [[nodiscard]] Standard_EXPORT const BRepGraphInc::FaceRef& Entry( const BRepGraph_FaceRefId theRefId) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& IdsOf( + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& IdsOf( const BRepGraph_ShellId theShell) const; private: friend class RefsView; - explicit FaceOps(const BRepGraph* theGraph) + explicit FaceOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Wire reference queries. class WireOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; [[nodiscard]] BRepGraph_WireRefId StartId() const { return BRepGraph_WireRefId::Start(); } @@ -135,53 +137,26 @@ public: [[nodiscard]] Standard_EXPORT const BRepGraphInc::WireRef& Entry( const BRepGraph_WireRefId theRefId) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& IdsOf( + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& IdsOf( const BRepGraph_FaceId theFace) const; private: friend class RefsView; - explicit WireOps(const BRepGraph* theGraph) + explicit WireOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; - }; - - //! @brief Coedge reference queries. - class CoEdgeOps - { - public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; - - [[nodiscard]] BRepGraph_CoEdgeRefId StartId() const { return BRepGraph_CoEdgeRefId::Start(); } - - [[nodiscard]] BRepGraph_CoEdgeRefId EndId() const { return BRepGraph_CoEdgeRefId(Nb()); } - - [[nodiscard]] Standard_EXPORT const BRepGraphInc::CoEdgeRef& Entry( - const BRepGraph_CoEdgeRefId theRefId) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& IdsOf( - const BRepGraph_WireId theWire) const; - - private: - friend class RefsView; - - explicit CoEdgeOps(const BRepGraph* theGraph) - : myGraph(theGraph) - { - } - - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Vertex reference queries. class VertexOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; [[nodiscard]] BRepGraph_VertexRefId StartId() const { return BRepGraph_VertexRefId::Start(); } @@ -193,20 +168,20 @@ public: private: friend class RefsView; - explicit VertexOps(const BRepGraph* theGraph) + explicit VertexOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Solid reference queries. class SolidOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; [[nodiscard]] BRepGraph_SolidRefId StartId() const { return BRepGraph_SolidRefId::Start(); } @@ -214,26 +189,26 @@ public: [[nodiscard]] Standard_EXPORT const BRepGraphInc::SolidRef& Entry( const BRepGraph_SolidRefId theRefId) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& IdsOf( + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& IdsOf( const BRepGraph_CompSolidId theCompSolid) const; private: friend class RefsView; - explicit SolidOps(const BRepGraph* theGraph) + explicit SolidOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Generic child reference queries. class ChildOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; [[nodiscard]] BRepGraph_ChildRefId StartId() const { return BRepGraph_ChildRefId::Start(); } @@ -241,26 +216,28 @@ public: [[nodiscard]] Standard_EXPORT const BRepGraphInc::ChildRef& Entry( const BRepGraph_ChildRefId theRefId) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& IdsOf( + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& IdsOf( const BRepGraph_CompoundId theCompound) const; + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& + IdsReferencing(const BRepGraph_NodeId theChild) const; private: friend class RefsView; - explicit ChildOps(const BRepGraph* theGraph) + explicit ChildOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Occurrence reference queries. class OccurrenceOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; [[nodiscard]] BRepGraph_OccurrenceRefId StartId() const { @@ -274,18 +251,65 @@ public: [[nodiscard]] Standard_EXPORT const BRepGraphInc::OccurrenceRef& Entry( const BRepGraph_OccurrenceRefId theRefId) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& IdsOf( + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& IdsOf( const BRepGraph_ProductId theProduct) const; + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& + IdsReferencing(const BRepGraph_NodeId theChild) const; private: friend class RefsView; - explicit OccurrenceOps(const BRepGraph* theGraph) + explicit OccurrenceOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; + }; + + //! @brief Generic reference id queries. + class GenOps + { + public: + //! Return the number of references of the specified kind (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb(const BRepGraph_RefId::Kind theKind) const; + + //! Return true if the reference id kind and index are within storage bounds. + [[nodiscard]] Standard_EXPORT bool IsValid(const BRepGraph_RefId theRef) const; + + //! Return true if the reference id is valid and not soft-removed. + [[nodiscard]] Standard_EXPORT bool IsActive(const BRepGraph_RefId theRef) const; + + //! Return true if the specified typed RefId is invalid or marked removed. + [[nodiscard]] Standard_EXPORT bool IsRemoved(const BRepGraph_RefId theRef) const; + + //! Return the direct parent-owned RefId stored at the specified child step. + //! This is a structural lookup over the parent's raw ref arrays and does not + //! skip removed refs or refs targeting removed child defs. + [[nodiscard]] Standard_EXPORT BRepGraph_RefId RefAtStep(const BRepGraph_NodeId theParent, + const int theStep) const; + + //! Resolve the child definition node referenced by any typed RefId. + [[nodiscard]] Standard_EXPORT BRepGraph_NodeId ChildNode(const BRepGraph_RefId theRef) const; + + //! Return the local location carried by the specified typed RefId. + //! OccurrenceRef and invalid refs return identity. + [[nodiscard]] Standard_EXPORT TopLoc_Location LocalLocation(const BRepGraph_RefId theRef) const; + + //! Return the orientation carried by the specified typed RefId. + //! OccurrenceRef and invalid refs return TopAbs_FORWARD. + [[nodiscard]] Standard_EXPORT TopAbs_Orientation + Orientation(const BRepGraph_RefId theRef) const; + + private: + friend class RefsView; + + explicit GenOps(BRepGraph* theGraph) + : myGraph(theGraph) + { + } + + BRepGraph* myGraph; }; //! Grouped shell reference queries. @@ -297,9 +321,6 @@ public: //! Grouped wire reference queries. [[nodiscard]] const WireOps& Wires() const { return myWires; } - //! Grouped coedge reference queries. - [[nodiscard]] const CoEdgeOps& CoEdges() const { return myCoEdges; } - //! Grouped vertex reference queries. [[nodiscard]] const VertexOps& Vertices() const { return myVertices; } @@ -312,52 +333,35 @@ public: //! Grouped occurrence reference queries. [[nodiscard]] const OccurrenceOps& Occurrences() const { return myOccurrences; } - //! Return the direct parent-owned RefId stored at the specified child step. - //! This is a structural lookup over the parent's raw ref arrays and does not - //! skip removed refs or refs targeting removed child defs. - [[nodiscard]] Standard_EXPORT BRepGraph_RefId RefAtStep(const BRepGraph_NodeId theParent, - const int theStep) const; - - //! Resolve the child definition node referenced by any typed RefId. - [[nodiscard]] Standard_EXPORT BRepGraph_NodeId ChildNode(const BRepGraph_RefId theRef) const; - - //! Return true if the specified typed RefId is marked removed. - [[nodiscard]] Standard_EXPORT bool IsRemoved(const BRepGraph_RefId theRef) const; - - //! Return the local location carried by the specified typed RefId. - //! OccurrenceRef and invalid refs return identity. - [[nodiscard]] Standard_EXPORT TopLoc_Location LocalLocation(const BRepGraph_RefId theRef) const; - - //! Return the orientation carried by the specified typed RefId. - //! CoEdgeRef, OccurrenceRef, and invalid refs return TopAbs_FORWARD. - [[nodiscard]] Standard_EXPORT TopAbs_Orientation Orientation(const BRepGraph_RefId theRef) const; + //! Grouped generic reference id queries. + [[nodiscard]] const GenOps& Gen() const { return myGen; } private: friend class BRepGraph; friend struct BRepGraph_Data; - explicit RefsView(const BRepGraph* theGraph) + explicit RefsView(BRepGraph* theGraph) : myGraph(theGraph), myShells(theGraph), myFaces(theGraph), myWires(theGraph), - myCoEdges(theGraph), myVertices(theGraph), mySolids(theGraph), myChildren(theGraph), - myOccurrences(theGraph) + myOccurrences(theGraph), + myGen(theGraph) { } - const BRepGraph* myGraph; - ShellOps myShells; - FaceOps myFaces; - WireOps myWires; - CoEdgeOps myCoEdges; - VertexOps myVertices; - SolidOps mySolids; - ChildOps myChildren; - OccurrenceOps myOccurrences; + BRepGraph* myGraph; + ShellOps myShells; + FaceOps myFaces; + WireOps myWires; + VertexOps myVertices; + SolidOps mySolids; + ChildOps myChildren; + OccurrenceOps myOccurrences; + GenOps myGen; }; #endif // _BRepGraph_RefsView_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.cxx new file mode 100644 index 0000000000..4e74c9c708 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.cxx @@ -0,0 +1,254 @@ +// 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 + +//================================================================================================= + +bool BRepGraph_RelatedIterator::setCurrent(const BRepGraph_NodeId theNode, + const RelationKind theRelation) +{ + if (!theNode.IsValid() || myGraph->Topo().Gen().IsRemoved(theNode)) + { + return false; + } + + myCurrent = theNode; + myRelation = theRelation; + myHasCurrent = true; + return true; +} + +//================================================================================================= + +bool BRepGraph_RelatedIterator::advanceFaceBoundaryEdge() +{ + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::FromNodeId(myNode); + for (BRepGraph_DefsWireOfFace aWireIt(*myGraph, aFaceId); aWireIt.More(); aWireIt.Next()) + { + if (aWireIt.Index() < myIndex) + { + continue; + } + + for (BRepGraph_DefsEdgeOfWire anEdgeIt(*myGraph, aWireIt.CurrentId()); anEdgeIt.More(); + anEdgeIt.Next()) + { + if (aWireIt.Index() == myIndex && anEdgeIt.Index() < myInnerIndex) + { + continue; + } + + myIndex = aWireIt.Index(); + myInnerIndex = anEdgeIt.Index() + 1; + return setCurrent(BRepGraph_NodeId(anEdgeIt.CurrentId()), RelationKind::BoundaryEdge); + } + + myIndex = aWireIt.Index() + 1; + myInnerIndex = 0; + } + + return false; +} + +//================================================================================================= + +bool BRepGraph_RelatedIterator::advanceAdjacentFace() +{ + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::FromNodeId(myNode); + for (BRepGraph_DefsWireOfFace aWireIt(*myGraph, aFaceId); aWireIt.More(); aWireIt.Next()) + { + if (aWireIt.Index() < myIndex) + { + continue; + } + + for (BRepGraph_DefsCoEdgeOfWire aCoEdgeIt(*myGraph, aWireIt.CurrentId()); aCoEdgeIt.More(); + aCoEdgeIt.Next()) + { + if (aWireIt.Index() == myIndex && aCoEdgeIt.Index() < myInnerIndex) + { + continue; + } + + for (BRepGraph_FacesOfEdge aFaceIt = + myGraph->Topo().Edges().FacesOf(aCoEdgeIt.Current().ChildEdgeId, myDeepIndex); + aFaceIt.More(); + aFaceIt.Next()) + { + const BRepGraph_FaceId anAdjacentFaceId = aFaceIt.CurrentId(); + if (anAdjacentFaceId == aFaceId) + { + myDeepIndex = aFaceIt.Index() + 1; + continue; + } + + myIndex = aWireIt.Index(); + myInnerIndex = aCoEdgeIt.Index(); + myDeepIndex = aFaceIt.Index() + 1; + return setCurrent(BRepGraph_NodeId(anAdjacentFaceId), RelationKind::AdjacentFace); + } + + myDeepIndex = 0; + } + + myIndex = aWireIt.Index() + 1; + myInnerIndex = 0; + } + + return false; +} + +//================================================================================================= + +void BRepGraph_RelatedIterator::advance() +{ + myHasCurrent = false; + if (!myNode.IsValid() || myGraph->Topo().Gen().IsRemoved(myNode)) + { + return; + } + + for (;;) + { + switch (myNode.NodeKind) + { + // Container/assembly nodes have no topological relations. + // Use BRepGraph_ChildExplorer / BRepGraph_ParentExplorer for navigation. + case BRepGraph_NodeId::Kind::Solid: + case BRepGraph_NodeId::Kind::Shell: + case BRepGraph_NodeId::Kind::Compound: + case BRepGraph_NodeId::Kind::CompSolid: + case BRepGraph_NodeId::Kind::Product: + case BRepGraph_NodeId::Kind::Occurrence: + return; + case BRepGraph_NodeId::Kind::Face: { + if (myStage == Stage::First) + { + if (advanceFaceBoundaryEdge()) + { + return; + } + myStage = Stage::Second; + myIndex = 0; + myInnerIndex = 0; + myDeepIndex = 0; + } + if (myStage == Stage::Second) + { + if (advanceAdjacentFace()) + { + return; + } + myStage = Stage::Third; + myIndex = 0; + } + if (myStage == Stage::Third) + { + myStage = Stage::Finished; + if (setCurrent( + BRepGraph_NodeId( + BRepGraph_Tool::Face::OuterWire(*myGraph, BRepGraph_FaceId::FromNodeId(myNode))), + RelationKind::OuterWire)) + { + return; + } + return; + } + return; + } + case BRepGraph_NodeId::Kind::Edge: { + if (myStage == Stage::First) + { + if (advanceParentIterator( + myGraph->Topo().Edges().FacesOf(BRepGraph_EdgeId::FromNodeId(myNode), myIndex), + RelationKind::ReferencedByFace)) + { + return; + } + myStage = Stage::Second; + myIndex = 0; + } + if (advanceEdgeVertex()) + { + return; + } + return; + } + case BRepGraph_NodeId::Kind::Wire: { + if (myStage == Stage::First) + { + if (advanceRefChildren( + BRepGraph_CoEdgesOfWire(*myGraph, BRepGraph_WireId::FromNodeId(myNode)), + RelationKind::WireCoEdge)) + { + return; + } + myStage = Stage::Second; + myIndex = 0; + } + if (advanceParentIterator( + BRepGraph_FacesOfWire(*myGraph, + myGraph->Topo() + .Wires() + .Relations(BRepGraph_WireId::FromNodeId(myNode)) + .ParentWireRefIds, + myIndex), + RelationKind::OwningFace)) + { + return; + } + return; + } + case BRepGraph_NodeId::Kind::Vertex: { + if (advanceParents(myGraph->Topo().Vertices().Edges(BRepGraph_VertexId::FromNodeId(myNode)), + RelationKind::IncidentEdge)) + { + return; + } + return; + } + case BRepGraph_NodeId::Kind::CoEdge: { + const BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_CoEdgeId::FromNodeId(myNode); + if (myStage == Stage::First) + { + myStage = Stage::Second; + if (setCurrent(BRepGraph_NodeId(BRepGraph_Tool::CoEdge::EdgeOf(*myGraph, aCoEdgeId)), + RelationKind::ParentEdge)) + { + return; + } + } + if (myStage == Stage::Second) + { + myStage = Stage::Third; + if (setCurrent(BRepGraph_NodeId(BRepGraph_Tool::CoEdge::FaceOf(*myGraph, aCoEdgeId)), + RelationKind::OwningFace)) + { + return; + } + } + if (myStage == Stage::Third) + { + myStage = Stage::Finished; + if (setCurrent(BRepGraph_NodeId(BRepGraph_Tool::CoEdge::SeamPair(*myGraph, aCoEdgeId)), + RelationKind::SeamPair)) + { + return; + } + } + return; + } + } + } +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.hxx index d3528fde29..839c46ee88 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RelatedIterator.hxx @@ -22,6 +22,7 @@ #include #include #include +#include //! @brief Single-level iterator over semantically related topology nodes. //! @see BRepGraph class comment "Iterator guide" for choosing between iterator types. @@ -50,14 +51,18 @@ public: SeamPair, //!< CoEdge -> CoEdge (seam twin) }; + //! Internal traversal stage tracking which sub-iteration is active. enum class Stage { - First, - Second, - Third, - Finished, + First, //!< Primary relation iteration. + Second, //!< Secondary relation iteration. + Third, //!< Tertiary relation iteration. + Finished, //!< All relations exhausted. }; + //! Construct an iterator over all semantically related nodes of the given source node. + //! @param[in] theGraph graph containing the node + //! @param[in] theNode source node whose relations are iterated BRepGraph_RelatedIterator(const BRepGraph& theGraph, const BRepGraph_NodeId theNode) : myGraph(&theGraph), myNode(theNode) @@ -65,8 +70,10 @@ public: advance(); } + //! True if another related node is available. [[nodiscard]] bool More() const { return myHasCurrent; } + //! Advance to the next related node. void Next() { if (!myHasCurrent) @@ -76,8 +83,10 @@ public: advance(); } + //! Return the current related node id. [[nodiscard]] const BRepGraph_NodeId& Current() const { return myCurrent; } + //! Return the relation kind explaining why the current node is related. [[nodiscard]] RelationKind CurrentRelation() const { return myRelation; } //! Returns an STL-compatible iterator for range-based for loops. @@ -90,18 +99,7 @@ public: NCollection_ForwardRangeSentinel end() const { return NCollection_ForwardRangeSentinel{}; } private: - [[nodiscard]] bool setCurrent(const BRepGraph_NodeId theNode, const RelationKind theRelation) - { - if (!theNode.IsValid() || myGraph->Topo().Gen().IsRemoved(theNode)) - { - return false; - } - - myCurrent = theNode; - myRelation = theRelation; - myHasCurrent = true; - return true; - } + [[nodiscard]] bool setCurrent(const BRepGraph_NodeId theNode, const RelationKind theRelation); template [[nodiscard]] bool advanceRefChildren(IteratorT theIterator, const RelationKind theRelation) @@ -113,8 +111,16 @@ private: continue; } - myIndex = theIterator.Index() + 1; - const BRepGraph_NodeId aChildNode = myGraph->Refs().ChildNode(theIterator.CurrentId()); + myIndex = theIterator.Index() + 1; + BRepGraph_NodeId aChildNode; + if constexpr (std::is_convertible_v) + { + aChildNode = myGraph->Refs().Gen().ChildNode(theIterator.CurrentId()); + } + else + { + aChildNode = BRepGraph_NodeId(theIterator.CurrentId()); + } return setCurrent(aChildNode, theRelation); } @@ -138,79 +144,9 @@ private: return false; } - [[nodiscard]] bool advanceFaceBoundaryEdge() - { - const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::FromNodeId(myNode); - for (BRepGraph_DefsWireOfFace aWireIt(*myGraph, aFaceId); aWireIt.More(); aWireIt.Next()) - { - if (aWireIt.Index() < myIndex) - { - continue; - } + [[nodiscard]] bool advanceFaceBoundaryEdge(); - for (BRepGraph_DefsEdgeOfWire anEdgeIt(*myGraph, aWireIt.CurrentId()); anEdgeIt.More(); - anEdgeIt.Next()) - { - if (aWireIt.Index() == myIndex && anEdgeIt.Index() < myInnerIndex) - { - continue; - } - - myIndex = aWireIt.Index(); - myInnerIndex = anEdgeIt.Index() + 1; - return setCurrent(BRepGraph_NodeId(anEdgeIt.CurrentId()), RelationKind::BoundaryEdge); - } - - myIndex = aWireIt.Index() + 1; - myInnerIndex = 0; - } - - return false; - } - - [[nodiscard]] bool advanceAdjacentFace() - { - const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::FromNodeId(myNode); - for (BRepGraph_DefsWireOfFace aWireIt(*myGraph, aFaceId); aWireIt.More(); aWireIt.Next()) - { - if (aWireIt.Index() < myIndex) - { - continue; - } - - for (BRepGraph_DefsCoEdgeOfWire aCoEdgeIt(*myGraph, aWireIt.CurrentId()); aCoEdgeIt.More(); - aCoEdgeIt.Next()) - { - if (aWireIt.Index() == myIndex && aCoEdgeIt.Index() < myInnerIndex) - { - continue; - } - - const NCollection_DynamicArray& aFaces = - myGraph->Topo().Edges().Faces(aCoEdgeIt.Current().EdgeDefId); - for (; myDeepIndex < static_cast(aFaces.Size()); ++myDeepIndex) - { - const BRepGraph_FaceId anAdjacentFaceId = aFaces.Value(static_cast(myDeepIndex)); - if (anAdjacentFaceId == aFaceId) - { - continue; - } - - myIndex = aWireIt.Index(); - myInnerIndex = aCoEdgeIt.Index(); - ++myDeepIndex; - return setCurrent(BRepGraph_NodeId(anAdjacentFaceId), RelationKind::AdjacentFace); - } - - myDeepIndex = 0; - } - - myIndex = aWireIt.Index() + 1; - myInnerIndex = 0; - } - - return false; - } + [[nodiscard]] bool advanceAdjacentFace(); [[nodiscard]] bool advanceEdgeVertex() { @@ -219,13 +155,16 @@ private: RelationKind::IncidentVertex); } - //! Advance through a reverse-index iterator (e.g. BRepGraph_FacesOfEdge). + //! Advance through a relation iterator (e.g. BRepGraph_FacesOfEdge). //! Constructs a ParentsOf starting at myIndex for O(1) amortized resumption. template - [[nodiscard]] bool advanceParents(const NCollection_DynamicArray& theParents, + [[nodiscard]] bool advanceParents(const NCollection_LinearVector& theParents, const RelationKind theRelation) { - BRepGraph_ReverseIterator::ParentsOf anIt(*myGraph, theParents, myIndex); + BRepGraph_ReverseIterator::ParentsOf> anIt( + *myGraph, + theParents, + myIndex); if (anIt.More()) { myIndex = anIt.Index() + 1; @@ -235,137 +174,19 @@ private: return false; } - void advance() + template + [[nodiscard]] bool advanceParentIterator(IteratorT theIterator, const RelationKind theRelation) { - myHasCurrent = false; - if (!myNode.IsValid() || myGraph->Topo().Gen().IsRemoved(myNode)) + if (theIterator.More()) { - return; - } - - for (;;) - { - switch (myNode.NodeKind) - { - // Container/assembly nodes have no topological relations. - // Use BRepGraph_ChildExplorer / BRepGraph_ParentExplorer for navigation. - case BRepGraph_NodeId::Kind::Solid: - case BRepGraph_NodeId::Kind::Shell: - case BRepGraph_NodeId::Kind::Compound: - case BRepGraph_NodeId::Kind::CompSolid: - case BRepGraph_NodeId::Kind::Product: - case BRepGraph_NodeId::Kind::Occurrence: - return; - case BRepGraph_NodeId::Kind::Face: { - if (myStage == Stage::First) - { - if (advanceFaceBoundaryEdge()) - { - return; - } - myStage = Stage::Second; - myIndex = 0; - myInnerIndex = 0; - myDeepIndex = 0; - } - if (myStage == Stage::Second) - { - if (advanceAdjacentFace()) - { - return; - } - myStage = Stage::Third; - myIndex = 0; - } - if (myStage == Stage::Third) - { - myStage = Stage::Finished; - return (void)setCurrent(BRepGraph_NodeId(myGraph->Topo().Faces().OuterWire( - BRepGraph_FaceId::FromNodeId(myNode))), - RelationKind::OuterWire); - } - return; - } - case BRepGraph_NodeId::Kind::Edge: { - if (myStage == Stage::First) - { - if (advanceParents(myGraph->Topo().Edges().Faces(BRepGraph_EdgeId::FromNodeId(myNode)), - RelationKind::ReferencedByFace)) - { - return; - } - myStage = Stage::Second; - myIndex = 0; - } - if (advanceEdgeVertex()) - { - return; - } - return; - } - case BRepGraph_NodeId::Kind::Wire: { - if (myStage == Stage::First) - { - if (advanceRefChildren( - BRepGraph_RefsCoEdgeOfWire(*myGraph, BRepGraph_WireId::FromNodeId(myNode)), - RelationKind::WireCoEdge)) - { - return; - } - myStage = Stage::Second; - myIndex = 0; - } - if (advanceParents(myGraph->Topo().Wires().Faces(BRepGraph_WireId::FromNodeId(myNode)), - RelationKind::OwningFace)) - { - return; - } - return; - } - case BRepGraph_NodeId::Kind::Vertex: { - if (advanceParents( - myGraph->Topo().Vertices().Edges(BRepGraph_VertexId::FromNodeId(myNode)), - RelationKind::IncidentEdge)) - { - return; - } - return; - } - case BRepGraph_NodeId::Kind::CoEdge: { - const BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_CoEdgeId::FromNodeId(myNode); - if (myStage == Stage::First) - { - myStage = Stage::Second; - if (setCurrent(BRepGraph_NodeId(BRepGraph_Tool::CoEdge::EdgeOf(*myGraph, aCoEdgeId)), - RelationKind::ParentEdge)) - { - return; - } - } - if (myStage == Stage::Second) - { - myStage = Stage::Third; - if (setCurrent(BRepGraph_NodeId(BRepGraph_Tool::CoEdge::FaceOf(*myGraph, aCoEdgeId)), - RelationKind::OwningFace)) - { - return; - } - } - if (myStage == Stage::Third) - { - myStage = Stage::Finished; - if (setCurrent(BRepGraph_NodeId(BRepGraph_Tool::CoEdge::SeamPair(*myGraph, aCoEdgeId)), - RelationKind::SeamPair)) - { - return; - } - } - return; - } - } + myIndex = theIterator.Index() + 1; + return setCurrent(BRepGraph_NodeId(theIterator.CurrentId()), theRelation); } + return false; } + Standard_EXPORT void advance(); + private: const BRepGraph* myGraph; BRepGraph_NodeId myNode; @@ -378,4 +199,4 @@ private: bool myHasCurrent = false; }; -#endif // _BRepGraph_RelatedIterator_HeaderFile \ No newline at end of file +#endif // _BRepGraph_RelatedIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RepId.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RepId.hxx deleted file mode 100644 index 6a3570a610..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_RepId.hxx +++ /dev/null @@ -1,369 +0,0 @@ -// 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. - -#ifndef _BRepGraph_RepId_HeaderFile -#define _BRepGraph_RepId_HeaderFile - -#include -#include - -#include -#include -#include -#include -#include - -//! Lightweight typed index into a per-kind representation vector inside BRepGraph. -//! -//! The pair (Kind, Index) forms a unique representation identifier within one -//! graph instance. Default-constructed RepId has Index = UINT32_MAX (invalid). -//! -//! Representations are NOT topology nodes - they hold geometry or mesh data -//! referenced by topology entities. They do not participate in BFS traversal, -//! reverse index, or parent-child relationships. -//! -//! RepId is a value type: cheap to copy, compare, hash. -struct BRepGraph_RepId -{ - //! Categories of representation data. - enum class Kind : int - { - // Geometry (exact mathematical definition) - Surface = 0, //!< Geom_Surface for faces - Curve3D = 1, //!< Geom_Curve for edges - Curve2D = 2, //!< Geom2d_Curve for coedges (PCurve geometry) - - // Mesh (discrete approximation) - Triangulation = 3, //!< Poly_Triangulation for faces - Polygon3D = 4, //!< Poly_Polygon3D for edges - Polygon2D = 5, //!< Poly_Polygon2D for coedges (polygon-on-surface) - PolygonOnTri = 6, //!< Poly_PolygonOnTriangulation for coedges - - // Reserved 7-19 for future built-in types - // Custom plugin types start at 100+ - }; - - //! True if the kind is a geometry kind (Surface, Curve3D, Curve2D). - static bool IsGeometryKind(const Kind theKind) - { - return theKind == Kind::Surface || theKind == Kind::Curve3D || theKind == Kind::Curve2D; - } - - //! True if the kind is a mesh kind (Triangulation, Polygon3D, Polygon2D, PolygonOnTri). - static bool IsMeshKind(const Kind theKind) - { - return theKind == Kind::Triangulation || theKind == Kind::Polygon3D - || theKind == Kind::Polygon2D || theKind == Kind::PolygonOnTri; - } - - //! @brief Compile-time typed wrapper around BRepGraph_RepId. - //! - //! Provides compile-time kind safety: a Typed - //! cannot be accidentally used where a Typed is expected. - //! Implicitly converts to BRepGraph_RepId for backward compatibility. - //! - //! @tparam TheKind the BRepGraph_RepId::Kind this typed id represents - template - struct Typed - { - static constexpr uint32_t THE_START_INDEX = 0u; - static constexpr uint32_t THE_INVALID_INDEX = std::numeric_limits::max(); - - uint32_t Index; - - //! Default: invalid (Index = UINT32_MAX). - Typed() - : Index(THE_INVALID_INDEX) - { - } - - //! Construct from index. - explicit Typed(const uint32_t theIdx) - : Index(theIdx) - { - } - - //! Construct from an untyped representation id of the same kind. - explicit Typed(const BRepGraph_RepId theId) - : Typed(FromRepId(theId)) - { - } - - template = 0> - Typed(const Typed&) = delete; - - //! First valid id in a dense per-kind sequence. - [[nodiscard]] static Typed Start() { return Typed(THE_START_INDEX); } - - //! Invalid sentinel id. - [[nodiscard]] static Typed Invalid() { return Typed(); } - - //! True if this id points to an allocated representation slot. - [[nodiscard]] bool IsValid() const { return Index != THE_INVALID_INDEX; } - - //! True if this id points to an allocated slot within [0, theMaxCount). - //! UINT32_MAX (invalid sentinel) always fails this check for any realistic count. - [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const { return Index < theMaxCount; } - - //! True if this id is within the dense range exposed by a provider with Nb(). - template - [[nodiscard]] auto IsValidIn(const CountProviderT& theProvider) const - -> decltype(theProvider.Nb(), bool()) - { - return IsValid(theProvider.Nb()); - } - - //! True if this id is within the dense range exposed by a provider with Length(). - template - [[nodiscard]] auto IsValidIn(const CountProviderT& theProvider) const - -> decltype(theProvider.Size(), bool()) - { - return IsValid(static_cast(theProvider.Size())); - } - - //! Implicit conversion to untyped RepId. - operator BRepGraph_RepId() const { return BRepGraph_RepId(TheKind, Index); } - - //! Explicit conversion from untyped RepId. - //! Asserts that the Kind matches in debug builds. - //! @param[in] theId untyped RepId to convert - static Typed FromRepId(const BRepGraph_RepId theId) - { - Standard_ASSERT_VOID(theId.RepKind == TheKind, "RepId kind mismatch"); - return Typed(theId.Index); - } - - bool operator==(const Typed& theOther) const { return Index == theOther.Index; } - - bool operator!=(const Typed& theOther) const { return Index != theOther.Index; } - - bool operator<(const Typed& theOther) const { return Index < theOther.Index; } - - bool operator<=(const Typed& theOther) const { return Index <= theOther.Index; } - - bool operator>(const Typed& theOther) const { return Index > theOther.Index; } - - bool operator>=(const Typed& theOther) const { return Index >= theOther.Index; } - - //! Pre-increment (++id). - Typed& operator++() - { - Standard_ASSERT_VOID(Index != THE_INVALID_INDEX, "pre-increment on invalid id"); - ++Index; - return *this; - } - - //! Post-increment (id++). - Typed operator++(int) - { - Standard_ASSERT_VOID(Index != THE_INVALID_INDEX, "post-increment on invalid id"); - Typed aPrev = *this; - ++Index; - return aPrev; - } - - //! Advance by offset. - [[nodiscard]] Typed operator+(const uint32_t theOffset) const - { - return Typed(Index + theOffset); - } - - //! Retreat by offset. - [[nodiscard]] Typed operator-(const uint32_t theOffset) const - { - Standard_ASSERT_VOID(Index != THE_INVALID_INDEX && Index >= theOffset, - "retreat underflows index"); - return Typed(Index - theOffset); - } - - //! Comparison with untyped RepId (checks both Kind and Index). - bool operator==(const BRepGraph_RepId& theOther) const - { - return theOther.RepKind == TheKind && theOther.Index == Index; - } - - bool operator!=(const BRepGraph_RepId& theOther) const { return !(*this == theOther); } - - //! Allow reversed comparison: RepId == Typed. - friend bool operator==(const BRepGraph_RepId& theLhs, const Typed& theRhs) - { - return theRhs == theLhs; - } - - friend bool operator!=(const BRepGraph_RepId& theLhs, const Typed& theRhs) - { - return theRhs != theLhs; - } - }; - - static constexpr uint32_t THE_START_INDEX = 0u; - static constexpr uint32_t THE_INVALID_INDEX = std::numeric_limits::max(); - - Kind RepKind; - uint32_t Index; - - //! Default: invalid RepId (Index = UINT32_MAX). - //! RepKind is set to Kind::Surface but is meaningless when !IsValid(). - BRepGraph_RepId() - : RepKind(Kind::Surface), - Index(THE_INVALID_INDEX) - { - } - - BRepGraph_RepId(const Kind theKind, const uint32_t theIdx) - : RepKind(theKind), - Index(theIdx) - { - } - - //! First valid id in a dense sequence for the specified kind. - [[nodiscard]] static BRepGraph_RepId Start(const Kind theKind) - { - return BRepGraph_RepId(theKind, THE_START_INDEX); - } - - //! Invalid sentinel id for the specified kind. - [[nodiscard]] static BRepGraph_RepId Invalid(const Kind theKind = Kind::Surface) - { - return BRepGraph_RepId(theKind, THE_INVALID_INDEX); - } - - //! True if this id points to an allocated representation slot. - [[nodiscard]] bool IsValid() const { return Index != THE_INVALID_INDEX; } - - //! True if this id points to an allocated slot within [0, theMaxCount). - //! UINT32_MAX (invalid sentinel) always fails this check for any realistic count. - [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const { return Index < theMaxCount; } - - //! True if this id is within the dense range exposed by a provider with Nb(). - template - [[nodiscard]] auto IsValidIn(const CountProviderT& theProvider) const - -> decltype(theProvider.Nb(), bool()) - { - return IsValid(theProvider.Nb()); - } - - //! True if this id is within the dense range exposed by a provider with Size(). - template - [[nodiscard]] auto IsValidIn(const CountProviderT& theProvider) const - -> decltype(theProvider.Size(), bool()) - { - return IsValid(static_cast(theProvider.Size())); - } - - bool operator==(const BRepGraph_RepId& theOther) const - { - return RepKind == theOther.RepKind && Index == theOther.Index; - } - - bool operator!=(const BRepGraph_RepId& theOther) const { return !(*this == theOther); } - - bool operator<(const BRepGraph_RepId& theOther) const - { - if (RepKind != theOther.RepKind) - return static_cast(RepKind) < static_cast(theOther.RepKind); - return Index < theOther.Index; - } - - //! Pre-increment (++id). - BRepGraph_RepId& operator++() - { - Standard_ASSERT_VOID(Index != THE_INVALID_INDEX, "pre-increment on invalid id"); - ++Index; - return *this; - } - - //! Post-increment (id++). - BRepGraph_RepId operator++(int) - { - Standard_ASSERT_VOID(Index != THE_INVALID_INDEX, "post-increment on invalid id"); - BRepGraph_RepId aPrev = *this; - ++Index; - return aPrev; - } - - //! Advance by offset. - [[nodiscard]] BRepGraph_RepId operator+(const uint32_t theOffset) const - { - return BRepGraph_RepId(RepKind, Index + theOffset); - } - - //! Retreat by offset. - [[nodiscard]] BRepGraph_RepId operator-(const uint32_t theOffset) const - { - Standard_ASSERT_VOID(Index != THE_INVALID_INDEX && Index >= theOffset, - "retreat underflows index"); - return BRepGraph_RepId(RepKind, Index - theOffset); - } - - //! Dispatch a generic rep id to a callable taking the matching typed rep id. - template - static auto Visit(const BRepGraph_RepId theRepId, FuncT&& theFunc) - -> decltype(std::forward(theFunc)(Typed())) - { - switch (theRepId.RepKind) - { - case Kind::Surface: - return std::forward(theFunc)(Typed::FromRepId(theRepId)); - case Kind::Curve3D: - return std::forward(theFunc)(Typed::FromRepId(theRepId)); - case Kind::Curve2D: - return std::forward(theFunc)(Typed::FromRepId(theRepId)); - case Kind::Triangulation: - return std::forward(theFunc)(Typed::FromRepId(theRepId)); - case Kind::Polygon3D: - return std::forward(theFunc)(Typed::FromRepId(theRepId)); - case Kind::Polygon2D: - return std::forward(theFunc)(Typed::FromRepId(theRepId)); - case Kind::PolygonOnTri: - return std::forward(theFunc)(Typed::FromRepId(theRepId)); - } - - Standard_ASSERT_VOID(false, "BRepGraph_RepId::Visit: unhandled Kind"); - return std::forward(theFunc)(Typed()); - } -}; - -// Convenience type aliases for typed RepIds. -using BRepGraph_SurfaceRepId = BRepGraph_RepId::Typed; -using BRepGraph_Curve3DRepId = BRepGraph_RepId::Typed; -using BRepGraph_Curve2DRepId = BRepGraph_RepId::Typed; -using BRepGraph_TriangulationRepId = BRepGraph_RepId::Typed; -using BRepGraph_Polygon3DRepId = BRepGraph_RepId::Typed; -using BRepGraph_Polygon2DRepId = BRepGraph_RepId::Typed; -using BRepGraph_PolygonOnTriRepId = BRepGraph_RepId::Typed; - -//! std::hash specialization for NCollection_DefaultHasher support. -template <> -struct std::hash -{ - size_t operator()(const BRepGraph_RepId& theId) const noexcept - { - size_t aCombination[2]; - aCombination[0] = opencascade::hash(static_cast(theId.RepKind)); - aCombination[1] = opencascade::hash(theId.Index); - return opencascade::hashBytes(aCombination, sizeof(aCombination)); - } -}; - -//! std::hash specialization for BRepGraph_RepId::Typed. -template -struct std::hash> -{ - size_t operator()(const BRepGraph_RepId::Typed& theId) const noexcept - { - return std::hash{}(static_cast(theId)); - } -}; - -#endif // _BRepGraph_RepId_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ReverseIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ReverseIterator.hxx index 6374107bd4..4906feebbc 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ReverseIterator.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ReverseIterator.hxx @@ -18,28 +18,27 @@ #include #include #include - #include +#include +#include -//! @brief Single-level typed iterators over parent definitions via reverse index. +//! @brief Single-level typed iterators over parent definitions via relation lists. //! -//! These iterators wrap the NCollection_DynamicArray returned by TopoView -//! reverse-index accessors (e.g. Edges().Faces(), Wires().Faces(), Vertices().Edges()). +//! These iterators wrap parent relation containers returned by TopoView accessors, +//! or derive parent definitions from const relation storage such as EdgeRelations::CoEdgeIds. //! They provide a typed, skip-removed iteration pattern consistent with the //! forward iterators in BRepGraph_DefsIterator and BRepGraph_RefsIterator. //! //! Usage: //! @code //! // Traditional iteration: -//! for (BRepGraph_FacesOfEdge anIt(aGraph, aGraph.Topo().Edges().Faces(anEdgeId)); -//! anIt.More(); anIt.Next()) +//! for (BRepGraph_FacesOfEdge anIt(aGraph, anEdgeId); anIt.More(); anIt.Next()) //! { //! const BRepGraph_FaceId aFaceId = anIt.CurrentId(); //! } //! //! // Range-based for: -//! for (const BRepGraph_FaceId aFaceId : -//! BRepGraph_FacesOfEdge(aGraph, aGraph.Topo().Edges().Faces(anEdgeId))) +//! for (const BRepGraph_FaceId aFaceId : BRepGraph_FacesOfEdge(aGraph, anEdgeId)) //! { //! // ... //! } @@ -172,29 +171,29 @@ struct DefTraits } }; -//! Typed iterator over a reverse-index vector of parent IDs. +//! Typed iterator over a relation vector of parent IDs. //! Skips removed parent definitions automatically in sequential iteration. //! Also provides indexed access (Length/Value) for callers that need //! random access into the underlying vector (e.g. BRepGraph_ParentExplorer). //! @tparam TypedIdT Typed ID such as BRepGraph_FaceId, BRepGraph_EdgeId, etc. -template +template > class ParentsOf { public: - ParentsOf(const BRepGraph& theGraph, const NCollection_DynamicArray& theParents) + ParentsOf(const BRepGraph& theGraph, const ContainerT& theParents) : myGraph(&theGraph), - myParents(&theParents) + myParents(&theParents), + myNbParents(theGraph.Topo().Gen().Nb(BRepGraph_NodeId(TypedIdT()).NodeKind)) { skipRemoved(); } //! Construct starting at a given vector index (for resumable iteration). //! Skips to the first non-removed entry at or after theStartIndex. - ParentsOf(const BRepGraph& theGraph, - const NCollection_DynamicArray& theParents, - const uint32_t theStartIndex) + ParentsOf(const BRepGraph& theGraph, const ContainerT& theParents, const uint32_t theStartIndex) : myGraph(&theGraph), myParents(&theParents), + myNbParents(theGraph.Topo().Gen().Nb(BRepGraph_NodeId(TypedIdT()).NodeKind)), myIndex(theStartIndex) { skipRemoved(); @@ -224,12 +223,9 @@ public: [[nodiscard]] uint32_t Index() const { return myIndex; } - //! Returns the total number of parent entries (including removed). - [[nodiscard]] int Length() const { return myParents->Length(); } - [[nodiscard]] size_t Size() const { return myParents->Size(); } - //! Returns the parent ID at the given index (does NOT check IsRemoved). + //! Returns the parent ID at the given bucket index (does NOT check removal status). [[nodiscard]] TypedIdT Value(const size_t theIndex) const { return myParents->Value(theIndex); } //! Returns an STL-compatible iterator for range-based for loops. @@ -244,11 +240,10 @@ public: private: void skipRemoved() { - while (myIndex < static_cast(myParents->Length())) + while (myIndex < static_cast(myParents->Size())) { - const BRepGraphInc::BaseDef* aDef = - myGraph->Topo().Gen().TopoEntity(myParents->Value(static_cast(myIndex))); - if (aDef != nullptr && !aDef->IsRemoved) + const TypedIdT aParentId = myParents->Value(static_cast(myIndex)); + if (aParentId.IsValid(myNbParents) && !aParentId.IsRemoved(*myGraph)) { return; } @@ -256,12 +251,139 @@ private: } } - const BRepGraph* myGraph = nullptr; - const NCollection_DynamicArray* myParents = nullptr; - uint32_t myIndex = 0; + const BRepGraph* myGraph = nullptr; + const ContainerT* myParents = nullptr; + uint32_t myNbParents = 0; + uint32_t myIndex = 0; }; -//! Result pair returned by RefsParentsOf: parent definition ID + the RefId +template +class EdgeParentsOf +{ +public: + using ParentId = typename TraitsT::ParentId; + + EdgeParentsOf(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) + : myGraph(&theGraph), + myEdge(theEdge) + { + init(); + advance(); + } + + //! Construct starting at a given coedge relation index (for resumable iteration). + EdgeParentsOf(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const uint32_t theStartIndex) + : myGraph(&theGraph), + myEdge(theEdge), + myIndex(theStartIndex) + { + init(); + advance(); + } + + [[nodiscard]] bool More() const { return myHasCurrent; } + + void Next() + { + ++myIndex; + advance(); + } + + [[nodiscard]] ParentId CurrentId() const { return myCurrent; } + + [[nodiscard]] ParentId Current() const { return CurrentId(); } + + [[nodiscard]] const typename DefTraits::DefType& Definition() const + { + return DefTraits::Get(*myGraph, CurrentId()); + } + + [[nodiscard]] uint32_t Index() const { return myIndex; } + + NCollection_ForwardRangeIterator begin() + { + return NCollection_ForwardRangeIterator(this); + } + + NCollection_ForwardRangeSentinel end() const { return NCollection_ForwardRangeSentinel{}; } + +private: + void init() + { + if (!myEdge.IsValid(myGraph->Topo().Edges().Nb()) || myEdge.IsRemoved(*myGraph)) + { + return; + } + myNbParents = TraitsT::NbParents(*myGraph); + myCoEdges = &myGraph->Topo().Edges().CoEdges(myEdge); + } + + [[nodiscard]] ParentId parentAt(const uint32_t theIndex) const + { + if (myCoEdges == nullptr || theIndex >= static_cast(myCoEdges->Size())) + { + return ParentId(); + } + const BRepGraph_CoEdgeId aCoEdgeId = myCoEdges->Value(static_cast(theIndex)); + if (!aCoEdgeId.IsValid(myGraph->Topo().CoEdges().Nb()) || aCoEdgeId.IsRemoved(*myGraph)) + { + return ParentId(); + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph->Topo().CoEdges().Definition(aCoEdgeId); + const ParentId aParent = TraitsT::ParentIdOf(aCoEdge); + if (!aParent.IsValid(myNbParents) || aParent.IsRemoved(*myGraph)) + { + return ParentId(); + } + return aParent; + } + + [[nodiscard]] bool isFirstOccurrence(const ParentId theParent, const uint32_t theIndex) const + { + for (uint32_t anIndex = 0; anIndex < theIndex; ++anIndex) + { + if (parentAt(anIndex) == theParent) + { + return false; + } + } + return true; + } + + void advance() + { + myHasCurrent = false; + if (myCoEdges == nullptr) + { + return; + } + + while (myIndex < static_cast(myCoEdges->Size())) + { + const ParentId aParent = parentAt(myIndex); + if (aParent.IsValid() && isFirstOccurrence(aParent, myIndex)) + { + myCurrent = aParent; + myHasCurrent = true; + return; + } + ++myIndex; + } + } + + const BRepGraph* myGraph = nullptr; + BRepGraph_EdgeId myEdge; + const NCollection_LinearVector* myCoEdges = nullptr; + ParentId myCurrent; + uint32_t myNbParents = 0; + uint32_t myIndex = 0; + bool myHasCurrent = false; +}; + +//! Result pair returned by parent-ref iterators: parent definition ID + the RefId //! in that parent which references the child. template struct ParentRef @@ -270,26 +392,38 @@ struct ParentRef RefIdT Ref; }; -//! Typed iterator over parent definitions via reverse index that also resolves -//! the specific RefId linking each parent to the child. -//! Requires a traits class to find the matching ref within each parent. +//! Typed iterator over parent ID relation lists that also resolves the specific +//! RefId linking each parent to the child by lookup in the parent definition. +//! Used only where the reverse relation stores parent IDs but no ref IDs. //! @tparam TraitsT Traits with: ParentId, ChildId, RefId types, //! FindRef(graph, parentId, childId) -> RefId (invalid if not found) template -class RefsParentsOf +class LookupParentRefsOf { public: - using ParentIdType = typename TraitsT::ParentId; - using ChildIdType = typename TraitsT::ChildId; - using RefIdType = typename TraitsT::RefId; - using ResultType = ParentRef; + using ParentIdType = typename TraitsT::ParentId; + using ChildIdType = typename TraitsT::ChildId; + using RefIdType = typename TraitsT::RefId; + using ResultType = ParentRef; + using ContainerType = typename TraitsT::ContainerType; - RefsParentsOf(const BRepGraph& theGraph, - const NCollection_DynamicArray& theParents, - const ChildIdType theChild) + LookupParentRefsOf(const BRepGraph& theGraph, + const ContainerType& theParents, + const ChildIdType theChild) : myGraph(&theGraph), myParents(&theParents), - myChild(theChild) + myChild(theChild), + myNbParents(theGraph.Topo().Gen().Nb(BRepGraph_NodeId(ParentIdType()).NodeKind)), + myNbRefs([&]() { + if constexpr (std::is_convertible_v) + { + return theGraph.Topo().Gen().Nb(BRepGraph_NodeId(RefIdType()).NodeKind); + } + else + { + return theGraph.Refs().Gen().Nb(BRepGraph_RefId(RefIdType()).RefKind); + } + }()) { advance(); } @@ -311,9 +445,9 @@ public: [[nodiscard]] uint32_t Index() const { return myIndex; } //! Returns an STL-compatible iterator for range-based for loops. - NCollection_ForwardRangeIterator begin() + NCollection_ForwardRangeIterator begin() { - return NCollection_ForwardRangeIterator(this); + return NCollection_ForwardRangeIterator(this); } //! Returns a sentinel marking the end of iteration. @@ -325,13 +459,11 @@ private: myHasCurrent = false; while (myIndex < static_cast(myParents->Size())) { - const ParentIdType aParentId = myParents->Value(static_cast(myIndex)); - const BRepGraphInc::BaseDef* aDef = - myGraph->Topo().Gen().TopoEntity(BRepGraph_NodeId(aParentId)); - if (aDef != nullptr && !aDef->IsRemoved) + const ParentIdType aParentId = myParents->Value(static_cast(myIndex)); + if (aParentId.IsValid(myNbParents) && !aParentId.IsRemoved(*myGraph)) { const RefIdType aRefId = TraitsT::FindRef(*myGraph, aParentId, myChild); - if (aRefId.IsValid()) + if (aRefId.IsValid(myNbRefs) && !aRefId.IsRemoved(*myGraph)) { myCurrent = ResultType{aParentId, aRefId}; myHasCurrent = true; @@ -342,93 +474,119 @@ private: } } - const BRepGraph* myGraph = nullptr; - const NCollection_DynamicArray* myParents = nullptr; - ChildIdType myChild; - ResultType myCurrent; - uint32_t myIndex = 0; - bool myHasCurrent = false; + const BRepGraph* myGraph = nullptr; + const ContainerType* myParents = nullptr; + ChildIdType myChild; + ResultType myCurrent; + uint32_t myNbParents = 0; + uint32_t myNbRefs = 0; + uint32_t myIndex = 0; + bool myHasCurrent = false; }; -// Traits for RefsParentsOf - each knows how to find the RefId -// linking a parent to a specific child definition. - -struct FaceOfWireRefTraits +template +class IdsOfRefs { - using ParentId = BRepGraph_FaceId; - using ChildId = BRepGraph_WireId; - using RefId = BRepGraph_WireRefId; +public: + using IdType = typename TraitsT::IdType; + using RefIdType = typename TraitsT::RefIdType; + using ContainerType = typename TraitsT::ContainerType; - static RefId FindRef(const BRepGraph& theGraph, - const BRepGraph_FaceId theParent, - const BRepGraph_WireId theChild) + IdsOfRefs(const BRepGraph& theGraph, const ContainerType& theRefs) + : myGraph(&theGraph), + myRefs(&theRefs), + myNbRefs(theGraph.Refs().Gen().Nb(BRepGraph_RefId(RefIdType()).RefKind)), + myNbIds(theGraph.Topo().Gen().Nb(BRepGraph_NodeId(IdType()).NodeKind)) { - for (BRepGraph_RefsWireOfFace aIt(theGraph, theParent); aIt.More(); aIt.Next()) - { - if (theGraph.Refs().ChildNode(aIt.CurrentId()) == BRepGraph_NodeId(theChild)) - { - return aIt.CurrentId(); - } - } - return RefId(); + advance(); } -}; -struct ShellOfFaceRefTraits -{ - using ParentId = BRepGraph_ShellId; - using ChildId = BRepGraph_FaceId; - using RefId = BRepGraph_FaceRefId; - - static RefId FindRef(const BRepGraph& theGraph, - const BRepGraph_ShellId theParent, - const BRepGraph_FaceId theChild) + IdsOfRefs(const BRepGraph& theGraph, const ContainerType& theRefs, const uint32_t theStartIndex) + : myGraph(&theGraph), + myRefs(&theRefs), + myNbRefs(theGraph.Refs().Gen().Nb(BRepGraph_RefId(RefIdType()).RefKind)), + myNbIds(theGraph.Topo().Gen().Nb(BRepGraph_NodeId(IdType()).NodeKind)), + myIndex(theStartIndex) { - for (BRepGraph_RefsFaceOfShell aIt(theGraph, theParent); aIt.More(); aIt.Next()) - { - if (theGraph.Refs().ChildNode(aIt.CurrentId()) == BRepGraph_NodeId(theChild)) - { - return aIt.CurrentId(); - } - } - return RefId(); + advance(); } -}; -struct SolidOfShellRefTraits -{ - using ParentId = BRepGraph_SolidId; - using ChildId = BRepGraph_ShellId; - using RefId = BRepGraph_ShellRefId; + [[nodiscard]] bool More() const { return myHasCurrent; } - static RefId FindRef(const BRepGraph& theGraph, - const BRepGraph_SolidId theParent, - const BRepGraph_ShellId theChild) + void Next() { - for (BRepGraph_RefsShellOfSolid aIt(theGraph, theParent); aIt.More(); aIt.Next()) - { - if (theGraph.Refs().ChildNode(aIt.CurrentId()) == BRepGraph_NodeId(theChild)) - { - return aIt.CurrentId(); - } - } - return RefId(); + ++myIndex; + advance(); } + + [[nodiscard]] IdType CurrentId() const { return myCurrent; } + + [[nodiscard]] IdType CurrentParentId() const { return CurrentId(); } + + [[nodiscard]] RefIdType CurrentRefId() const { return myCurrentRef; } + + [[nodiscard]] IdType Current() const { return CurrentId(); } + + [[nodiscard]] const typename DefTraits::DefType& Definition() const + { + return DefTraits::Get(*myGraph, CurrentId()); + } + + [[nodiscard]] uint32_t Index() const { return myIndex; } + + NCollection_ForwardRangeIterator begin() + { + return NCollection_ForwardRangeIterator(this); + } + + NCollection_ForwardRangeSentinel end() const { return NCollection_ForwardRangeSentinel{}; } + +private: + void advance() + { + myHasCurrent = false; + while (myIndex < static_cast(myRefs->Size())) + { + const RefIdType aRefId = myRefs->Value(static_cast(myIndex)); + if (aRefId.IsValid(myNbRefs) && !aRefId.IsRemoved(*myGraph)) + { + const IdType anId = TraitsT::Id(*myGraph, aRefId); + if (anId.IsValid(myNbIds) && !anId.IsRemoved(*myGraph)) + { + myCurrent = anId; + myCurrentRef = aRefId; + myHasCurrent = true; + return; + } + } + ++myIndex; + } + } + + const BRepGraph* myGraph = nullptr; + const ContainerType* myRefs = nullptr; + IdType myCurrent; + RefIdType myCurrentRef; + uint32_t myNbRefs = 0; + uint32_t myNbIds = 0; + uint32_t myIndex = 0; + bool myHasCurrent = false; }; -struct WireOfCoEdgeRefTraits +struct WireOfCoEdgeUsageTraits { - using ParentId = BRepGraph_WireId; - using ChildId = BRepGraph_CoEdgeId; - using RefId = BRepGraph_CoEdgeRefId; + using ParentId = BRepGraph_WireId; + using ChildId = BRepGraph_CoEdgeId; + using RefId = BRepGraph_CoEdgeId; + using ContainerType = NCollection_LinearVector; static RefId FindRef(const BRepGraph& theGraph, const BRepGraph_WireId theParent, const BRepGraph_CoEdgeId theChild) { - for (BRepGraph_RefsCoEdgeOfWire aIt(theGraph, theParent); aIt.More(); aIt.Next()) + for (BRepGraph_CoEdgesOfWire aIt(theGraph, theParent); aIt.More(); aIt.Next()) { - if (theGraph.Refs().ChildNode(aIt.CurrentId()) == BRepGraph_NodeId(theChild)) + if (aIt.CurrentId() == theChild) { return aIt.CurrentId(); } @@ -437,11 +595,33 @@ struct WireOfCoEdgeRefTraits } }; +struct WireFromEdgeCoEdgeTraits +{ + using ParentId = BRepGraph_WireId; + + static ParentId ParentIdOf(const BRepGraphInc::CoEdgeDef& theCoEdge) + { + return theCoEdge.ParentWireId; + } + + static uint32_t NbParents(const BRepGraph& theGraph) { return theGraph.Topo().Wires().Nb(); } +}; + +struct FaceFromEdgeCoEdgeTraits +{ + using ParentId = BRepGraph_FaceId; + + static ParentId ParentIdOf(const BRepGraphInc::CoEdgeDef& theCoEdge) { return theCoEdge.FaceId; } + + static uint32_t NbParents(const BRepGraph& theGraph) { return theGraph.Topo().Faces().Nb(); } +}; + struct EdgeOfVertexRefTraits { - using ParentId = BRepGraph_EdgeId; - using ChildId = BRepGraph_VertexId; - using RefId = BRepGraph_VertexRefId; + using ParentId = BRepGraph_EdgeId; + using ChildId = BRepGraph_VertexId; + using RefId = BRepGraph_VertexRefId; + using ContainerType = NCollection_LinearVector; static RefId FindRef(const BRepGraph& theGraph, const BRepGraph_EdgeId theParent, @@ -449,7 +629,7 @@ struct EdgeOfVertexRefTraits { for (BRepGraph_RefsVertexOfEdge aIt(theGraph, theParent); aIt.More(); aIt.Next()) { - if (theGraph.Refs().ChildNode(aIt.CurrentId()) == BRepGraph_NodeId(theChild)) + if (theGraph.Refs().Gen().ChildNode(aIt.CurrentId()) == BRepGraph_NodeId(theChild)) { return aIt.CurrentId(); } @@ -458,128 +638,198 @@ struct EdgeOfVertexRefTraits } }; -struct CompSolidOfSolidRefTraits +struct FaceFromWireRefTraits { - using ParentId = BRepGraph_CompSolidId; - using ChildId = BRepGraph_SolidId; - using RefId = BRepGraph_SolidRefId; + using IdType = BRepGraph_FaceId; + using RefIdType = BRepGraph_WireRefId; + using ContainerType = NCollection_LinearVector; - static RefId FindRef(const BRepGraph& theGraph, - const BRepGraph_CompSolidId theParent, - const BRepGraph_SolidId theChild) + static IdType Id(const BRepGraph& theGraph, const RefIdType theRefId) { - for (BRepGraph_RefsSolidOfCompSolid aIt(theGraph, theParent); aIt.More(); aIt.Next()) - { - if (theGraph.Refs().ChildNode(aIt.CurrentId()) == BRepGraph_NodeId(theChild)) - { - return aIt.CurrentId(); - } - } - return RefId(); + return theGraph.Refs().Wires().Entry(theRefId).ParentFaceId; } }; -struct CompoundOfChildRefTraits +struct ShellFromFaceRefTraits { - using ParentId = BRepGraph_CompoundId; - using ChildId = BRepGraph_NodeId; - using RefId = BRepGraph_ChildRefId; + using IdType = BRepGraph_ShellId; + using RefIdType = BRepGraph_FaceRefId; + using ContainerType = NCollection_LinearVector; - static RefId FindRef(const BRepGraph& theGraph, - const BRepGraph_CompoundId theParent, - const BRepGraph_NodeId theChild) + static IdType Id(const BRepGraph& theGraph, const RefIdType theRefId) { - for (BRepGraph_RefsChildOfCompound aIt(theGraph, theParent); aIt.More(); aIt.Next()) - { - if (theGraph.Refs().ChildNode(aIt.CurrentId()) == theChild) - { - return aIt.CurrentId(); - } - } - return RefId(); + return theGraph.Refs().Faces().Entry(theRefId).ParentShellId; } }; -struct ProductOfOccurrenceRefTraits +struct SolidFromShellRefTraits { - using ParentId = BRepGraph_ProductId; - using ChildId = BRepGraph_OccurrenceId; - using RefId = BRepGraph_OccurrenceRefId; + using IdType = BRepGraph_SolidId; + using RefIdType = BRepGraph_ShellRefId; + using ContainerType = NCollection_LinearVector; - static RefId FindRef(const BRepGraph& theGraph, - const BRepGraph_ProductId theParent, - const BRepGraph_OccurrenceId theChild) + static IdType Id(const BRepGraph& theGraph, const RefIdType theRefId) { - for (BRepGraph_RefsOccurrenceOfProduct aIt(theGraph, theParent); aIt.More(); aIt.Next()) - { - if (theGraph.Refs().ChildNode(aIt.CurrentId()) == BRepGraph_NodeId(theChild)) - { - return aIt.CurrentId(); - } - } - return RefId(); + return theGraph.Refs().Shells().Entry(theRefId).ParentSolidId; + } +}; + +struct CompSolidFromSolidRefTraits +{ + using IdType = BRepGraph_CompSolidId; + using RefIdType = BRepGraph_SolidRefId; + using ContainerType = NCollection_LinearVector; + + static IdType Id(const BRepGraph& theGraph, const RefIdType theRefId) + { + return theGraph.Refs().Solids().Entry(theRefId).ParentCompSolidId; + } +}; + +struct CompoundFromChildRefTraits +{ + using IdType = BRepGraph_CompoundId; + using RefIdType = BRepGraph_ChildRefId; + using ContainerType = NCollection_LinearVector; + + static IdType Id(const BRepGraph& theGraph, const RefIdType theRefId) + { + return theGraph.Refs().Children().Entry(theRefId).ParentCompoundId; + } +}; + +struct OccurrenceFromOccurrenceRefTraits +{ + using IdType = BRepGraph_OccurrenceId; + using RefIdType = BRepGraph_OccurrenceRefId; + using ContainerType = NCollection_LinearVector; + + static IdType Id(const BRepGraph& theGraph, const RefIdType theRefId) + { + return theGraph.Refs().Occurrences().Entry(theRefId).ChildOccurrenceId; + } +}; + +struct ProductFromOccurrenceRefTraits +{ + using IdType = BRepGraph_ProductId; + using RefIdType = BRepGraph_OccurrenceRefId; + using ContainerType = NCollection_LinearVector; + + static IdType Id(const BRepGraph& theGraph, const RefIdType theRefId) + { + return theGraph.Refs().Occurrences().Entry(theRefId).ParentProductId; } }; } // namespace BRepGraph_ReverseIterator // Vertex -> parent Edges -using BRepGraph_EdgesOfVertex = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_EdgesOfVertex = + BRepGraph_ReverseIterator::ParentsOf>; +// Vertex -> parent Compounds +using BRepGraph_CompoundsOfVertex = + BRepGraph_ReverseIterator::IdsOfRefs; + // Edge -> parent Wires -using BRepGraph_WiresOfEdge = BRepGraph_ReverseIterator::ParentsOf; +class BRepGraph_WiresOfEdge : public BRepGraph_ReverseIterator::EdgeParentsOf< + BRepGraph_ReverseIterator::WireFromEdgeCoEdgeTraits> +{ +public: + using BRepGraph_ReverseIterator::EdgeParentsOf< + BRepGraph_ReverseIterator::WireFromEdgeCoEdgeTraits>::EdgeParentsOf; +}; + // Edge -> parent CoEdges -using BRepGraph_CoEdgesOfEdge = BRepGraph_ReverseIterator::ParentsOf; -// Edge -> parent Faces (derived from CoEdge.FaceDefId) -using BRepGraph_FacesOfEdge = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_CoEdgesOfEdge = + BRepGraph_ReverseIterator::ParentsOf>; + +// Edge -> parent Faces (derived from CoEdge.FaceId) +class BRepGraph_FacesOfEdge : public BRepGraph_ReverseIterator::EdgeParentsOf< + BRepGraph_ReverseIterator::FaceFromEdgeCoEdgeTraits> +{ +public: + using BRepGraph_ReverseIterator::EdgeParentsOf< + BRepGraph_ReverseIterator::FaceFromEdgeCoEdgeTraits>::EdgeParentsOf; +}; + +// Edge -> parent Compounds +using BRepGraph_CompoundsOfEdge = + BRepGraph_ReverseIterator::IdsOfRefs; +// CoEdge -> parent Compounds +using BRepGraph_CompoundsOfCoEdge = + BRepGraph_ReverseIterator::IdsOfRefs; // Wire -> parent Faces -using BRepGraph_FacesOfWire = BRepGraph_ReverseIterator::ParentsOf; -// CoEdge -> parent Wires -using BRepGraph_WiresOfCoEdge = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_FacesOfWire = + BRepGraph_ReverseIterator::IdsOfRefs; +// Wire -> parent Compounds +using BRepGraph_CompoundsOfWire = + BRepGraph_ReverseIterator::IdsOfRefs; // Face -> parent Shells -using BRepGraph_ShellsOfFace = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_ShellsOfFace = + BRepGraph_ReverseIterator::IdsOfRefs; // Face -> parent Compounds -using BRepGraph_CompoundsOfFace = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_CompoundsOfFace = + BRepGraph_ReverseIterator::IdsOfRefs; // Shell -> parent Solids -using BRepGraph_SolidsOfShell = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_SolidsOfShell = + BRepGraph_ReverseIterator::IdsOfRefs; // Shell -> parent Compounds -using BRepGraph_CompoundsOfShell = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_CompoundsOfShell = + BRepGraph_ReverseIterator::IdsOfRefs; // Solid -> parent CompSolids -using BRepGraph_CompSolidsOfSolid = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_CompSolidsOfSolid = + BRepGraph_ReverseIterator::IdsOfRefs; // Solid -> parent Compounds -using BRepGraph_CompoundsOfSolid = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_CompoundsOfSolid = + BRepGraph_ReverseIterator::IdsOfRefs; // CompSolid -> parent Compounds -using BRepGraph_CompoundsOfCompSolid = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_CompoundsOfCompSolid = + BRepGraph_ReverseIterator::IdsOfRefs; // Compound -> parent Compounds -using BRepGraph_CompoundsOfCompound = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_CompoundsOfCompound = + BRepGraph_ReverseIterator::IdsOfRefs; +// Any child -> parent Compounds +using BRepGraph_CompoundsOfChild = + BRepGraph_ReverseIterator::IdsOfRefs; // Product -> Occurrences -using BRepGraph_OccurrencesOfProduct = BRepGraph_ReverseIterator::ParentsOf; +using BRepGraph_OccurrencesOfProduct = BRepGraph_ReverseIterator::IdsOfRefs< + BRepGraph_ReverseIterator::OccurrenceFromOccurrenceRefTraits>; +// Any occurrence child -> Occurrences +using BRepGraph_OccurrencesOfChild = BRepGraph_ReverseIterator::IdsOfRefs< + BRepGraph_ReverseIterator::OccurrenceFromOccurrenceRefTraits>; +// Occurrence -> parent Products +using BRepGraph_ProductsOfOccurrence = + BRepGraph_ReverseIterator::IdsOfRefs; // Ref-based reverse iterators: yield (ParentId, RefId) pairs. // These find the specific reference entry in each parent that links to the child. // Wire -> parent Faces (with WireRefId) using BRepGraph_RefsFacesOfWire = - BRepGraph_ReverseIterator::RefsParentsOf; + BRepGraph_ReverseIterator::IdsOfRefs; // Face -> parent Shells (with FaceRefId) using BRepGraph_RefsShellsOfFace = - BRepGraph_ReverseIterator::RefsParentsOf; + BRepGraph_ReverseIterator::IdsOfRefs; // Shell -> parent Solids (with ShellRefId) using BRepGraph_RefsSolidsOfShell = - BRepGraph_ReverseIterator::RefsParentsOf; -// CoEdge -> parent Wires (with CoEdgeRefId) + BRepGraph_ReverseIterator::IdsOfRefs; +// CoEdge -> parent Wires (with direct CoEdgeId usage in each wire) using BRepGraph_RefsWiresOfCoEdge = - BRepGraph_ReverseIterator::RefsParentsOf; + BRepGraph_ReverseIterator::LookupParentRefsOf; // Vertex -> parent Edges (with VertexRefId) using BRepGraph_RefsEdgesOfVertex = - BRepGraph_ReverseIterator::RefsParentsOf; + BRepGraph_ReverseIterator::LookupParentRefsOf; // Solid -> parent CompSolids (with SolidRefId) using BRepGraph_RefsCompSolidsOfSolid = - BRepGraph_ReverseIterator::RefsParentsOf; + BRepGraph_ReverseIterator::IdsOfRefs; // Any child -> parent Compounds (with ChildRefId) using BRepGraph_RefsCompoundsOfChild = - BRepGraph_ReverseIterator::RefsParentsOf; + BRepGraph_ReverseIterator::IdsOfRefs; // Occurrence -> parent Products (with OccurrenceRefId) using BRepGraph_RefsProductsOfOccurrence = - BRepGraph_ReverseIterator::RefsParentsOf; + BRepGraph_ReverseIterator::IdsOfRefs; #endif // _BRepGraph_ReverseIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx index 38cb7ad69b..e235b6365f 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.cxx @@ -12,16 +12,27 @@ // 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 @@ -29,19 +40,467 @@ namespace { struct BRepGraph_ReconstructionContext { - const BRepGraph* Graph = nullptr; - const BRepGraphInc_Storage* Storage = nullptr; - const BRepGraph_LayerParam* Params = nullptr; - const BRepGraph_LayerRegularity* Regularities = nullptr; - BRepGraphInc_Reconstruct::Cache Cache; - NCollection_Map ActiveProducts; + const BRepGraph* Graph = nullptr; + const BRepGraphInc_Storage* Storage = nullptr; + BRepGraphInc_Reconstruct::Cache Cache; + NCollection_FlatMap ActiveProducts; }; -static TopoDS_Shape reconstructProductLocal( - BRepGraph_ReconstructionContext& theContext, - const BRepGraph_ProductId theProduct, - const BRepGraph_OccurrenceId theParentOccurrence = BRepGraph_OccurrenceId(), - const bool theFilterByParentOccurrence = false); +enum class ShapeParentRoute +{ + Reject, + Core, + Supplement +}; + +static void assertMutationBoundary(BRepGraph& theGraph, [[maybe_unused]] const char* theContext) +{ + [[maybe_unused]] const bool isValid = theGraph.Editor().ValidateMutationBoundary(); + Standard_ASSERT_VOID(isValid, theContext); +} + +static bool isCoreParityOrientation(const TopAbs_Orientation theOrientation) +{ + return theOrientation == TopAbs_FORWARD || theOrientation == TopAbs_REVERSED; +} + +static ShapeParentRoute classifyShapeToParent(const TopAbs_ShapeEnum theShapeType, + const TopAbs_Orientation theOrientation, + const BRepGraph_NodeId& theParent) +{ + if (!theParent.IsValid()) + { + return ShapeParentRoute::Reject; + } + + switch (theParent.NodeKind) + { + case BRepGraph_NodeId::Kind::Product: + return theShapeType == TopAbs_SHAPE ? ShapeParentRoute::Reject : ShapeParentRoute::Core; + case BRepGraph_NodeId::Kind::Compound: + if (theShapeType == TopAbs_SHAPE) + { + return ShapeParentRoute::Reject; + } + return isCoreParityOrientation(theOrientation) ? ShapeParentRoute::Core + : ShapeParentRoute::Supplement; + case BRepGraph_NodeId::Kind::Solid: + if (theShapeType == TopAbs_SHAPE) + { + return ShapeParentRoute::Reject; + } + return theShapeType == TopAbs_SHELL && isCoreParityOrientation(theOrientation) + ? ShapeParentRoute::Core + : ShapeParentRoute::Supplement; + case BRepGraph_NodeId::Kind::Shell: + if (theShapeType == TopAbs_SHAPE) + { + return ShapeParentRoute::Reject; + } + return theShapeType == TopAbs_FACE && isCoreParityOrientation(theOrientation) + ? ShapeParentRoute::Core + : ShapeParentRoute::Supplement; + case BRepGraph_NodeId::Kind::Face: + return theShapeType == TopAbs_VERTEX ? ShapeParentRoute::Supplement + : ShapeParentRoute::Reject; + case BRepGraph_NodeId::Kind::Edge: + return theShapeType == TopAbs_VERTEX ? ShapeParentRoute::Supplement + : ShapeParentRoute::Reject; + case BRepGraph_NodeId::Kind::CompSolid: + if (theShapeType == TopAbs_SHAPE) + { + return ShapeParentRoute::Reject; + } + return theShapeType == TopAbs_SOLID && isCoreParityOrientation(theOrientation) + ? ShapeParentRoute::Core + : ShapeParentRoute::Supplement; + case BRepGraph_NodeId::Kind::CoEdge: + case BRepGraph_NodeId::Kind::Wire: + case BRepGraph_NodeId::Kind::Vertex: + case BRepGraph_NodeId::Kind::Occurrence: + return ShapeParentRoute::Reject; + } + + return ShapeParentRoute::Reject; +} + +static uint64_t attachSupplementToParent(BRepGraph& theGraph, + const TopoDS_Shape& theShape, + const BRepGraph_NodeId theParent) +{ + TopoDS_Shape aSuppShape = theShape; + aSuppShape.Orientation(theShape.Orientation()); + switch (theParent.NodeKind) + { + case BRepGraph_NodeId::Kind::Compound: + return theGraph.Editor().Supplement().AttachToCompound( + BRepGraph_CompoundId(theParent), + aSuppShape, + BRepGraph_LayerTopoSupplement::AttachmentKind::CompoundAuxShape); + case BRepGraph_NodeId::Kind::CompSolid: + return theGraph.Editor().Supplement().AttachToCompSolid( + BRepGraph_CompSolidId(theParent), + aSuppShape, + BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape); + case BRepGraph_NodeId::Kind::Solid: + return theGraph.Editor().Supplement().AttachToSolid( + BRepGraph_SolidId(theParent), + aSuppShape, + BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape); + case BRepGraph_NodeId::Kind::Shell: + return theGraph.Editor().Supplement().AttachToShell( + BRepGraph_ShellId(theParent), + aSuppShape, + BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape); + case BRepGraph_NodeId::Kind::Face: + return theGraph.Editor().Supplement().AttachToFace( + BRepGraph_FaceId(theParent), + aSuppShape, + BRepGraph_LayerTopoSupplement::AttachmentKind::FaceDirectVertex); + case BRepGraph_NodeId::Kind::Edge: + return theGraph.Editor().Supplement().AttachToEdge( + BRepGraph_EdgeId(theParent), + aSuppShape, + BRepGraph_LayerTopoSupplement::AttachmentKind::EdgeInternalVertex); + default: + return 0; + } +} + +} // namespace + +//================================================================================================= + +uint32_t BRepGraph::ShapesView::snapshotCountForKind(const BRepGraph& theGraph, + const TopAbs_ShapeEnum theShapeType) +{ + const BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage; + switch (theShapeType) + { + case TopAbs_COMPOUND: + return static_cast(aStorage.NbCompounds()); + case TopAbs_COMPSOLID: + return static_cast(aStorage.NbCompSolids()); + case TopAbs_SOLID: + return static_cast(aStorage.NbSolids()); + case TopAbs_SHELL: + return static_cast(aStorage.NbShells()); + case TopAbs_FACE: + return static_cast(aStorage.NbFaces()); + case TopAbs_WIRE: + return static_cast(aStorage.NbWires()); + case TopAbs_EDGE: + return static_cast(aStorage.NbEdges()); + case TopAbs_VERTEX: + return static_cast(aStorage.NbVertices()); + default: + return 0; + } +} + +//================================================================================================= + +BRepGraph_NodeId BRepGraph::ShapesView::detectTopologyRoot(const BRepGraph& theGraph, + const TopAbs_ShapeEnum theShapeType, + const uint32_t theOldCountOfShapeKind) +{ + const uint32_t aNewCount = snapshotCountForKind(theGraph, theShapeType); + if (aNewCount <= theOldCountOfShapeKind) + { + return BRepGraph_NodeId(); + } + + // BRepGraphInc_Populate::Append appends entities in declaration order, so the first entity + // appended for a given shape type is always the shape root (index == pre-append count). + // This assumption holds as long as no intermediate entities of the same type are inserted + // before the root node during population. + switch (theShapeType) + { + case TopAbs_COMPOUND: + return BRepGraph_CompoundId(theOldCountOfShapeKind); + case TopAbs_COMPSOLID: + return BRepGraph_CompSolidId(theOldCountOfShapeKind); + case TopAbs_SOLID: + return BRepGraph_SolidId(theOldCountOfShapeKind); + case TopAbs_SHELL: + return BRepGraph_ShellId(theOldCountOfShapeKind); + case TopAbs_FACE: + return BRepGraph_FaceId(theOldCountOfShapeKind); + case TopAbs_WIRE: + return BRepGraph_WireId(theOldCountOfShapeKind); + case TopAbs_EDGE: + return BRepGraph_EdgeId(theOldCountOfShapeKind); + case TopAbs_VERTEX: + return BRepGraph_VertexId(theOldCountOfShapeKind); + default: + return BRepGraph_NodeId(); + } +} + +void BRepGraph::ShapesView::populateUIDsIncremental(BRepGraph& theGraph, + const uint32_t theOldVtx, + const uint32_t theOldEdge, + const uint32_t theOldCoEdge, + const uint32_t theOldWire, + const uint32_t theOldFace, + const uint32_t theOldShell, + const uint32_t theOldSolid, + const uint32_t theOldComp, + const uint32_t theOldCS, + const uint32_t theOldProduct, + const uint32_t theOldOccurrence, + const uint32_t theOldShellRef, + const uint32_t theOldFaceRef, + const uint32_t theOldWireRef, + const uint32_t theOldVertexRef, + const uint32_t theOldSolidRef, + const uint32_t theOldChildRef) +{ + for (BRepGraph_FullVertexIterator aVertexIt(theGraph, BRepGraph_VertexId(theOldVtx)); + aVertexIt.More(); + aVertexIt.Next()) + { + theGraph.allocateUID(aVertexIt.CurrentId()); + } + for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph, BRepGraph_EdgeId(theOldEdge)); anEdgeIt.More(); + anEdgeIt.Next()) + { + theGraph.allocateUID(anEdgeIt.CurrentId()); + } + for (BRepGraph_FullCoEdgeIterator aCoEdgeIt(theGraph, BRepGraph_CoEdgeId(theOldCoEdge)); + aCoEdgeIt.More(); + aCoEdgeIt.Next()) + { + theGraph.allocateUID(aCoEdgeIt.CurrentId()); + } + for (BRepGraph_FullWireIterator aWireIt(theGraph, BRepGraph_WireId(theOldWire)); aWireIt.More(); + aWireIt.Next()) + { + theGraph.allocateUID(aWireIt.CurrentId()); + } + for (BRepGraph_FullFaceIterator aFaceIt(theGraph, BRepGraph_FaceId(theOldFace)); aFaceIt.More(); + aFaceIt.Next()) + { + theGraph.allocateUID(aFaceIt.CurrentId()); + } + for (BRepGraph_FullShellIterator aShellIt(theGraph, BRepGraph_ShellId(theOldShell)); + aShellIt.More(); + aShellIt.Next()) + { + theGraph.allocateUID(aShellIt.CurrentId()); + } + for (BRepGraph_FullSolidIterator aSolidIt(theGraph, BRepGraph_SolidId(theOldSolid)); + aSolidIt.More(); + aSolidIt.Next()) + { + theGraph.allocateUID(aSolidIt.CurrentId()); + } + for (BRepGraph_FullCompoundIterator aCompoundIt(theGraph, BRepGraph_CompoundId(theOldComp)); + aCompoundIt.More(); + aCompoundIt.Next()) + { + theGraph.allocateUID(aCompoundIt.CurrentId()); + } + for (BRepGraph_FullCompSolidIterator aCompSolidIt(theGraph, BRepGraph_CompSolidId(theOldCS)); + aCompSolidIt.More(); + aCompSolidIt.Next()) + { + theGraph.allocateUID(aCompSolidIt.CurrentId()); + } + for (BRepGraph_FullProductIterator aProductIt(theGraph, BRepGraph_ProductId(theOldProduct)); + aProductIt.More(); + aProductIt.Next()) + { + theGraph.allocateUID(aProductIt.CurrentId()); + } + for (BRepGraph_FullOccurrenceIterator anOccurrenceIt(theGraph, + BRepGraph_OccurrenceId(theOldOccurrence)); + anOccurrenceIt.More(); + anOccurrenceIt.Next()) + { + theGraph.allocateUID(anOccurrenceIt.CurrentId()); + } + + for (BRepGraph_FullShellRefIterator aShellRefIt(theGraph, BRepGraph_ShellRefId(theOldShellRef)); + aShellRefIt.More(); + aShellRefIt.Next()) + { + theGraph.allocateRefUID(aShellRefIt.CurrentId()); + } + for (BRepGraph_FullFaceRefIterator aFaceRefIt(theGraph, BRepGraph_FaceRefId(theOldFaceRef)); + aFaceRefIt.More(); + aFaceRefIt.Next()) + { + theGraph.allocateRefUID(aFaceRefIt.CurrentId()); + } + for (BRepGraph_FullWireRefIterator aWireRefIt(theGraph, BRepGraph_WireRefId(theOldWireRef)); + aWireRefIt.More(); + aWireRefIt.Next()) + { + theGraph.allocateRefUID(aWireRefIt.CurrentId()); + } + for (BRepGraph_FullVertexRefIterator aVertexRefIt(theGraph, + BRepGraph_VertexRefId(theOldVertexRef)); + aVertexRefIt.More(); + aVertexRefIt.Next()) + { + theGraph.allocateRefUID(aVertexRefIt.CurrentId()); + } + for (BRepGraph_FullSolidRefIterator aSolidRefIt(theGraph, BRepGraph_SolidRefId(theOldSolidRef)); + aSolidRefIt.More(); + aSolidRefIt.Next()) + { + theGraph.allocateRefUID(aSolidRefIt.CurrentId()); + } + for (BRepGraph_FullChildRefIterator aChildRefIt(theGraph, BRepGraph_ChildRefId(theOldChildRef)); + aChildRefIt.More(); + aChildRefIt.Next()) + { + theGraph.allocateRefUID(aChildRefIt.CurrentId()); + } +} + +BRepGraph::ShapesView::AddStatus BRepGraph::ShapesView::appendImpl( + BRepGraph& theGraph, + const TopoDS_Shape& theShape, + const BRepGraph::ShapesView::Options& theOptions, + NCollection_LinearVector* theOutFlatRoots) +{ + BRepGraphInc_Storage& aStorage = theGraph.myData->myIncStorage; + const uint32_t anOldVtx = aStorage.NbVertices(); + const uint32_t anOldEdge = aStorage.NbEdges(); + const uint32_t anOldCoEdge = aStorage.NbCoEdges(); + const uint32_t anOldWire = aStorage.NbWires(); + const uint32_t anOldFace = aStorage.NbFaces(); + const uint32_t anOldShell = aStorage.NbShells(); + const uint32_t anOldSolid = aStorage.NbSolids(); + const uint32_t anOldComp = aStorage.NbCompounds(); + const uint32_t anOldCS = aStorage.NbCompSolids(); + const uint32_t anOldProduct = aStorage.NbProducts(); + const uint32_t anOldOccurrence = aStorage.NbOccurrences(); + const uint32_t anOldShellRef = aStorage.NbShellRefs(); + const uint32_t anOldFaceRef = aStorage.NbFaceRefs(); + const uint32_t anOldWireRef = aStorage.NbWireRefs(); + const uint32_t anOldVertexRef = aStorage.NbVertexRefs(); + const uint32_t anOldSolidRef = aStorage.NbSolidRefs(); + const uint32_t anOldChildRef = aStorage.NbChildRefs(); + + BRepGraphInc_Populate::BuildStatus aBuildStatus; + if (theOptions.Flatten) + { + NCollection_LinearVector aAppendedRoots(8); + aBuildStatus = BRepGraphInc_Populate::AppendFlattened(theGraph, + theShape, + theOptions.Parallel, + aAppendedRoots, + theOptions.Populate); + if (theOutFlatRoots != nullptr) + { + for (const BRepGraph_NodeId& anId : aAppendedRoots) + { + theOutFlatRoots->Append(anId); + } + } + } + else + { + aBuildStatus = + BRepGraphInc_Populate::Append(theGraph, theShape, theOptions.Parallel, theOptions.Populate); + } + + // Map internal BuildStatus to public AddStatus. + AddStatus aAddStatus = AddStatus::Failed; + switch (aBuildStatus) + { + case BRepGraphInc_Populate::BuildStatus::Failed: + aAddStatus = AddStatus::Failed; + break; + case BRepGraphInc_Populate::BuildStatus::SuccessWithWarnings: + aAddStatus = AddStatus::SuccessWithWarnings; + break; + case BRepGraphInc_Populate::BuildStatus::Success: + aAddStatus = AddStatus::Success; + break; + } + + theGraph.myData->myIncStorage.ClearCurrentShapes(); + + populateUIDsIncremental(theGraph, + anOldVtx, + anOldEdge, + anOldCoEdge, + anOldWire, + anOldFace, + anOldShell, + anOldSolid, + anOldComp, + anOldCS, + anOldProduct, + anOldOccurrence, + anOldShellRef, + anOldFaceRef, + anOldWireRef, + anOldVertexRef, + anOldSolidRef, + anOldChildRef); + + assertMutationBoundary(theGraph, "BRepGraph::ShapesView::Add: post-append mutation boundary"); + return aAddStatus; +} + +//================================================================================================= + +void BRepGraph::ShapesView::collectAddedNodes( + const BRepGraph& theGraph, + const TopoDS_Shape& theShape, + NCollection_DataMap& theMap) +{ + if (theShape.IsNull()) + { + return; + } + + const BRepGraph::ShapesView& aShapes = theGraph.Shapes(); + + // Helper: record (shape -> NodeId) when the shape resolves to a node. + auto bind = [&](const TopoDS_Shape& aSub) { + if (aSub.IsNull() || theMap.IsBound(aSub)) + { + return; + } + const BRepGraph_NodeId aNode = aShapes.FindNode(aSub); + if (aNode.IsValid()) + { + theMap.Bind(aSub, aNode); + } + }; + + // Bind the root then enumerate every subshape kind. + bind(theShape); + + static const TopAbs_ShapeEnum aKinds[] = {TopAbs_COMPOUND, + TopAbs_COMPSOLID, + TopAbs_SOLID, + TopAbs_SHELL, + TopAbs_FACE, + TopAbs_WIRE, + TopAbs_EDGE, + TopAbs_VERTEX}; + for (const TopAbs_ShapeEnum aKind : aKinds) + { + for (TopExp_Explorer anExp(theShape, aKind); anExp.More(); anExp.Next()) + { + bind(anExp.Current()); + } + } +} + +//================================================================================================= + +namespace +{ +static TopoDS_Shape reconstructProductLocal(BRepGraph_ReconstructionContext& theContext, + const BRepGraph_ProductId theProduct); static TopoDS_Shape reconstructOccurrenceLocal(BRepGraph_ReconstructionContext& theContext, const BRepGraph_OccurrenceId theOccurrence, @@ -54,26 +513,20 @@ static TopoDS_Shape reconstructOccurrenceLocal(BRepGraph_ReconstructionContext& } const BRepGraphInc::OccurrenceDef& anOccurrence = aStorage.Occurrence(theOccurrence); - if (anOccurrence.IsRemoved || !anOccurrence.ChildDefId.IsValid()) + if (aStorage.IsRemoved(theOccurrence) || !anOccurrence.ChildNodeId.IsValid()) { return TopoDS_Shape(); } TopoDS_Shape aShape; - if (anOccurrence.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) + if (anOccurrence.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) { - aShape = reconstructProductLocal(theContext, - BRepGraph_ProductId(anOccurrence.ChildDefId), - theOccurrence, - true); + aShape = reconstructProductLocal(theContext, BRepGraph_ProductId(anOccurrence.ChildNodeId)); } else { - aShape = BRepGraphInc_Reconstruct::Node(aStorage, - anOccurrence.ChildDefId, - theContext.Cache, - theContext.Params, - theContext.Regularities); + aShape = + BRepGraphInc_Reconstruct::Node(*theContext.Graph, anOccurrence.ChildNodeId, theContext.Cache); } if (!aShape.IsNull() && !theLocalLocation.IsIdentity()) { @@ -83,11 +536,8 @@ static TopoDS_Shape reconstructOccurrenceLocal(BRepGraph_ReconstructionContext& } static TopoDS_Shape reconstructProductLocal(BRepGraph_ReconstructionContext& theContext, - const BRepGraph_ProductId theProduct, - const BRepGraph_OccurrenceId theParentOccurrence, - const bool theFilterByParentOccurrence) + const BRepGraph_ProductId theProduct) { - (void)theParentOccurrence; // Reserved for future parent-occurrence filtering. const BRepGraphInc_Storage& aStorage = *theContext.Storage; if (!theProduct.IsValid(aStorage.NbProducts())) { @@ -95,16 +545,12 @@ static TopoDS_Shape reconstructProductLocal(BRepGraph_ReconstructionContext& the } const BRepGraph_NodeId aProductNode = theProduct; - if (!theFilterByParentOccurrence) + if (const TopoDS_Shape* aCached = theContext.Cache.Seek(aProductNode)) { - if (const TopoDS_Shape* aCached = theContext.Cache.Seek(aProductNode)) - { - return *aCached; - } + return *aCached; } - const BRepGraphInc::ProductDef& aProduct = aStorage.Product(theProduct); - if (aProduct.IsRemoved || theContext.ActiveProducts.Contains(theProduct)) + if (aStorage.IsRemoved(theProduct) || theContext.ActiveProducts.Contains(theProduct)) { return TopoDS_Shape(); } @@ -119,21 +565,17 @@ static TopoDS_Shape reconstructProductLocal(BRepGraph_ReconstructionContext& the anOccIt.Next()) { const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(anOccIt.CurrentId()); - const BRepGraphInc::OccurrenceDef& aDef = aStorage.Occurrence(aRef.OccurrenceDefId); - if (aDef.ChildDefId.IsValid() && aDef.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Product) + const BRepGraphInc::OccurrenceDef& aDef = aStorage.Occurrence(aRef.ChildOccurrenceId); + if (aDef.ChildNodeId.IsValid() && aDef.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Product) { - aShapeRootNode = aDef.ChildDefId; + aShapeRootNode = aDef.ChildNodeId; aRootLocation = aRef.LocalLocation; break; } } if (aShapeRootNode.IsValid()) { - aResult = BRepGraphInc_Reconstruct::Node(aStorage, - aShapeRootNode, - theContext.Cache, - theContext.Params, - theContext.Regularities); + aResult = BRepGraphInc_Reconstruct::Node(*theContext.Graph, aShapeRootNode, theContext.Cache); if (!aResult.IsNull() && !aRootLocation.IsIdentity()) { aResult.Move(aRootLocation); @@ -150,20 +592,18 @@ static TopoDS_Shape reconstructProductLocal(BRepGraph_ReconstructionContext& the { const BRepGraphInc::OccurrenceRef& anOccurrenceRef = aStorage.OccurrenceRef(anOccIt.CurrentId()); - if (!anOccurrenceRef.OccurrenceDefId.IsValid(aStorage.NbOccurrences())) + if (!anOccurrenceRef.ChildOccurrenceId.IsValid(aStorage.NbOccurrences())) { continue; } - const BRepGraphInc::OccurrenceDef& anOccurrence = - aStorage.Occurrence(anOccurrenceRef.OccurrenceDefId); - if (anOccurrence.IsRemoved) + if (aStorage.IsRemoved(anOccurrenceRef.ChildOccurrenceId)) { continue; } TopoDS_Shape aChild = reconstructOccurrenceLocal(theContext, - anOccurrenceRef.OccurrenceDefId, + anOccurrenceRef.ChildOccurrenceId, anOccurrenceRef.LocalLocation); if (!aChild.IsNull()) { @@ -175,7 +615,7 @@ static TopoDS_Shape reconstructProductLocal(BRepGraph_ReconstructionContext& the } theContext.ActiveProducts.Remove(theProduct); - if (!theFilterByParentOccurrence && !aResult.IsNull()) + if (!aResult.IsNull()) { theContext.Cache.Bind(aProductNode, aResult); } @@ -203,26 +643,22 @@ static TopoDS_Shape reconstructShape(BRepGraph_ReconstructionContext& theContext } const BRepGraphInc::OccurrenceDef& anOccurrenceDef = aStorage.Occurrence(anOccurrence); - if (anOccurrenceDef.IsRemoved || !anOccurrenceDef.ChildDefId.IsValid()) + if (aStorage.IsRemoved(anOccurrence) || !anOccurrenceDef.ChildNodeId.IsValid()) { return TopoDS_Shape(); } TopoDS_Shape aShape; - if (anOccurrenceDef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) + if (anOccurrenceDef.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) { - aShape = reconstructProductLocal(theContext, - BRepGraph_ProductId(anOccurrenceDef.ChildDefId), - anOccurrence, - true); + aShape = + reconstructProductLocal(theContext, BRepGraph_ProductId(anOccurrenceDef.ChildNodeId)); } else { - aShape = BRepGraphInc_Reconstruct::Node(aStorage, - anOccurrenceDef.ChildDefId, - theContext.Cache, - theContext.Params, - theContext.Regularities); + aShape = BRepGraphInc_Reconstruct::Node(*theContext.Graph, + anOccurrenceDef.ChildNodeId, + theContext.Cache); } if (!aShape.IsNull()) { @@ -236,34 +672,361 @@ static TopoDS_Shape reconstructShape(BRepGraph_ReconstructionContext& theContext return aShape; } default: - return BRepGraphInc_Reconstruct::Node(aStorage, - theNode, - theContext.Cache, - theContext.Params, - theContext.Regularities); + return BRepGraphInc_Reconstruct::Node(*theContext.Graph, theNode, theContext.Cache); } } static BRepGraph_ReconstructionContext makeReconstructionContext( - const BRepGraph* theGraph, + const BRepGraph& theGraph, const BRepGraphInc_Storage& theStorage) { BRepGraph_ReconstructionContext aContext; - aContext.Graph = theGraph; + aContext.Graph = &theGraph; aContext.Storage = &theStorage; - - const occ::handle aParamLayer = - theGraph->LayerRegistry().FindLayer(); - const occ::handle aRegularityLayer = - theGraph->LayerRegistry().FindLayer(); - aContext.Params = aParamLayer.get(); - aContext.Regularities = aRegularityLayer.get(); return aContext; } } // namespace //================================================================================================= +void BRepGraph::ShapesView::CollectHistoryInputs( + const NCollection_Array1& theRoots, + NCollection_DataMap& theOutInputs) const +{ + const BRepGraph& theGraph = *myGraph; + + for (const BRepGraph_NodeId& aRootId : theRoots) + { + const TopoDS_Shape aRoot = theGraph.Shapes().Shape(aRootId); + collectAddedNodes(theGraph, aRoot, theOutInputs); + } +} + +//================================================================================================= + +BRepGraph::ShapesView::Result BRepGraph::ShapesView::AddWithHistory( + const TopoDS_Shape& theResultShape, + const NCollection_DataMap& theInputs, + const occ::handle& theHistory, + const TCollection_AsciiString& theOpLabel) +{ + Options anOptions; + anOptions.CreateAutoProduct = false; + return AddWithHistory(theResultShape, theInputs, theHistory, theOpLabel, anOptions); +} + +//================================================================================================= + +BRepGraph::ShapesView::Result BRepGraph::ShapesView::AddWithHistory( + const TopoDS_Shape& theResultShape, + const NCollection_DataMap& theInputs, + const occ::handle& theHistory, + const TCollection_AsciiString& theOpLabel, + const BRepGraph::ShapesView::Options& theOptions) +{ + BRepGraph& theGraph = *myGraph; + + BRepGraph_LayerHistory& aHistory = *theGraph.LayerRegistry().Ensure(); + const bool shouldAbsorbHistory = aHistory.IsEnabled() && !theHistory.IsNull(); + Options aBuildOptions = theOptions; + aBuildOptions.TrackAddedNodes = theOptions.TrackAddedNodes || shouldAbsorbHistory; + + Result aResult = Add(theResultShape, aBuildOptions); + if (aResult.IsOk() && shouldAbsorbHistory) + { + aHistory.Absorb(theInputs, aResult.AddedNodes, theHistory, theOpLabel); + } + return aResult; +} + +//================================================================================================= + +BRepGraph::ShapesView::Result BRepGraph::ShapesView::AddWithHistory( + const TopoDS_Shape& theResultShape, + const NCollection_Array1& theInputRoots, + const occ::handle& theHistory, + const TCollection_AsciiString& theOpLabel) +{ + Options anOptions; + anOptions.CreateAutoProduct = false; + return AddWithHistory(theResultShape, theInputRoots, theHistory, theOpLabel, anOptions); +} + +//================================================================================================= + +BRepGraph::ShapesView::Result BRepGraph::ShapesView::AddWithHistory( + const TopoDS_Shape& theResultShape, + const NCollection_Array1& theInputRoots, + const occ::handle& theHistory, + const TCollection_AsciiString& theOpLabel, + const BRepGraph::ShapesView::Options& theOptions) +{ + NCollection_DataMap anInputs; + CollectHistoryInputs(theInputRoots, anInputs); + return AddWithHistory(theResultShape, anInputs, theHistory, theOpLabel, theOptions); +} + +//================================================================================================= + +BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add(const TopoDS_Shape& theShape) +{ + return Add(theShape, Options{}); +} + +//================================================================================================= + +BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add( + const TopoDS_Shape& theShape, + const BRepGraph::ShapesView::Options& theOptions) +{ + BRepGraph& theGraph = *myGraph; + Result aResult; + if (theShape.IsNull()) + { + return aResult; + } + + const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType()); + + NCollection_LinearVector aFlatRoots; + const AddStatus aAddStatus = + appendImpl(theGraph, theShape, theOptions, theOptions.Flatten ? &aFlatRoots : nullptr); + + aResult.Status = aAddStatus; + + if (theOptions.Flatten && !aFlatRoots.IsEmpty()) + { + aResult.TopologyRoot = aFlatRoots.First(); + } + else + { + aResult.TopologyRoot = detectTopologyRoot(theGraph, theShape.ShapeType(), anOldCount); + } + + if (theOptions.CreateAutoProduct && aResult.TopologyRoot.IsValid()) + { + aResult.Product = theGraph.Editor().Products().Add(aResult.TopologyRoot, theShape.Location()); + theGraph.Editor().Products().AppendDocumentRoot(aResult.Product); + if (aResult.Product.IsValid()) + { + const BRepGraphInc::ProductRelations& aProductRel = + theGraph.myData->myIncStorage.ProductRelations(aResult.Product); + if (!aProductRel.OccurrenceRefIds.IsEmpty()) + { + const BRepGraph_OccurrenceRefId anOccRefId = aProductRel.OccurrenceRefIds.First(); + const BRepGraph_OccurrenceId anOccId = + theGraph.myData->myIncStorage.OccurrenceRef(anOccRefId).ChildOccurrenceId; + aResult.Occurrence = anOccId; + } + } + } + + if (!aResult.TopologyRoot.IsValid() + && !(theOptions.CreateAutoProduct && aResult.Product.IsValid())) + { + aResult.Status = AddStatus::Failed; + } + if (theOptions.TrackAddedNodes && aResult.TopologyRoot.IsValid()) + { + collectAddedNodes(theGraph, theShape, aResult.AddedNodes); + } + return aResult; +} + +//================================================================================================= + +BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add(const TopoDS_Shape& theShape, + const BRepGraph_NodeId theParent) +{ + return Add(theShape, theParent, Options{}); +} + +//================================================================================================= + +BRepGraph::ShapesView::Result BRepGraph::ShapesView::Add( + const TopoDS_Shape& theShape, + const BRepGraph_NodeId theParent, + const BRepGraph::ShapesView::Options& theOptions) +{ + BRepGraph& theGraph = *myGraph; + Result aResult; + if (theShape.IsNull() || !theParent.IsValid()) + { + return aResult; + } + + const ShapeParentRoute aRoute = + classifyShapeToParent(theShape.ShapeType(), theShape.Orientation(), theParent); + if (aRoute == ShapeParentRoute::Reject) + { + return aResult; + } + if (aRoute == ShapeParentRoute::Supplement) + { + if (attachSupplementToParent(theGraph, theShape, theParent) != 0) + { + aResult.Status = AddStatus::Success; + } + return aResult; + } + + const uint32_t anOldCount = snapshotCountForKind(theGraph, theShape.ShapeType()); + + Options anInner = theOptions; + anInner.CreateAutoProduct = false; + + NCollection_LinearVector aFlatRoots; + const AddStatus aStatus = + appendImpl(theGraph, theShape, anInner, anInner.Flatten ? &aFlatRoots : nullptr); + + if (aStatus == AddStatus::Failed) + { + return aResult; + } + + if (anInner.Flatten && !aFlatRoots.IsEmpty()) + { + aResult.TopologyRoot = aFlatRoots.First(); + } + else + { + aResult.TopologyRoot = detectTopologyRoot(theGraph, theShape.ShapeType(), anOldCount); + } + if (!aResult.TopologyRoot.IsValid()) + { + return aResult; + } + + if (anInner.TrackAddedNodes) + { + collectAddedNodes(theGraph, theShape, aResult.AddedNodes); + } + + switch (theParent.NodeKind) + { + case BRepGraph_NodeId::Kind::Product: { + const BRepGraph_ProductId aChildProduct = + theGraph.Editor().Products().Add(aResult.TopologyRoot, TopLoc_Location()); + if (!aChildProduct.IsValid()) + { + return aResult; + } + + BRepGraph_OccurrenceRefId anOccRefId; + const BRepGraph_OccurrenceId anOccId = + theGraph.Editor().Products().Append(BRepGraph_ProductId(theParent), + aChildProduct, + theShape.Location(), + BRepGraph_OccurrenceId(), + &anOccRefId); + if (!anOccId.IsValid()) + { + return aResult; + } + aResult.Product = aChildProduct; + aResult.Occurrence = anOccId; + aResult.InsertedRef = anOccRefId; + aResult.Status = AddStatus::Success; + return aResult; + } + case BRepGraph_NodeId::Kind::Compound: { + const TopAbs_Orientation aShapeOri = theShape.Orientation(); + const BRepGraph_ChildRefId aRid = + theGraph.Editor().Compounds().Append(BRepGraph_CompoundId(theParent), + aResult.TopologyRoot, + aShapeOri); + if (!aRid.IsValid()) + { + return aResult; + } + aResult.InsertedRef = aRid; + aResult.Status = AddStatus::Success; + return aResult; + } + case BRepGraph_NodeId::Kind::Shell: { + const TopAbs_Orientation aShapeOri = theShape.Orientation(); + const BRepGraph_ShellId aShell(theParent); + const BRepGraph_FaceRefId aRid = + theGraph.Editor().Shells().Append(aShell, + BRepGraph_FaceId(aResult.TopologyRoot), + aShapeOri); + if (!aRid.IsValid()) + { + return aResult; + } + aResult.InsertedRef = aRid; + aResult.Status = AddStatus::Success; + return aResult; + } + case BRepGraph_NodeId::Kind::Solid: { + const BRepGraph_SolidId aSolid(theParent); + if (aResult.TopologyRoot.NodeKind == BRepGraph_NodeId::Kind::Shell) + { + const TopAbs_Orientation aShapeOri = theShape.Orientation(); + const BRepGraph_ShellRefId aRid = + theGraph.Editor().Solids().Append(aSolid, + BRepGraph_ShellId(aResult.TopologyRoot), + aShapeOri); + if (!aRid.IsValid()) + { + return aResult; + } + aResult.InsertedRef = aRid; + aResult.Status = AddStatus::Success; + return aResult; + } + return aResult; + } + case BRepGraph_NodeId::Kind::CompSolid: { + if (aResult.TopologyRoot.NodeKind != BRepGraph_NodeId::Kind::Solid) + { + return aResult; + } + const TopAbs_Orientation aShapeOri = theShape.Orientation(); + const BRepGraph_SolidRefId aRid = + theGraph.Editor().CompSolids().Append(BRepGraph_CompSolidId(theParent), + BRepGraph_SolidId(aResult.TopologyRoot), + aShapeOri); + if (!aRid.IsValid()) + { + return aResult; + } + aResult.InsertedRef = aRid; + aResult.Status = AddStatus::Success; + return aResult; + } + default: + return aResult; + } +} + +//================================================================================================= + +void BRepGraph::ShapesView::ClearCached(const BRepGraph_NodeId theNode) +{ + if (myGraph == nullptr || !theNode.IsValid() || myGraph->Topo().Gen().IsRemoved(theNode)) + { + return; + } + + myGraph->myData->myIncStorage.UnbindCurrentShape(theNode); +} + +//================================================================================================= + +void BRepGraph::ShapesView::ClearCached(const BRepGraph_RefId theRef) +{ + if (myGraph == nullptr || !theRef.IsValid() || myGraph->Refs().Gen().IsRemoved(theRef)) + { + return; + } + + const BRepGraph_NodeId aChildNode = myGraph->Refs().Gen().ChildNode(theRef); + ClearCached(aChildNode); +} + +//================================================================================================= + TopoDS_Shape BRepGraph::ShapesView::Shape(const BRepGraph_NodeId theNode) const { if (!theNode.IsValid()) @@ -271,25 +1034,27 @@ TopoDS_Shape BRepGraph::ShapesView::Shape(const BRepGraph_NodeId theNode) const return TopoDS_Shape(); } - // Fast path: if entity was never mutated, return the original shape. - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef != nullptr && aDef->IsRemoved) + if (theNode.IsRemoved(*myGraph)) { return TopoDS_Shape(); } + // Fast path: if entity was never mutated, return the original shape. + const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); if (aDef != nullptr && aDef->SubtreeGen == 0) { - const TopoDS_Shape* anOrig = FindOriginal(theNode); - if (anOrig != nullptr) + TopoDS_Shape anOrig = Original(theNode); + if (!anOrig.IsNull()) { - return *anOrig; + return anOrig; } } // Check mutable cache under shared lock with SubtreeGen validation. { - std::shared_lock aReadLock(myGraph->myData->myCurrentShapesMutex); - const BRepGraph_Data::CachedShape* aCached = myGraph->myData->myCurrentShapes.Seek(theNode); + std::shared_lock aReadLock( + myGraph->myData->myIncStorage.CurrentShapesMutex()); + const BRepGraphInc_Storage::CachedShape* aCached = + myGraph->myData->myIncStorage.CurrentShapes().Seek(theNode); if (aCached != nullptr && aDef != nullptr && aCached->StoredSubtreeGen == aDef->SubtreeGen) { return aCached->Shape; @@ -298,23 +1063,25 @@ TopoDS_Shape BRepGraph::ShapesView::Shape(const BRepGraph_NodeId theNode) const // Reconstruct from incidence storage / assembly facade. BRepGraph_ReconstructionContext aContext = - makeReconstructionContext(myGraph, myGraph->myData->myIncStorage); + makeReconstructionContext(*myGraph, myGraph->myData->myIncStorage); TopoDS_Shape aReconstructed = reconstructShape(aContext, theNode); // Store under exclusive lock with double-check to avoid redundant writes // when multiple threads reconstruct the same parent node concurrently. if (!aReconstructed.IsNull() && aDef != nullptr) { - std::unique_lock aWriteLock(myGraph->myData->myCurrentShapesMutex); - const BRepGraph_Data::CachedShape* aExisting = myGraph->myData->myCurrentShapes.Seek(theNode); + std::unique_lock aWriteLock( + myGraph->myData->myIncStorage.CurrentShapesMutex()); + const BRepGraphInc_Storage::CachedShape* aExisting = + myGraph->myData->myIncStorage.CurrentShapes().Seek(theNode); if (aExisting != nullptr && aExisting->StoredSubtreeGen == aDef->SubtreeGen) { return aExisting->Shape; } - BRepGraph_Data::CachedShape anEntry; + BRepGraphInc_Storage::CachedShape anEntry; anEntry.Shape = aReconstructed; anEntry.StoredSubtreeGen = aDef->SubtreeGen; - myGraph->myData->myCurrentShapes.Bind(theNode, anEntry); + myGraph->myData->myIncStorage.ChangeCurrentShapes().Bind(theNode, anEntry); if (theNode.NodeKind != BRepGraph_NodeId::Kind::Product && theNode.NodeKind != BRepGraph_NodeId::Kind::Occurrence) { @@ -328,51 +1095,39 @@ TopoDS_Shape BRepGraph::ShapesView::Shape(const BRepGraph_NodeId theNode) const bool BRepGraph::ShapesView::HasOriginal(const BRepGraph_NodeId theNode) const { - return FindOriginal(theNode) != nullptr; + return !Original(theNode).IsNull(); } //================================================================================================= -const TopoDS_Shape* BRepGraph::ShapesView::FindOriginal(const BRepGraph_NodeId theNode) const +TopoDS_Shape BRepGraph::ShapesView::Original(const BRepGraph_NodeId theNode) const { if (!theNode.IsValid()) { - return nullptr; + return TopoDS_Shape(); } - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef != nullptr && aDef->IsRemoved) + if (theNode.IsRemoved(*myGraph)) { - return nullptr; + return TopoDS_Shape(); } - return myGraph->myData->myIncStorage.FindOriginal(theNode); -} - -//================================================================================================= - -const TopoDS_Shape& BRepGraph::ShapesView::OriginalOf(const BRepGraph_NodeId theNode) const -{ - const TopoDS_Shape* aShape = FindOriginal(theNode); - if (aShape == nullptr) - { - throw Standard_ProgramError("BRepGraph::ShapesView::OriginalOf() - no original shape."); - } - return *aShape; + const TopoDS_Shape* anOriginal = myGraph->myData->myIncStorage.FindOriginal(theNode); + return anOriginal != nullptr ? *anOriginal : TopoDS_Shape(); } //================================================================================================= TopoDS_Shape BRepGraph::ShapesView::Reconstruct(const BRepGraph_NodeId theRoot) const { - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theRoot); - if (aDef != nullptr && aDef->IsRemoved) + if (theRoot.IsRemoved(*myGraph)) { return TopoDS_Shape(); } BRepGraph_ReconstructionContext aContext = - makeReconstructionContext(myGraph, myGraph->myData->myIncStorage); - return reconstructShape(aContext, theRoot); + makeReconstructionContext(*myGraph, myGraph->myData->myIncStorage); + TopoDS_Shape aResult = reconstructShape(aContext, theRoot); + return aResult; } //================================================================================================= @@ -390,8 +1145,12 @@ BRepGraph_NodeId BRepGraph::ShapesView::FindNode(const TopoDS_Shape& theShape) c myGraph->myData->myIncStorage.FindNodeByTShape(theShape.TShape().get()); if (aNodeId != nullptr) { + if ((*aNodeId).IsRemoved(*myGraph)) + { + return BRepGraph_NodeId(); + } const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(*aNodeId); - if (aDef == nullptr || aDef->IsRemoved) + if (aDef == nullptr) { return BRepGraph_NodeId(); } @@ -411,3 +1170,17 @@ bool BRepGraph::ShapesView::HasNode(const TopoDS_Shape& theShape) const return FindNode(theShape).IsValid(); } + +//================================================================================================= + +bool BRepGraph::ShapesView::RemoveShape(const TopoDS_Shape& theShape) +{ + const BRepGraph_NodeId aNode = FindNode(theShape); + if (!aNode.IsValid()) + { + return false; + } + + myGraph->Editor().Gen().RemoveNode(aNode); + return true; +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.hxx index d3af85d964..abc6f74a3d 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_ShapesView.hxx @@ -15,8 +15,18 @@ #define _BRepGraph_ShapesView_HeaderFile #include +#include +#include +#include +#include +#include +#include +#include -//! @brief Read-only view for TopoDS_Shape reconstruction from graph data. +class BRepTools_History; +class TCollection_AsciiString; + +//! @brief View for TopoDS_Shape ingestion, reconstruction and lookup. //! //! Reconstructs TopoDS shapes from graph nodes on demand, with caching //! for repeated access. Topology nodes are delegated to the incidence-table @@ -26,11 +36,129 @@ //! NodeIds via TShape pointer comparison. Shape() is the stable cached public //! route for repeated access; Reconstruct() forces a fresh rebuild with the //! same node-kind semantics and bypasses the persistent reconstructed-shape cache. -//! BRepGraph_Builder::Add() and Compact() clear the persistent reconstructed-shape cache. +//! Add() and Compact() clear the persistent reconstructed-shape cache. //! Obtained via BRepGraph::Shapes(). class BRepGraph::ShapesView { public: + //! Shape-ingestion options. + struct Options + { + BRepGraphInc_Populate::Options Populate; + bool CreateAutoProduct = true; //!< wrap topology root in a Product (unparented Add only) + bool Flatten = false; //!< drop hierarchy containers, append faces as roots + bool Parallel = false; //!< run face-level construction in parallel + //! Capture every input subshape's NodeId in Result::AddedNodes. Off by + //! default so the hot path pays nothing. Used by algorithm wrappers + //! (Booleans, fillets, ...) that need to translate TopoDS_Shape objects + //! returned by an OCCT algorithm into graph NodeIds for history harvest. + bool TrackAddedNodes = false; + }; + + //! Status of a single Add() call. + enum class AddStatus + { + Success, //!< All faces built successfully. + SuccessWithWarnings, //!< Build completed with diagnostics, e.g. unbounded natural faces. + Failed //!< Build failed (e.g., null shape). + }; + + //! Outcome of a single Add() call. + struct Result + { + BRepGraph_NodeId TopologyRoot; + BRepGraph_ProductId Product; + BRepGraph_OccurrenceId Occurrence; + BRepGraph_RefId InsertedRef; + AddStatus Status = AddStatus::Failed; + + //! True if the build succeeded (with or without warnings). + [[nodiscard]] bool IsOk() const { return Status != AddStatus::Failed; } + + //! Populated only when Options::TrackAddedNodes is true. Maps every + //! subshape of the input @c theShape (including the root) to the + //! BRepGraph_NodeId it resolves to after the Add. Multiple input + //! shapes that share identity collapse to one entry, as in OCCT's + //! map types. + NCollection_DataMap AddedNodes; + }; + + //! Ingest a TopoDS_Shape as a new root subgraph, wrapping the topology root in a Product. + //! @param[in] theShape shape to ingest + //! @return Result with TopologyRoot, Product and Occurrence set on success. + [[nodiscard]] Standard_EXPORT Result Add(const TopoDS_Shape& theShape); + + //! Ingest a TopoDS_Shape as a new root subgraph with explicit options. + //! @param[in] theShape shape to ingest + //! @param[in] theOptions shape-ingestion options + //! @return Result with TopologyRoot set on success; Product/Occurrence set + //! when theOptions.CreateAutoProduct is true. + [[nodiscard]] Standard_EXPORT Result Add(const TopoDS_Shape& theShape, const Options& theOptions); + + //! Ingest a TopoDS_Shape under an existing parent. + //! + //! Parent kind dispatch: + //! - Product: creates a child part-product, links via Occurrence with shape.Location(). + //! - Compound: appends topology root as a child reference. + //! - Shell: appends a Face as a FaceRef; other shapes via AddChild. + //! - Solid: appends a Shell as a ShellRef; other shapes via AddChild. + //! - CompSolid: appends a Solid as a SolidRef. + //! Other parent kinds (Wire, Edge, Vertex, Occurrence) are not supported and yield + //! an invalid Result (Result::Ok == false) without modification to the graph. + //! @param[in] theShape shape to ingest + //! @param[in] theParent parent node receiving the topology + //! @return Result with TopologyRoot set, plus (Product, Occurrence, InsertedRef) for Product + //! parents or InsertedRef for topology container parents. + [[nodiscard]] Standard_EXPORT Result Add(const TopoDS_Shape& theShape, + const BRepGraph_NodeId theParent); + + //! Ingest a shape under an existing parent with explicit options. + //! Options::CreateAutoProduct is ignored. + [[nodiscard]] Standard_EXPORT Result Add(const TopoDS_Shape& theShape, + const BRepGraph_NodeId theParent, + const Options& theOptions); + + //! Collect a TopoDS_Shape -> NodeId map for graph roots and all subshapes + //! resolvable through FindNode(). This is intended for algorithms that + //! reconstruct selected graph roots to TopoDS, run OCCT, and then need to + //! translate BRepTools_History back to graph NodeIds. + Standard_EXPORT void CollectHistoryInputs( + const NCollection_Array1& theRoots, + NCollection_DataMap& theOutInputs) + const; + + //! Add an OCCT algorithm result and absorb BRepTools_History into the + //! registered BRepGraph_LayerHistory layer using explicit input shape mapping. + [[nodiscard]] Standard_EXPORT Result AddWithHistory( + const TopoDS_Shape& theResultShape, + const NCollection_DataMap& theInputs, + const occ::handle& theHistory, + const TCollection_AsciiString& theOpLabel); + + //! Add an OCCT algorithm result and absorb BRepTools_History with explicit options. + [[nodiscard]] Standard_EXPORT Result AddWithHistory( + const TopoDS_Shape& theResultShape, + const NCollection_DataMap& theInputs, + const occ::handle& theHistory, + const TCollection_AsciiString& theOpLabel, + const Options& theOptions); + + //! Convenience overload that collects the history input map from selected roots. + [[nodiscard]] Standard_EXPORT Result + AddWithHistory(const TopoDS_Shape& theResultShape, + const NCollection_Array1& theInputRoots, + const occ::handle& theHistory, + const TCollection_AsciiString& theOpLabel); + + //! Convenience overload that collects the history input map from selected roots + //! and uses explicit options. + [[nodiscard]] Standard_EXPORT Result + AddWithHistory(const TopoDS_Shape& theResultShape, + const NCollection_Array1& theInputRoots, + const occ::handle& theHistory, + const TCollection_AsciiString& theOpLabel, + const Options& theOptions); + //! Return or reconstruct a TopoDS_Shape for a node. //! Prefer this route for repeated public queries. //! Returns a cached shape when available and valid; otherwise reconstructs. @@ -48,19 +176,10 @@ public: //! @return true if an original shape exists [[nodiscard]] Standard_EXPORT bool HasOriginal(const BRepGraph_NodeId theNode) const; - //! Return a pointer to the original TopoDS_Shape stored during graph construction. - //! This is the non-throw lookup counterpart of OriginalOf(). - //! @param[in] theNode node identifier - //! @return pointer to original shape for an active node, or nullptr when absent/invalid/removed - [[nodiscard]] Standard_EXPORT const TopoDS_Shape* FindOriginal( - const BRepGraph_NodeId theNode) const; - //! Return the original TopoDS_Shape stored during graph construction. //! @param[in] theNode node identifier - //! @return reference to the exact TopoDS_Shape stored during graph construction - //! @exception Standard_ProgramError if no original shape exists - [[nodiscard]] Standard_EXPORT const TopoDS_Shape& OriginalOf( - const BRepGraph_NodeId theNode) const; + //! @return original shape for an active node, or null shape when absent/invalid/removed + [[nodiscard]] Standard_EXPORT TopoDS_Shape Original(const BRepGraph_NodeId theNode) const; //! Reconstruct a TopoDS_Shape from a graph node without using the persistent cache. //! Use this when the caller explicitly needs a fresh rebuild instead of the @@ -73,11 +192,21 @@ public: //! @return reconstructed shape, or null shape for invalid/removed nodes [[nodiscard]] Standard_EXPORT TopoDS_Shape Reconstruct(const BRepGraph_NodeId theRoot) const; + //! Remove the cached reconstructed shape for one node. + //! Does not change graph generation counters and does not rebuild the shape. + //! Invalid or removed nodes are ignored. + Standard_EXPORT void ClearCached(const BRepGraph_NodeId theNode); + + //! Remove the cached reconstructed shape for the node referenced by one reference. + //! Does not change graph generation counters and does not rebuild the shape. + //! Invalid or removed references are ignored. + Standard_EXPORT void ClearCached(const BRepGraph_RefId theRef); + //! Look up the definition NodeId for a shape from graph construction input. //! Uses TShape pointer comparison (same semantics as IsSame()). //! Synthetic Product / Occurrence reconstructions are not given dedicated //! TShape bindings, so lookup is only guaranteed for construction-time topology. - //! Programmatically created Builder().Add*() nodes can still be located by + //! Programmatically created Editor().Add*() nodes can still be located by //! UID or by direct iteration over Topo() definitions. //! @param[in] theShape shape to look up //! @return active node identifier, or invalid NodeId if the shape is absent or removed @@ -87,22 +216,71 @@ public: //! Uses TShape pointer comparison (same semantics as IsSame()). //! Synthetic Product / Occurrence reconstructions are not given dedicated //! TShape bindings, so this is only guaranteed for construction-time topology. - //! Programmatically created Builder().Add*() nodes can still be located by + //! Programmatically created Editor().Add*() nodes can still be located by //! UID or by direct iteration over Topo() definitions. //! @param[in] theShape shape to check //! @return true if the shape has a corresponding active definition node [[nodiscard]] Standard_EXPORT bool HasNode(const TopoDS_Shape& theShape) const; + //! Remove the active graph node corresponding to a construction-time shape. + //! This is the convenience equivalent of FindNode(theShape) followed by + //! Editor().Gen().RemoveNode(node). + //! @param[in] theShape shape to remove + //! @return true when an active node was found and removed + Standard_EXPORT bool RemoveShape(const TopoDS_Shape& theShape); + private: friend class BRepGraph; friend struct BRepGraph_Data; - explicit ShapesView(const BRepGraph* theGraph) + explicit ShapesView(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + [[nodiscard]] static AddStatus appendImpl( + BRepGraph& theGraph, + const TopoDS_Shape& theShape, + const Options& theOptions, + NCollection_LinearVector* theOutFlatRoots = nullptr); + + //! Walk @p theShape and populate @p theMap with (subshape -> NodeId) + //! entries for every subshape resolvable through @c theGraph.Shapes(). + //! Used by the Add() overloads when Options::TrackAddedNodes is true. + //! Shape identity follows TopTools_ShapeMapHasher (TShape pointer + + //! Location), matching OCCT's standard shape-keyed maps. + static void collectAddedNodes( + const BRepGraph& theGraph, + const TopoDS_Shape& theShape, + NCollection_DataMap& theMap); + + static BRepGraph_NodeId detectTopologyRoot(const BRepGraph& theGraph, + const TopAbs_ShapeEnum theShapeType, + const uint32_t theOldCountOfShapeKind); + + static uint32_t snapshotCountForKind(const BRepGraph& theGraph, + const TopAbs_ShapeEnum theShapeType); + + static void populateUIDsIncremental(BRepGraph& theGraph, + const uint32_t theOldVtx, + const uint32_t theOldEdge, + const uint32_t theOldCoEdge, + const uint32_t theOldWire, + const uint32_t theOldFace, + const uint32_t theOldShell, + const uint32_t theOldSolid, + const uint32_t theOldComp, + const uint32_t theOldCS, + const uint32_t theOldProduct, + const uint32_t theOldOccurrence, + const uint32_t theOldShellRef, + const uint32_t theOldFaceRef, + const uint32_t theOldWireRef, + const uint32_t theOldVertexRef, + const uint32_t theOldSolidRef, + const uint32_t theOldChildRef); + + BRepGraph* myGraph; }; #endif // _BRepGraph_ShapesView_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementEditor.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementEditor.cxx new file mode 100644 index 0000000000..54006bc734 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementEditor.cxx @@ -0,0 +1,143 @@ +// 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 + +#include +#include + +namespace +{ +bool isValidSupplementOwner(const BRepGraph& theGraph, const BRepGraph_NodeId theOwner) +{ + if (!theOwner.IsValid()) + { + return false; + } + + switch (theOwner.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + case BRepGraph_NodeId::Kind::Edge: + case BRepGraph_NodeId::Kind::Face: + case BRepGraph_NodeId::Kind::Shell: + case BRepGraph_NodeId::Kind::Solid: + case BRepGraph_NodeId::Kind::CompSolid: + case BRepGraph_NodeId::Kind::Compound: + break; + case BRepGraph_NodeId::Kind::Wire: + case BRepGraph_NodeId::Kind::CoEdge: + case BRepGraph_NodeId::Kind::Product: + case BRepGraph_NodeId::Kind::Occurrence: + return false; + } + + return !theGraph.Topo().Gen().IsRemoved(theOwner); +} +} // namespace + +//================================================================================================= + +uint64_t BRepGraph_SupplementEditor::Attach( + const BRepGraph_NodeId theOwner, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind, + const TopoDS_Shape& theShape) +{ + if (!isValidSupplementOwner(myGraph, theOwner) || theShape.IsNull()) + { + return 0; + } + + return myGraph.LayerRegistry().Ensure()->AddAttachment(theOwner, + theKind, + theShape); +} + +//================================================================================================= + +uint64_t BRepGraph_SupplementEditor::AttachToVertex( + const BRepGraph_VertexId theVertex, + const TopoDS_Shape& theShape, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind) +{ + return Attach(BRepGraph_NodeId(theVertex), theKind, theShape); +} + +//================================================================================================= + +uint64_t BRepGraph_SupplementEditor::AttachToEdge( + const BRepGraph_EdgeId theEdge, + const TopoDS_Shape& theShape, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind) +{ + return Attach(BRepGraph_NodeId(theEdge), theKind, theShape); +} + +//================================================================================================= + +uint64_t BRepGraph_SupplementEditor::AttachToFace( + const BRepGraph_FaceId theFace, + const TopoDS_Shape& theShape, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind) +{ + return Attach(BRepGraph_NodeId(theFace), theKind, theShape); +} + +//================================================================================================= + +uint64_t BRepGraph_SupplementEditor::AttachToSolid( + const BRepGraph_SolidId theSolid, + const TopoDS_Shape& theShape, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind) +{ + return Attach(BRepGraph_NodeId(theSolid), theKind, theShape); +} + +//================================================================================================= + +uint64_t BRepGraph_SupplementEditor::AttachToCompSolid( + const BRepGraph_CompSolidId theCompSolid, + const TopoDS_Shape& theShape, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind) +{ + return Attach(BRepGraph_NodeId(theCompSolid), theKind, theShape); +} + +//================================================================================================= + +uint64_t BRepGraph_SupplementEditor::AttachToShell( + const BRepGraph_ShellId theShell, + const TopoDS_Shape& theShape, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind) +{ + return Attach(BRepGraph_NodeId(theShell), theKind, theShape); +} + +//================================================================================================= + +uint64_t BRepGraph_SupplementEditor::AttachToCompound( + const BRepGraph_CompoundId theCompound, + const TopoDS_Shape& theShape, + const BRepGraph_LayerTopoSupplement::AttachmentKind theKind) +{ + return Attach(BRepGraph_NodeId(theCompound), theKind, theShape); +} + +//================================================================================================= + +bool BRepGraph_SupplementEditor::RemoveAttachment(const uint64_t theUid) +{ + const occ::handle aLayer = + myGraph.LayerRegistry().FindLayer(); + return !aLayer.IsNull() && aLayer->RemoveAttachment(theUid); +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementEditor.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementEditor.hxx new file mode 100644 index 0000000000..8381effdc7 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementEditor.hxx @@ -0,0 +1,127 @@ +// 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. + +#ifndef _BRepGraph_SupplementEditor_HeaderFile +#define _BRepGraph_SupplementEditor_HeaderFile + +#include +#include + +//! @brief Lightweight mutation facade for runtime supplement attachments. +class BRepGraph_SupplementEditor +{ +public: + //! @brief Create an editor facade bound to one graph instance. + //! @param[in] theGraph graph receiving supplement attachments + explicit BRepGraph_SupplementEditor(BRepGraph& theGraph) + : myGraph(theGraph) + { + } + + //! @brief Attach one supplemental shape to an arbitrary supported core owner. + //! @param[in] theOwner active owner node + //! @param[in] theKind semantic attachment kind + //! @param[in] theShape supplemental shape to attach + //! @return non-zero attachment uid on success, `0` on rejection + [[nodiscard]] Standard_EXPORT uint64_t + Attach(BRepGraph_NodeId theOwner, + BRepGraph_LayerTopoSupplement::AttachmentKind theKind, + const TopoDS_Shape& theShape); + + //! @brief Attach a supplemental shape to a vertex owner. + //! @param[in] theVertex active vertex owner + //! @param[in] theShape supplemental shape to attach + //! @param[in] theKind semantic attachment kind + //! @return non-zero attachment uid on success, `0` on rejection + [[nodiscard]] Standard_EXPORT uint64_t + AttachToVertex(BRepGraph_VertexId theVertex, + const TopoDS_Shape& theShape, + BRepGraph_LayerTopoSupplement::AttachmentKind theKind = + BRepGraph_LayerTopoSupplement::AttachmentKind::VertexSupplementShape); + + //! @brief Attach a supplemental shape to an edge owner. + //! @param[in] theEdge active edge owner + //! @param[in] theShape supplemental shape to attach + //! @param[in] theKind semantic attachment kind + //! @return non-zero attachment uid on success, `0` on rejection + [[nodiscard]] Standard_EXPORT uint64_t + AttachToEdge(BRepGraph_EdgeId theEdge, + const TopoDS_Shape& theShape, + BRepGraph_LayerTopoSupplement::AttachmentKind theKind = + BRepGraph_LayerTopoSupplement::AttachmentKind::EdgeInternalVertex); + + //! @brief Attach a supplemental shape to a face owner. + //! @param[in] theFace active face owner + //! @param[in] theShape supplemental shape to attach + //! @param[in] theKind semantic attachment kind + //! @return non-zero attachment uid on success, `0` on rejection + [[nodiscard]] Standard_EXPORT uint64_t + AttachToFace(BRepGraph_FaceId theFace, + const TopoDS_Shape& theShape, + BRepGraph_LayerTopoSupplement::AttachmentKind theKind = + BRepGraph_LayerTopoSupplement::AttachmentKind::FaceDirectVertex); + + //! @brief Attach a supplemental shape to a solid owner. + //! @param[in] theSolid active solid owner + //! @param[in] theShape supplemental shape to attach + //! @param[in] theKind semantic attachment kind + //! @return non-zero attachment uid on success, `0` on rejection + [[nodiscard]] Standard_EXPORT uint64_t + AttachToSolid(BRepGraph_SolidId theSolid, + const TopoDS_Shape& theShape, + BRepGraph_LayerTopoSupplement::AttachmentKind theKind = + BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape); + + //! @brief Attach a supplemental shape to a compsolid owner. + //! @param[in] theCompSolid active compsolid owner + //! @param[in] theShape supplemental shape to attach + //! @param[in] theKind semantic attachment kind + //! @return non-zero attachment uid on success, `0` on rejection + [[nodiscard]] Standard_EXPORT uint64_t + AttachToCompSolid(BRepGraph_CompSolidId theCompSolid, + const TopoDS_Shape& theShape, + BRepGraph_LayerTopoSupplement::AttachmentKind theKind = + BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape); + + //! @brief Attach a supplemental shape to a shell owner. + //! @param[in] theShell active shell owner + //! @param[in] theShape supplemental shape to attach + //! @param[in] theKind semantic attachment kind + //! @return non-zero attachment uid on success, `0` on rejection + [[nodiscard]] Standard_EXPORT uint64_t + AttachToShell(BRepGraph_ShellId theShell, + const TopoDS_Shape& theShape, + BRepGraph_LayerTopoSupplement::AttachmentKind theKind = + BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape); + + //! @brief Attach a supplemental shape to a compound owner. + //! @param[in] theCompound active compound owner + //! @param[in] theShape supplemental shape to attach + //! @param[in] theKind semantic attachment kind + //! @return non-zero attachment uid on success, `0` on rejection + [[nodiscard]] Standard_EXPORT uint64_t + AttachToCompound(BRepGraph_CompoundId theCompound, + const TopoDS_Shape& theShape, + BRepGraph_LayerTopoSupplement::AttachmentKind theKind = + BRepGraph_LayerTopoSupplement::AttachmentKind::CompoundAuxShape); + + //! @brief Remove one attachment by uid. + //! @param[in] theUid layer-local attachment uid + //! @return `true` when the attachment existed and was removed + Standard_EXPORT bool RemoveAttachment(uint64_t theUid); + +private: + BRepGraph& myGraph; +}; + +#endif // _BRepGraph_SupplementEditor_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementIterator.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementIterator.cxx new file mode 100644 index 0000000000..28da273fb5 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementIterator.cxx @@ -0,0 +1,37 @@ +// 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 + +//================================================================================================= + +void BRepGraph_SupplementIterator::skipInvalid() +{ + myEntry = nullptr; + if (myLayer.IsNull() || myUids == nullptr) + { + return; + } + + while (myIndex < myUids->Size()) + { + const BRepGraph_LayerTopoSupplement::Entry* anEntry = + myLayer->FindByUid(myUids->Value(myIndex)); + if (anEntry != nullptr) + { + myEntry = anEntry; + return; + } + ++myIndex; + } +} \ No newline at end of file diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementIterator.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementIterator.hxx new file mode 100644 index 0000000000..96d5b9785a --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_SupplementIterator.hxx @@ -0,0 +1,79 @@ +// 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. + +#ifndef _BRepGraph_SupplementIterator_HeaderFile +#define _BRepGraph_SupplementIterator_HeaderFile + +#include +#include +#include +#include + +//! @brief Iterator over supplemental TopoDS attachments owned by one core node. +//! +//! The iterator resolves `BRepGraph_LayerTopoSupplement` through the graph layer +//! registry and yields only explicit supplement attachments. Core traversal +//! remains core-only and does not surface these entries. +class BRepGraph_SupplementIterator +{ +public: + //! @brief Construct an iterator over supplement attachments of one owner. + //! @param[in] theGraph graph providing the supplement layer + //! @param[in] theOwner core owner node whose attachments should be iterated + explicit BRepGraph_SupplementIterator(const BRepGraph& theGraph, const BRepGraph_NodeId theOwner) + : myLayer(theGraph.LayerRegistry().FindLayer()), + myUids(myLayer.IsNull() ? nullptr : &myLayer->AttachedTo(theOwner)) + { + skipInvalid(); + } + + //! @brief Return true when the iterator currently points to an attachment. + [[nodiscard]] bool More() const + { + return myUids != nullptr && myEntry != nullptr && myIndex < myUids->Size(); + } + + //! @brief Advance to the next attachment. + void Next() + { + ++myIndex; + skipInvalid(); + } + + //! @brief Return the current layer-local attachment uid. + [[nodiscard]] uint64_t Uid() const { return More() ? myUids->Value(myIndex) : uint64_t(0); } + + //! @brief Return the current attachment entry. + [[nodiscard]] const BRepGraph_LayerTopoSupplement::Entry& Value() const { return *myEntry; } + + //! @brief STL range-for support. + NCollection_ForwardRangeIterator begin() + { + return NCollection_ForwardRangeIterator(this); + } + + //! @brief Sentinel marking end of iteration. + NCollection_ForwardRangeSentinel end() const { return NCollection_ForwardRangeSentinel{}; } + +private: + //! @brief Skip missing entries in owner-local uid storage. + Standard_EXPORT void skipInvalid(); + +private: + occ::handle myLayer; + const NCollection_LinearVector* myUids = nullptr; + const BRepGraph_LayerTopoSupplement::Entry* myEntry = nullptr; + size_t myIndex = 0; +}; + +#endif // _BRepGraph_SupplementIterator_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.cxx index 42b5c77460..387446834b 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.cxx @@ -14,16 +14,14 @@ #include #include -#include +#include +#include +#include "BRepGraph_CacheDerivedState.hxx" +#include "BRepGraph_CacheRegistry.hxx" #include -#include -#include -#include #include #include #include -#include -#include #include #include #include @@ -32,8 +30,190 @@ #include #include #include +#include #include -#include +#include + +namespace +{ +template +occ::handle findOrCreateCache(BRepGraph& theGraph) +{ + return theGraph.CacheRegistry().Ensure(); +} + +template +occ::handle findOrCreateCache(const BRepGraph& theGraph) +{ + return const_cast(theGraph).CacheRegistry().Ensure(); +} + +bool addWireUVBounds(const BRepGraph& theGraph, const BRepGraph_WireId theWire, Bnd_Box2d& theBox) +{ + for (BRepGraph_CoEdgesOfWire aCoEdgeIt(theGraph, theWire); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + const Geom2dAdaptor_Curve aPCurve = + BRepGraph_Tool::CoEdge::PCurveAdaptor(theGraph, aCoEdgeIt.CurrentId()); + if (aPCurve.IsInitialized()) + { + BndLib_Add2dCurve::Add(aPCurve, Precision::Confusion(), theBox); + } + } + return !theBox.IsVoid(); +} + +bool wireUVBounds(const BRepGraph& theGraph, + const BRepGraph_WireId theWire, + double& theUMin, + double& theUMax, + double& theVMin, + double& theVMax) +{ + Bnd_Box2d aBox; + if (!addWireUVBounds(theGraph, theWire, aBox)) + { + theUMin = theUMax = theVMin = theVMax = 0.0; + return false; + } + + aBox.Get(theUMin, theVMin, theUMax, theVMax); + return true; +} + +BRepGraph_EdgeId edgeOf(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) +{ + return theCoEdge.IsValid() && !theCoEdge.IsRemoved(theGraph) + ? theGraph.Topo().CoEdges().Definition(theCoEdge).ChildEdgeId + : BRepGraph_EdgeId(); +} + +BRepGraph_FaceId faceOf(const BRepGraph& theGraph, const BRepGraph_FaceRefId theFaceRef) +{ + if (!theFaceRef.IsValid() || theGraph.Refs().Gen().IsRemoved(theFaceRef)) + { + return BRepGraph_FaceId(); + } + const BRepGraph_FaceId aFace = theGraph.Refs().Faces().Entry(theFaceRef).ChildFaceId; + return aFace.IsValid() && !aFace.IsRemoved(theGraph) ? aFace : BRepGraph_FaceId(); +} + +BRepGraph_WireId wireOf(const BRepGraph& theGraph, const BRepGraph_WireRefId theWireRef) +{ + if (!theWireRef.IsValid() || theGraph.Refs().Gen().IsRemoved(theWireRef)) + { + return BRepGraph_WireId(); + } + const BRepGraph_WireId aWire = theGraph.Refs().Wires().Entry(theWireRef).ChildWireId; + return aWire.IsValid() && !aWire.IsRemoved(theGraph) ? aWire : BRepGraph_WireId(); +} + +BRepGraph_ShellId shellOf(const BRepGraph& theGraph, const BRepGraph_ShellRefId theShellRef) +{ + if (!theShellRef.IsValid() || theGraph.Refs().Gen().IsRemoved(theShellRef)) + { + return BRepGraph_ShellId(); + } + const BRepGraph_ShellId aShell = theGraph.Refs().Shells().Entry(theShellRef).ChildShellId; + return aShell.IsValid() && !aShell.IsRemoved(theGraph) ? aShell : BRepGraph_ShellId(); +} + +occ::handle derivedStateCache(const BRepGraph& theGraph) +{ + return const_cast(theGraph).CacheRegistry().Ensure(); +} + +enum class CoEdgeLookupContent +{ + Any, + WithPCurve +}; + +bool matchesLookupContent(const BRepGraph& theGraph, + const BRepGraphInc::CoEdgeDef& theCoEdge, + const CoEdgeLookupContent theContent) +{ + return theContent == CoEdgeLookupContent::Any + || (theCoEdge.Curve2DRepId.IsValid(theGraph.Topo().Geometry().NbCoEdgeCurves2D()) + && !theCoEdge.Curve2DRepId.IsRemoved(theGraph)); +} + +BRepGraph_CoEdgeId findCoEdgeId(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace, + const CoEdgeLookupContent theContent) +{ + if (!theEdge.IsValid(theGraph.Topo().Edges().Nb()) + || !theFace.IsValid(theGraph.Topo().Faces().Nb())) + { + return BRepGraph_CoEdgeId(); + } + + for (BRepGraph_CoEdgesOfEdge aCoEdgeIt(theGraph, theGraph.Topo().Edges().CoEdges(theEdge)); + aCoEdgeIt.More(); + aCoEdgeIt.Next()) + { + const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Definition(); + if (aCoEdge.ChildEdgeId == theEdge && aCoEdge.FaceId == theFace + && matchesLookupContent(theGraph, aCoEdge, theContent)) + { + return aCoEdgeIt.CurrentId(); + } + } + return BRepGraph_CoEdgeId(); +} + +BRepGraph_CoEdgeId findCoEdgeId(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace, + const TopAbs_Orientation theOrientation, + const CoEdgeLookupContent theContent) +{ + if (!theEdge.IsValid(theGraph.Topo().Edges().Nb()) + || !theFace.IsValid(theGraph.Topo().Faces().Nb())) + { + return BRepGraph_CoEdgeId(); + } + + BRepGraph_CoEdgeId aFirstMatch; + for (BRepGraph_CoEdgesOfEdge aCoEdgeIt(theGraph, theGraph.Topo().Edges().CoEdges(theEdge)); + aCoEdgeIt.More(); + aCoEdgeIt.Next()) + { + const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Definition(); + if (aCoEdge.ChildEdgeId != theEdge || aCoEdge.FaceId != theFace + || !matchesLookupContent(theGraph, aCoEdge, theContent)) + { + continue; + } + if (!aFirstMatch.IsValid()) + { + aFirstMatch = aCoEdgeIt.CurrentId(); + } + if (aCoEdge.Orientation == theOrientation) + { + return aCoEdgeIt.CurrentId(); + } + } + return aFirstMatch; +} +} // namespace + +BRepGraph_Tool::VertexUsage BRepGraph_Tool::Vertex::Usage(const BRepGraph& theGraph, + const BRepGraph_VertexRefId theVertexRef) +{ + if (!theVertexRef.IsValid() || theGraph.Refs().Gen().IsRemoved(theVertexRef)) + { + return VertexUsage(); + } + const BRepGraphInc::VertexRef& aRef = theGraph.Refs().Vertices().Entry(theVertexRef); + if (!aRef.ChildVertexId.IsValid() || aRef.ChildVertexId.IsRemoved(theGraph)) + { + return VertexUsage(); + } + return VertexUsage{aRef.ChildVertexId, TopLoc_Location(), aRef.Orientation}; +} + +//================================================================================================= gp_Pnt BRepGraph_Tool::Vertex::Pnt(const BRepGraph& theGraph, const BRepGraphInc::VertexInstance& theRef) @@ -55,6 +235,15 @@ gp_Pnt BRepGraph_Tool::Vertex::Pnt(const BRepGraph& theGraph, const BRepGraph_Ve //================================================================================================= +gp_Pnt BRepGraph_Tool::Vertex::Pnt(const BRepGraph& theGraph, + const BRepGraph_VertexRefId theVertexRef) +{ + const VertexUsage aUsage = Usage(theGraph, theVertexRef); + return aUsage.IsValid() ? Pnt(theGraph, aUsage) : gp_Pnt(); +} + +//================================================================================================= + double BRepGraph_Tool::Vertex::Tolerance(const BRepGraph& theGraph, const BRepGraph_VertexId theVertex) { @@ -63,34 +252,15 @@ double BRepGraph_Tool::Vertex::Tolerance(const BRepGraph& theGraph, //================================================================================================= -double BRepGraph_Tool::Vertex::Parameter(const BRepGraph& theGraph, - const BRepGraph_VertexId theVertex, - const BRepGraph_EdgeId theEdge) +double BRepGraph_Tool::Vertex::Tolerance(const BRepGraph& theGraph, + const BRepGraph_VertexRefId theVertexRef) { - double aParameter = 0.0; - const occ::handle aParamLayer = - theGraph.LayerRegistry().FindLayer(); - if (!aParamLayer.IsNull() && aParamLayer->FindPointOnCurve(theVertex, theEdge, &aParameter)) + if (!theVertexRef.IsValid() || theGraph.Refs().Gen().IsRemoved(theVertexRef)) { - return aParameter; + return 0.0; } - throw Standard_NoSuchObject("BRepGraph_Tool::Parameter - no PointOnCurve for this edge"); -} - -//================================================================================================= - -gp_Pnt2d BRepGraph_Tool::Vertex::Parameters(const BRepGraph& theGraph, - const BRepGraph_VertexId theVertex, - const BRepGraph_FaceId theFace) -{ - gp_Pnt2d aUV; - const occ::handle aParamLayer = - theGraph.LayerRegistry().FindLayer(); - if (!aParamLayer.IsNull() && aParamLayer->FindPointOnSurface(theVertex, theFace, &aUV)) - { - return aUV; - } - throw Standard_NoSuchObject("BRepGraph_Tool::Parameters - no PointOnSurface for this face"); + const BRepGraph_VertexId aVertex = theGraph.Refs().Vertices().Entry(theVertexRef).ChildVertexId; + return aVertex.IsValid() && !aVertex.IsRemoved(theGraph) ? Tolerance(theGraph, aVertex) : 0.0; } //================================================================================================= @@ -104,21 +274,76 @@ double BRepGraph_Tool::Edge::Tolerance(const BRepGraph& theGraph, const BRepGrap bool BRepGraph_Tool::Edge::Degenerated(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) { - return theGraph.Topo().Edges().Definition(theEdge).IsDegenerate; + BRepGraph_CacheDerivedState::EdgeEntry anEntry; + return derivedStateCache(theGraph)->GetEdgeStatus(theEdge, anEntry) + && anEntry.Status == BRepGraph_CacheDerivedState::EdgeGeometryStatus::DegenerateOnSurface; +} + +//================================================================================================= + +double BRepGraph_Tool::Edge::Tolerance(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() ? Tolerance(theGraph, anEdge) : 0.0; +} + +//================================================================================================= + +bool BRepGraph_Tool::Edge::Degenerated(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() && Degenerated(theGraph, anEdge); } //================================================================================================= bool BRepGraph_Tool::Edge::SameParameter(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) { - return theGraph.Topo().Edges().Definition(theEdge).SameParameter; + BRepGraph_CacheDerivedState::EdgeEntry anEntry; + return derivedStateCache(theGraph)->GetEdgeStatus(theEdge, anEntry) && anEntry.SameParameter; +} + +//================================================================================================= + +bool BRepGraph_Tool::Edge::SameParameter(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() && SameParameter(theGraph, anEdge); } //================================================================================================= bool BRepGraph_Tool::Edge::SameRange(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) { - return theGraph.Topo().Edges().Definition(theEdge).SameRange; + BRepGraph_CacheDerivedState::EdgeEntry anEntry; + return derivedStateCache(theGraph)->GetEdgeStatus(theEdge, anEntry) && anEntry.SameRange; +} + +//================================================================================================= + +bool BRepGraph_Tool::Edge::SameRange(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() && SameRange(theGraph, anEdge); +} + +//================================================================================================= + +bool BRepGraph_Tool::Edge::IsClosed(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) +{ + BRepGraph_CacheDerivedState::EdgeEntry anEntry; + return derivedStateCache(theGraph)->GetEdgeStatus(theEdge, anEntry) && anEntry.IsClosed; +} + +//================================================================================================= + +bool BRepGraph_Tool::Edge::IsClosed(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() && IsClosed(theGraph, anEdge); } //================================================================================================= @@ -126,85 +351,86 @@ bool BRepGraph_Tool::Edge::SameRange(const BRepGraph& theGraph, const BRepGraph_ std::pair BRepGraph_Tool::Edge::Range(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) { - const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(theEdge); - return {anEdge.ParamFirst, anEdge.ParamLast}; + const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(theEdge); + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + double aFirst = 0.0, aLast = 0.0; + if (anEdge.Curve3DRepId.IsValid() && anEdge.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D()) + && !aStorage.IsRemoved(anEdge.Curve3DRepId)) + { + const BRepGraphInc::EdgeCurve3DRep& aUse = aStorage.EdgeCurve3DRep(anEdge.Curve3DRepId); + aFirst = aUse.ParamFirst; + aLast = aUse.ParamLast; + } + return {aFirst, aLast}; +} + +//================================================================================================= + +std::pair BRepGraph_Tool::Edge::Range(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() ? Range(theGraph, anEdge) : std::pair{0.0, 0.0}; } //================================================================================================= bool BRepGraph_Tool::Edge::HasCurve(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) { - const BRepGraph_Curve3DRepId aRepId = theGraph.Topo().Edges().Curve3DRepId(theEdge); - if (!aRepId.IsValid()) + const occ::handle aCurve = theGraph.Topo().Edges().Curve3D(theEdge); + return !aCurve.IsNull(); +} + +//================================================================================================= + +BRepGraph_VertexRefId BRepGraph_Tool::Edge::StartVertexId(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge) +{ + return theGraph.Topo().Edges().Definition(theEdge).StartVertexRefId; +} + +//================================================================================================= + +BRepGraph_VertexRefId BRepGraph_Tool::Edge::EndVertexId(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge) +{ + return theGraph.Topo().Edges().Definition(theEdge).EndVertexRefId; +} + +//================================================================================================= + +BRepGraph_VertexRefId BRepGraph_Tool::Edge::StartVertexId(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + if (!theCoEdge.IsValid() || theCoEdge.IsRemoved(theGraph)) { - return false; + return BRepGraph_VertexRefId(); } - - const BRepGraphInc::Curve3DRep& aCurveRep = theGraph.Topo().Geometry().Curve3DRep(aRepId); - return !aCurveRep.IsRemoved && !aCurveRep.Curve.IsNull(); + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); + return aCoEdge.Orientation == TopAbs_REVERSED ? EndVertexId(theGraph, aCoEdge.ChildEdgeId) + : StartVertexId(theGraph, aCoEdge.ChildEdgeId); } //================================================================================================= -bool BRepGraph_Tool::Edge::HasPolygon3D(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) +BRepGraph_VertexRefId BRepGraph_Tool::Edge::EndVertexId(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) { - // Cache-first, persistent fallback via MeshCacheOps. - return theGraph.Mesh().Edges().HasPolygon3D(theEdge); -} - -//================================================================================================= - -namespace -{ -const BRepGraphInc::VertexRef& invalidVertexRef() -{ - static const BRepGraphInc::VertexRef THE_INVALID_VERTEX_REF; - return THE_INVALID_VERTEX_REF; -} -} // namespace - -//================================================================================================= - -const BRepGraphInc::VertexRef& BRepGraph_Tool::Edge::StartVertexRef(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge) -{ - const BRepGraph_VertexRefId aRefId = theGraph.Topo().Edges().Definition(theEdge).StartVertexRefId; - return aRefId.IsValid() ? theGraph.Refs().Vertices().Entry(aRefId) : invalidVertexRef(); -} - -//================================================================================================= - -const BRepGraphInc::VertexRef& BRepGraph_Tool::Edge::EndVertexRef(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge) -{ - const BRepGraph_VertexRefId aRefId = theGraph.Topo().Edges().Definition(theEdge).EndVertexRefId; - return aRefId.IsValid() ? theGraph.Refs().Vertices().Entry(aRefId) : invalidVertexRef(); -} - -//================================================================================================= - -BRepGraph_VertexId BRepGraph_Tool::Edge::StartVertexId(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge) -{ - const BRepGraph_VertexRefId aRefId = theGraph.Topo().Edges().Definition(theEdge).StartVertexRefId; - if (!aRefId.IsValid()) + if (!theCoEdge.IsValid() || theCoEdge.IsRemoved(theGraph)) { - return BRepGraph_VertexId(); + return BRepGraph_VertexRefId(); } - return theGraph.Refs().Vertices().Entry(aRefId).VertexDefId; + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); + return aCoEdge.Orientation == TopAbs_REVERSED ? StartVertexId(theGraph, aCoEdge.ChildEdgeId) + : EndVertexId(theGraph, aCoEdge.ChildEdgeId); } //================================================================================================= -BRepGraph_VertexId BRepGraph_Tool::Edge::EndVertexId(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge) +bool BRepGraph_Tool::Edge::HasCurve(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) { - const BRepGraph_VertexRefId aRefId = theGraph.Topo().Edges().Definition(theEdge).EndVertexRefId; - if (!aRefId.IsValid()) - { - return BRepGraph_VertexId(); - } - return theGraph.Refs().Vertices().Entry(aRefId).VertexDefId; + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() && HasCurve(theGraph, anEdge); } //================================================================================================= @@ -214,48 +440,46 @@ GeomAdaptor_TransformedCurve BRepGraph_Tool::Edge::CurveAdaptor( const BRepGraphInc::CoEdgeInstance& theRef) { const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theRef.DefId); - const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(aCoEdge.EdgeDefId); + const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(aCoEdge.ChildEdgeId); const gp_Trsf aTrsf = theRef.Location.IsIdentity() ? gp_Trsf() : theRef.Location.Transformation(); // Prefer 3D curve when available. - if (anEdge.Curve3DRepId.IsValid()) + if (anEdge.Curve3DRepId.IsValid() + && anEdge.Curve3DRepId.IsValid(theGraph.incStorage().NbEdgeCurves3D()) + && !theGraph.incStorage().IsRemoved(anEdge.Curve3DRepId)) { - const BRepGraphInc::Curve3DRep& aCurveRep = - theGraph.Topo().Geometry().Curve3DRep(anEdge.Curve3DRepId); - if (aCurveRep.IsRemoved) - { - return GeomAdaptor_TransformedCurve(); - } + const BRepGraphInc::EdgeCurve3DRep& aCurveRep = + theGraph.incStorage().EdgeCurve3DRep(anEdge.Curve3DRepId); const occ::handle& aCurve = aCurveRep.Curve; if (!aCurve.IsNull()) { - return GeomAdaptor_TransformedCurve(aCurve, anEdge.ParamFirst, anEdge.ParamLast, aTrsf); + return GeomAdaptor_TransformedCurve(aCurve, aCurveRep.ParamFirst, aCurveRep.ParamLast, aTrsf); } } // Fallback: CurveOnSurface from PCurve + surface. - if (aCoEdge.Curve2DRepId.IsValid() && aCoEdge.FaceDefId.IsValid()) + if (aCoEdge.Curve2DRepId.IsValid() + && aCoEdge.Curve2DRepId.IsValid(theGraph.incStorage().NbCoEdgeCurves2D()) + && !theGraph.incStorage().IsRemoved(aCoEdge.Curve2DRepId) && aCoEdge.FaceId.IsValid()) { const BRepGraphInc::FaceDef& aFace = - theGraph.Topo().Faces().Definition(BRepGraph_FaceId(aCoEdge.FaceDefId)); - if (aFace.SurfaceRepId.IsValid()) + theGraph.Topo().Faces().Definition(BRepGraph_FaceId(aCoEdge.FaceId)); + if (aFace.SurfaceRepId.IsValid() + && aFace.SurfaceRepId.IsValid(theGraph.incStorage().NbFaceSurfaces()) + && !theGraph.incStorage().IsRemoved(aFace.SurfaceRepId)) { - const BRepGraphInc::Curve2DRep& aPCurveRep = - theGraph.Topo().Geometry().Curve2DRep(aCoEdge.Curve2DRepId); - const BRepGraphInc::SurfaceRep& aSurfaceRep = - theGraph.Topo().Geometry().SurfaceRep(aFace.SurfaceRepId); - if (aPCurveRep.IsRemoved || aSurfaceRep.IsRemoved) - { - return GeomAdaptor_TransformedCurve(); - } - const occ::handle& aPCurve = aPCurveRep.Curve; + const BRepGraphInc::CoEdgeCurve2DRep& aPCurveUse = + theGraph.incStorage().CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + const BRepGraphInc::FaceSurfaceRep& aSurfaceRep = + theGraph.incStorage().FaceSurfaceRep(aFace.SurfaceRepId); + const occ::handle& aPCurve = aPCurveUse.Curve; const occ::handle& aSurf = aSurfaceRep.Surface; if (!aPCurve.IsNull() && !aSurf.IsNull()) { GeomAdaptor_TransformedCurve aResult; aResult.SetTrsf(aTrsf); occ::handle aHC2d = - new Geom2dAdaptor_Curve(aPCurve, aCoEdge.ParamFirst, aCoEdge.ParamLast); + new Geom2dAdaptor_Curve(aPCurve, aPCurveUse.ParamFirst, aPCurveUse.ParamLast); occ::handle aHS = new GeomAdaptor_Surface(aSurf); aResult.LoadCurveOnSurface(new Adaptor3d_CurveOnSurface(aHC2d, aHS)); return aResult; @@ -272,22 +496,20 @@ GeomAdaptor_TransformedCurve BRepGraph_Tool::Edge::CurveAdaptor(const BRepGraph& const BRepGraph_EdgeId theEdge) { const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(theEdge); - if (!anEdge.Curve3DRepId.IsValid()) - { - return GeomAdaptor_TransformedCurve(); - } - const BRepGraphInc::Curve3DRep& aCurveRep = - theGraph.Topo().Geometry().Curve3DRep(anEdge.Curve3DRepId); - if (aCurveRep.IsRemoved) + if (!anEdge.Curve3DRepId.IsValid() + || !anEdge.Curve3DRepId.IsValid(theGraph.incStorage().NbEdgeCurves3D()) + || theGraph.incStorage().IsRemoved(anEdge.Curve3DRepId)) { return GeomAdaptor_TransformedCurve(); } + const BRepGraphInc::EdgeCurve3DRep& aCurveRep = + theGraph.incStorage().EdgeCurve3DRep(anEdge.Curve3DRepId); const occ::handle& aCurve = aCurveRep.Curve; if (aCurve.IsNull()) { return GeomAdaptor_TransformedCurve(); } - return GeomAdaptor_TransformedCurve(aCurve, anEdge.ParamFirst, anEdge.ParamLast, gp_Trsf()); + return GeomAdaptor_TransformedCurve(aCurve, aCurveRep.ParamFirst, aCurveRep.ParamLast, gp_Trsf()); } //================================================================================================= @@ -297,12 +519,32 @@ static const occ::handle THE_NULL_CURVE; const occ::handle& BRepGraph_Tool::Edge::Curve(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) { - const BRepGraph_Curve3DRepId aRepId = theGraph.Topo().Edges().Curve3DRepId(theEdge); - if (!aRepId.IsValid()) + const BRepGraph_EdgeCurve3DRepId aRepId = + theGraph.Topo().Edges().Definition(theEdge).Curve3DRepId; + if (!aRepId.IsValid(theGraph.incStorage().NbEdgeCurves3D()) + || theGraph.incStorage().IsRemoved(aRepId)) { return THE_NULL_CURVE; } - return theGraph.Topo().Geometry().Curve3DRep(aRepId).Curve; + return theGraph.incStorage().EdgeCurve3DRep(aRepId).Curve; +} + +//================================================================================================= + +const occ::handle& BRepGraph_Tool::Edge::Curve(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() ? Curve(theGraph, anEdge) : THE_NULL_CURVE; +} + +//================================================================================================= + +GeomAdaptor_TransformedCurve BRepGraph_Tool::Edge::CurveAdaptor(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() ? CurveAdaptor(theGraph, anEdge) : GeomAdaptor_TransformedCurve(); } //================================================================================================= @@ -311,17 +553,15 @@ occ::handle BRepGraph_Tool::Edge::Curve(const BRepGraph& const BRepGraphInc::CoEdgeInstance& theRef) { const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theRef.DefId); - const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(aCoEdge.EdgeDefId); - if (!anEdge.Curve3DRepId.IsValid()) - { - return occ::handle(); - } - const BRepGraphInc::Curve3DRep& aCurveRep = - theGraph.Topo().Geometry().Curve3DRep(anEdge.Curve3DRepId); - if (aCurveRep.IsRemoved) + const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(aCoEdge.ChildEdgeId); + if (!anEdge.Curve3DRepId.IsValid() + || !anEdge.Curve3DRepId.IsValid(theGraph.incStorage().NbEdgeCurves3D()) + || theGraph.incStorage().IsRemoved(anEdge.Curve3DRepId)) { return occ::handle(); } + const BRepGraphInc::EdgeCurve3DRep& aCurveRep = + theGraph.incStorage().EdgeCurve3DRep(anEdge.Curve3DRepId); const occ::handle& aCurve = aCurveRep.Curve; if (aCurve.IsNull() || theRef.Location.IsIdentity()) { @@ -332,38 +572,6 @@ occ::handle BRepGraph_Tool::Edge::Curve(const BRepGraph& //================================================================================================= -static const occ::handle THE_NULL_POLYGON3D; - -const occ::handle& BRepGraph_Tool::Edge::Polygon3D(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge) -{ - // Cache-first, persistent fallback via MeshCacheOps. - const BRepGraph_Polygon3DRepId aRepId = theGraph.Mesh().Edges().Polygon3DRepId(theEdge); - if (!aRepId.IsValid()) - { - return THE_NULL_POLYGON3D; - } - return theGraph.Mesh().Poly().Polygon3DRep(aRepId).Polygon; -} - -//================================================================================================= - -double BRepGraph_Tool::Vertex::PCurveParameter(const BRepGraph& theGraph, - const BRepGraph_VertexId theVertex, - const BRepGraph_CoEdgeId theCoEdge) -{ - double aParameter = 0.0; - const occ::handle aParamLayer = - theGraph.LayerRegistry().FindLayer(); - if (!aParamLayer.IsNull() && aParamLayer->FindPointOnPCurve(theVertex, theCoEdge, &aParameter)) - { - return aParameter; - } - throw Standard_NoSuchObject("BRepGraph_Tool::PCurveParameter - no PointOnPCurve for this coedge"); -} - -//================================================================================================= - uint32_t BRepGraph_Tool::Vertex::NbEdges(const BRepGraph& theGraph, const BRepGraph_VertexId theVertex) { @@ -372,43 +580,86 @@ uint32_t BRepGraph_Tool::Vertex::NbEdges(const BRepGraph& theGraph, //================================================================================================= -bool BRepGraph_Tool::Edge::HasContinuity(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2) +BRepGraph_EdgeId BRepGraph_Tool::Edge::FindByVertices(const BRepGraph& theGraph, + const BRepGraph_VertexId theStartVertex, + const BRepGraph_VertexId theEndVertex, + const bool theToIgnoreOrientation) { - const occ::handle aRegularityLayer = - theGraph.LayerRegistry().FindLayer(); - return !aRegularityLayer.IsNull() - && aRegularityLayer->FindContinuity(theEdge, theFace1, theFace2, nullptr); -} - -//================================================================================================= - -GeomAbs_Shape BRepGraph_Tool::Edge::Continuity(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2) -{ - GeomAbs_Shape aContinuity = GeomAbs_C0; - const occ::handle aRegularityLayer = - theGraph.LayerRegistry().FindLayer(); - if (!aRegularityLayer.IsNull() - && aRegularityLayer->FindContinuity(theEdge, theFace1, theFace2, &aContinuity)) + if (!theStartVertex.IsValid(theGraph.Topo().Vertices().Nb()) + || !theEndVertex.IsValid(theGraph.Topo().Vertices().Nb()) + || theStartVertex.IsRemoved(theGraph) || theEndVertex.IsRemoved(theGraph)) { - return aContinuity; + return BRepGraph_EdgeId(); } - return GeomAbs_C0; + + for (const BRepGraph_EdgeId& anEdgeId : theGraph.Topo().Vertices().Edges(theStartVertex)) + { + if (!anEdgeId.IsValid(theGraph.Topo().Edges().Nb()) || anEdgeId.IsRemoved(theGraph)) + { + continue; + } + + const BRepGraphInc::EdgeDef& anEdge = theGraph.Topo().Edges().Definition(anEdgeId); + if (!anEdge.StartVertexRefId.IsValid(theGraph.Refs().Vertices().Nb()) + || !anEdge.EndVertexRefId.IsValid(theGraph.Refs().Vertices().Nb()) + || anEdge.StartVertexRefId.IsRemoved(theGraph) || anEdge.EndVertexRefId.IsRemoved(theGraph)) + { + continue; + } + + const BRepGraph_VertexId aStart = + theGraph.Refs().Vertices().Entry(anEdge.StartVertexRefId).ChildVertexId; + const BRepGraph_VertexId anEnd = + theGraph.Refs().Vertices().Entry(anEdge.EndVertexRefId).ChildVertexId; + if (aStart == theStartVertex && anEnd == theEndVertex) + { + return anEdgeId; + } + if (theToIgnoreOrientation && aStart == theEndVertex && anEnd == theStartVertex) + { + return anEdgeId; + } + } + + return BRepGraph_EdgeId(); } //================================================================================================= -GeomAbs_Shape BRepGraph_Tool::Edge::MaxContinuity(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge) +BRepGraph_CoEdgeId BRepGraph_Tool::Edge::FindPCurveCoEdgeId(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace) { - const occ::handle aRegularityLayer = - theGraph.LayerRegistry().FindLayer(); - return !aRegularityLayer.IsNull() ? aRegularityLayer->MaxContinuity(theEdge) : GeomAbs_C0; + return findCoEdgeId(theGraph, theEdge, theFace, CoEdgeLookupContent::WithPCurve); +} + +//================================================================================================= + +BRepGraph_CoEdgeId BRepGraph_Tool::Edge::FindPCurveCoEdgeId(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace, + const TopAbs_Orientation theOrientation) +{ + return findCoEdgeId(theGraph, theEdge, theFace, theOrientation, CoEdgeLookupContent::WithPCurve); +} + +//================================================================================================= + +BRepGraph_CoEdgeId BRepGraph_Tool::Edge::FindCoEdgeId(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace) +{ + return findCoEdgeId(theGraph, theEdge, theFace, CoEdgeLookupContent::Any); +} + +//================================================================================================= + +BRepGraph_CoEdgeId BRepGraph_Tool::Edge::FindCoEdgeId(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace, + const TopAbs_Orientation theOrientation) +{ + return findCoEdgeId(theGraph, theEdge, theFace, theOrientation, CoEdgeLookupContent::Any); } //================================================================================================= @@ -420,16 +671,41 @@ uint32_t BRepGraph_Tool::Edge::NbFaces(const BRepGraph& theGraph, const BRepGrap //================================================================================================= +uint32_t BRepGraph_Tool::Edge::NbFaces(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() ? NbFaces(theGraph, anEdge) : 0; +} + +//================================================================================================= + bool BRepGraph_Tool::Edge::IsManifold(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) { - return theGraph.Topo().Edges().IsManifold(theEdge); + return NbFaces(theGraph, theEdge) == 2; +} + +//================================================================================================= + +bool BRepGraph_Tool::Edge::IsManifold(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() && IsManifold(theGraph, anEdge); } //================================================================================================= bool BRepGraph_Tool::Edge::IsBoundary(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) { - return theGraph.Topo().Edges().IsBoundary(theEdge); + return NbFaces(theGraph, theEdge) == 1; +} + +//================================================================================================= + +bool BRepGraph_Tool::Edge::IsBoundary(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) +{ + const BRepGraph_EdgeId anEdge = edgeOf(theGraph, theCoEdge); + return anEdge.IsValid() && IsBoundary(theGraph, anEdge); } //================================================================================================= @@ -453,7 +729,7 @@ bool BRepGraph_Tool::CoEdge::IsReversed(const BRepGraph& theGraph, BRepGraph_EdgeId BRepGraph_Tool::CoEdge::EdgeOf(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) { - return theGraph.Topo().CoEdges().Definition(theCoEdge).EdgeDefId; + return theGraph.Topo().CoEdges().Definition(theCoEdge).ChildEdgeId; } //================================================================================================= @@ -461,7 +737,7 @@ BRepGraph_EdgeId BRepGraph_Tool::CoEdge::EdgeOf(const BRepGraph& theGrap BRepGraph_FaceId BRepGraph_Tool::CoEdge::FaceOf(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) { - return theGraph.Topo().CoEdges().Definition(theCoEdge).FaceDefId; + return theGraph.Topo().CoEdges().Definition(theCoEdge).FaceId; } //================================================================================================= @@ -471,19 +747,21 @@ BRepGraph_CoEdgeId BRepGraph_Tool::CoEdge::SeamPair(const BRepGraph& the { // The seam mate is the sibling CoEdge on the same face with opposite orientation. const BRepGraphInc::CoEdgeDef& aDef = theGraph.Topo().CoEdges().Definition(theCoEdge); - if (!aDef.EdgeDefId.IsValid() || !aDef.FaceDefId.IsValid()) + if (!aDef.ChildEdgeId.IsValid() || !aDef.FaceId.IsValid()) { return {}; } - for (BRepGraph_CoEdgesOfEdge anIt(theGraph, theGraph.Topo().Edges().CoEdges(aDef.EdgeDefId)); + for (BRepGraph_CoEdgesOfEdge anIt(theGraph, theGraph.Topo().Edges().CoEdges(aDef.ChildEdgeId)); anIt.More(); anIt.Next()) { const BRepGraph_CoEdgeId aOther = anIt.CurrentId(); if (aOther == theCoEdge) + { continue; + } const BRepGraphInc::CoEdgeDef& aOtherDef = anIt.Definition(); - if (aOtherDef.FaceDefId == aDef.FaceDefId && aOtherDef.Orientation != aDef.Orientation) + if (aOtherDef.FaceId == aDef.FaceId && aOtherDef.Orientation != aDef.Orientation) { return aOther; } @@ -520,32 +798,57 @@ Geom2dAdaptor_Curve BRepGraph_Tool::CoEdge::PCurveAdaptor(const BRepGraph& const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); // Try stored PCurve first. - const occ::handle& aStored = PCurve(theGraph, aCoEdge); + const occ::handle& aStored = PCurve(theGraph, theCoEdge); if (!aStored.IsNull()) { - return Geom2dAdaptor_Curve(aStored, aCoEdge.ParamFirst, aCoEdge.ParamLast); + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + double aPCFirst = 0.0, aPCLast = 0.0; + if (aCoEdge.Curve2DRepId.IsValid() && aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + && !aStorage.IsRemoved(aCoEdge.Curve2DRepId)) + { + const BRepGraphInc::CoEdgeCurve2DRep& aPCUse = + aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + aPCFirst = aPCUse.ParamFirst; + aPCLast = aPCUse.ParamLast; + } + return Geom2dAdaptor_Curve(aStored, aPCFirst, aPCLast); } // For planar faces, compute PCurve on-the-fly by projecting 3D curve onto the plane. - if (!aCoEdge.FaceDefId.IsValid() || !aCoEdge.EdgeDefId.IsValid()) + if (!aCoEdge.FaceId.IsValid() || !aCoEdge.ChildEdgeId.IsValid()) { return Geom2dAdaptor_Curve(); } - const occ::handle& aSurface = Face::Surface(theGraph, aCoEdge.FaceDefId); - const occ::handle aPlane = occ::down_cast(aSurface); - if (aPlane.IsNull() || !Edge::HasCurve(theGraph, aCoEdge.EdgeDefId)) + const occ::handle& aSurface = Face::Surface(theGraph, aCoEdge.FaceId); + if (aSurface.IsNull() || !Edge::HasCurve(theGraph, aCoEdge.ChildEdgeId)) { return Geom2dAdaptor_Curve(); } - const occ::handle& aCurve3d = Edge::Curve(theGraph, aCoEdge.EdgeDefId); + // Unwrap trimmed surface to check for plane basis. + occ::handle aPlane = occ::down_cast(aSurface); + if (aPlane.IsNull()) + { + const occ::handle aTrimmed = + occ::down_cast(aSurface); + if (!aTrimmed.IsNull()) + { + aPlane = occ::down_cast(aTrimmed->BasisSurface()); + } + } + if (aPlane.IsNull()) + { + return Geom2dAdaptor_Curve(); + } + + const occ::handle& aCurve3d = Edge::Curve(theGraph, aCoEdge.ChildEdgeId); if (aCurve3d.IsNull()) { return Geom2dAdaptor_Curve(); } - const std::pair aRange = Edge::Range(theGraph, aCoEdge.EdgeDefId); + const std::pair aRange = Edge::Range(theGraph, aCoEdge.ChildEdgeId); const occ::handle aProjected = GeomProjLib::Curve2d(aCurve3d, aRange.first, aRange.second, aPlane); if (aProjected.IsNull()) @@ -563,31 +866,14 @@ static const occ::handle THE_NULL_PCURVE; const occ::handle& BRepGraph_Tool::CoEdge::PCurve(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) { - const BRepGraph_Curve2DRepId aRepId = theGraph.Topo().CoEdges().Curve2DRepId(theCoEdge); - if (!aRepId.IsValid()) + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + if (!aCoEdge.Curve2DRepId.IsValid() || !aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + || aStorage.IsRemoved(aCoEdge.Curve2DRepId)) { return THE_NULL_PCURVE; } - return theGraph.Topo().Geometry().Curve2DRep(aRepId).Curve; -} - -//================================================================================================= - -const occ::handle& BRepGraph_Tool::CoEdge::PCurve( - const BRepGraph& theGraph, - const BRepGraphInc::CoEdgeDef& theCoEdge) -{ - if (!theCoEdge.Curve2DRepId.IsValid()) - { - return THE_NULL_PCURVE; - } - const BRepGraphInc::Curve2DRep& aCurveRep = - theGraph.Topo().Geometry().Curve2DRep(theCoEdge.Curve2DRepId); - if (aCurveRep.IsRemoved) - { - return THE_NULL_PCURVE; - } - return aCurveRep.Curve; + return aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId).Curve; } //================================================================================================= @@ -595,7 +881,10 @@ const occ::handle& BRepGraph_Tool::CoEdge::PCurve( bool BRepGraph_Tool::CoEdge::HasPCurve(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) { - return theGraph.Topo().CoEdges().Curve2DRepId(theCoEdge).IsValid(); + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + return aCoEdge.Curve2DRepId.IsValid() && aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + && !aStorage.IsRemoved(aCoEdge.Curve2DRepId); } //================================================================================================= @@ -603,8 +892,20 @@ bool BRepGraph_Tool::CoEdge::HasPCurve(const BRepGraph& theGraph, std::pair BRepGraph_Tool::CoEdge::UVPoints(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) { - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); - return {aCoEdge.UV1, aCoEdge.UV2}; + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + if (!aCoEdge.Curve2DRepId.IsValid() || !aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + || aStorage.IsRemoved(aCoEdge.Curve2DRepId)) + { + return {gp_Pnt2d(), gp_Pnt2d()}; + } + const BRepGraphInc::CoEdgeCurve2DRep& aPCUse = aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + const occ::handle& aCurve = aPCUse.Curve; + if (aCurve.IsNull()) + { + return {gp_Pnt2d(), gp_Pnt2d()}; + } + return {aCurve->Value(aPCUse.ParamFirst), aCurve->Value(aPCUse.ParamLast)}; } //================================================================================================= @@ -612,15 +913,24 @@ std::pair BRepGraph_Tool::CoEdge::UVPoints(const BRepGraph& std::pair BRepGraph_Tool::CoEdge::Range(const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge) { - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); - return {aCoEdge.ParamFirst, aCoEdge.ParamLast}; + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + double aFirst = 0.0, aLast = 0.0; + if (aCoEdge.Curve2DRepId.IsValid() && aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + && !aStorage.IsRemoved(aCoEdge.Curve2DRepId)) + { + const BRepGraphInc::CoEdgeCurve2DRep& aPCUse = aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + aFirst = aPCUse.ParamFirst; + aLast = aPCUse.ParamLast; + } + return {aFirst, aLast}; } //================================================================================================= -bool BRepGraph_Tool::Edge::IsClosedOnFace(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace) +bool BRepGraph_Tool::Edge::IsSeamOnFace(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace) { if (!theEdge.IsValid() || !theFace.IsValid()) { @@ -633,7 +943,7 @@ bool BRepGraph_Tool::Edge::IsClosedOnFace(const BRepGraph& theGraph, anIt.Next()) { const BRepGraphInc::CoEdgeDef& aCoEdge = anIt.Definition(); - if (aCoEdge.FaceDefId != theFace) + if (aCoEdge.FaceId != theFace) { continue; } @@ -653,53 +963,19 @@ bool BRepGraph_Tool::Edge::IsClosedOnFace(const BRepGraph& theGraph, //================================================================================================= -const BRepGraphInc::CoEdgeDef* BRepGraph_Tool::Edge::FindPCurve(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace) +BRepGraph_Tool::FaceUsage BRepGraph_Tool::Face::Usage(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef) { - return theGraph.Topo().Edges().FindPCurve(theEdge, theFace); -} - -//================================================================================================= - -const BRepGraphInc::CoEdgeDef* BRepGraph_Tool::Edge::FindPCurve(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace, - const TopAbs_Orientation theOri) -{ - return theGraph.Topo().Edges().FindPCurve(theEdge, theFace, theOri); -} - -//================================================================================================= - -static const occ::handle THE_NULL_POLYGON2D; - -const occ::handle& BRepGraph_Tool::CoEdge::PolygonOnSurface( - const BRepGraph& theGraph, - const BRepGraph_CoEdgeId theCoEdge) -{ - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); - if (!aCoEdge.Polygon2DRepId.IsValid()) + if (!theFaceRef.IsValid() || theGraph.Refs().Gen().IsRemoved(theFaceRef)) { - return THE_NULL_POLYGON2D; + return FaceUsage(); } - const BRepGraphInc::Polygon2DRep& aPolygonRep = - theGraph.Mesh().Poly().Polygon2DRep(aCoEdge.Polygon2DRepId); - if (aPolygonRep.IsRemoved) + const BRepGraphInc::FaceRef& aRef = theGraph.Refs().Faces().Entry(theFaceRef); + if (!aRef.ChildFaceId.IsValid() || aRef.ChildFaceId.IsRemoved(theGraph)) { - return THE_NULL_POLYGON2D; + return FaceUsage(); } - return aPolygonRep.Polygon; -} - -//================================================================================================= - -bool BRepGraph_Tool::CoEdge::HasPolygonOnSurface(const BRepGraph& theGraph, - const BRepGraph_CoEdgeId theCoEdge) -{ - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theCoEdge); - return aCoEdge.Polygon2DRepId.IsValid() - && !theGraph.Mesh().Poly().Polygon2DRep(aCoEdge.Polygon2DRepId).IsRemoved; + return FaceUsage{aRef.ChildFaceId, TopLoc_Location(), aRef.Orientation}; } //================================================================================================= @@ -711,56 +987,86 @@ double BRepGraph_Tool::Face::Tolerance(const BRepGraph& theGraph, const BRepGrap //================================================================================================= -bool BRepGraph_Tool::Face::NaturalRestriction(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace) +double BRepGraph_Tool::Face::Tolerance(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef) { - return theGraph.Topo().Faces().Definition(theFace).NaturalRestriction; + const BRepGraph_FaceId aFace = faceOf(theGraph, theFaceRef); + return aFace.IsValid() ? Tolerance(theGraph, aFace) : 0.0; } //================================================================================================= bool BRepGraph_Tool::Face::HasSurface(const BRepGraph& theGraph, const BRepGraph_FaceId theFace) { - return theGraph.Topo().Faces().SurfaceRepId(theFace).IsValid(); + return !theGraph.Topo().Faces().Surface(theFace).IsNull(); } //================================================================================================= -bool BRepGraph_Tool::Face::HasTriangulation(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace) +bool BRepGraph_Tool::Face::HasSurface(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef) { - // Cache-first, persistent fallback via MeshCacheOps. - return theGraph.Mesh().Faces().HasTriangulation(theFace); + const BRepGraph_FaceId aFace = faceOf(theGraph, theFaceRef); + return aFace.IsValid() && HasSurface(theGraph, aFace); } //================================================================================================= -const BRepGraphInc::WireRef* BRepGraph_Tool::Face::OuterWire(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace) +BRepGraph_WireId BRepGraph_Tool::Face::OuterWire(const BRepGraph& theGraph, + const BRepGraph_FaceId theFace) { + BRepGraph_WireId aResult; + double aUMin = 0.0; + double aUMax = 0.0; + double aVMin = 0.0; + double aVMax = 0.0; + const BRepGraph::RefsView& aRefs = theGraph.Refs(); for (BRepGraph_RefsWireOfFace aWireIt(theGraph, theFace); aWireIt.More(); aWireIt.Next()) { - const BRepGraphInc::WireRef& aRef = aRefs.Wires().Entry(aWireIt.CurrentId()); - if (!aRef.IsRemoved && aRef.IsOuter) + const BRepGraphInc::WireRef& aRef = aRefs.Wires().Entry(aWireIt.CurrentId()); + const BRepGraph_WireId aWireId = aRef.ChildWireId; + double aCurUMin = 0.0; + double aCurUMax = 0.0; + double aCurVMin = 0.0; + double aCurVMax = 0.0; + if (!wireUVBounds(theGraph, aWireId, aCurUMin, aCurUMax, aCurVMin, aCurVMax)) { - return &aRef; + continue; + } + + if (!aResult.IsValid()) + { + aResult = aWireId; + aUMin = aCurUMin; + aUMax = aCurUMax; + aVMin = aCurVMin; + aVMax = aCurVMax; + continue; + } + + if (((aCurUMin - aUMin) <= Precision::PConfusion()) + && ((aCurUMax - aUMax) >= -Precision::PConfusion()) + && ((aCurVMin - aVMin) <= Precision::PConfusion()) + && ((aCurVMax - aVMax) >= -Precision::PConfusion())) + { + aResult = aWireId; + aUMin = aCurUMin; + aUMax = aCurUMax; + aVMin = aCurVMin; + aVMax = aCurVMax; } } - return nullptr; + return aResult; } //================================================================================================= -BRepGraph_WireId BRepGraph_Tool::Face::OuterWireId(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace) +BRepGraph_WireId BRepGraph_Tool::Face::OuterWire(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef) { - const BRepGraphInc::WireRef* aOuter = OuterWire(theGraph, theFace); - if (aOuter == nullptr) - { - return BRepGraph_WireId(); - } - return aOuter->WireDefId; + const BRepGraph_FaceId aFace = faceOf(theGraph, theFaceRef); + return aFace.IsValid() ? OuterWire(theGraph, aFace) : BRepGraph_WireId(); } //================================================================================================= @@ -770,12 +1076,23 @@ static const occ::handle THE_NULL_SURFACE; const occ::handle& BRepGraph_Tool::Face::Surface(const BRepGraph& theGraph, const BRepGraph_FaceId theFace) { - const BRepGraph_SurfaceRepId aRepId = theGraph.Topo().Faces().SurfaceRepId(theFace); - if (!aRepId.IsValid()) + const BRepGraph_FaceSurfaceRepId aRepId = + theGraph.Topo().Faces().Definition(theFace).SurfaceRepId; + if (!aRepId.IsValid(theGraph.incStorage().NbFaceSurfaces()) + || theGraph.incStorage().IsRemoved(aRepId)) { return THE_NULL_SURFACE; } - return theGraph.Topo().Geometry().SurfaceRep(aRepId).Surface; + return theGraph.incStorage().FaceSurfaceRep(aRepId).Surface; +} + +//================================================================================================= + +const occ::handle& BRepGraph_Tool::Face::Surface(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef) +{ + const BRepGraph_FaceId aFace = faceOf(theGraph, theFaceRef); + return aFace.IsValid() ? Surface(theGraph, aFace) : THE_NULL_SURFACE; } //================================================================================================= @@ -783,12 +1100,7 @@ const occ::handle& BRepGraph_Tool::Face::Surface(const BRepGraph& GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor(const BRepGraph& theGraph, const BRepGraph_FaceId theFace) { - const BRepGraph_SurfaceRepId aRepId = theGraph.Topo().Faces().SurfaceRepId(theFace); - if (!aRepId.IsValid()) - { - return GeomAdaptor_TransformedSurface(); - } - const occ::handle& aSurf = theGraph.Topo().Geometry().SurfaceRep(aRepId).Surface; + const occ::handle aSurf = Surface(theGraph, theFace); if (aSurf.IsNull()) { return GeomAdaptor_TransformedSurface(); @@ -798,6 +1110,31 @@ GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor(const BRepGr //================================================================================================= +GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor(const BRepGraph& theGraph, + const FaceUsage& theRef) +{ + const occ::handle aSurf = Surface(theGraph, theRef.DefId); + if (aSurf.IsNull()) + { + return GeomAdaptor_TransformedSurface(); + } + return GeomAdaptor_TransformedSurface( + aSurf, + theRef.Location.IsIdentity() ? gp_Trsf() : theRef.Location.Transformation()); +} + +//================================================================================================= + +GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor( + const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef) +{ + const FaceUsage aUsage = Usage(theGraph, theFaceRef); + return aUsage.IsValid() ? SurfaceAdaptor(theGraph, aUsage) : GeomAdaptor_TransformedSurface(); +} + +//================================================================================================= + GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor(const BRepGraph& theGraph, const BRepGraph_FaceId theFace, const double theUFirst, @@ -805,12 +1142,7 @@ GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor(const BRepGr const double theVFirst, const double theVLast) { - const BRepGraph_SurfaceRepId aRepId = theGraph.Topo().Faces().SurfaceRepId(theFace); - if (!aRepId.IsValid()) - { - return GeomAdaptor_TransformedSurface(); - } - const occ::handle& aSurf = theGraph.Topo().Geometry().SurfaceRep(aRepId).Surface; + const occ::handle aSurf = Surface(theGraph, theFace); if (aSurf.IsNull()) { return GeomAdaptor_TransformedSurface(); @@ -820,27 +1152,57 @@ GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor(const BRepGr //================================================================================================= -static const occ::handle THE_NULL_TRIANGULATION; - -const occ::handle& BRepGraph_Tool::Face::Triangulation( - const BRepGraph& theGraph, - const BRepGraph_FaceId theFace) +GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor(const BRepGraph& theGraph, + const FaceUsage& theRef, + const double theUFirst, + const double theULast, + const double theVFirst, + const double theVLast) { - // Cache-first, persistent fallback via MeshCacheOps. - const BRepGraph_TriangulationRepId aTriRepId = - theGraph.Mesh().Faces().ActiveTriangulationRepId(theFace); - if (!aTriRepId.IsValid()) + const occ::handle aSurf = Surface(theGraph, theRef.DefId); + if (aSurf.IsNull()) { - return THE_NULL_TRIANGULATION; + return GeomAdaptor_TransformedSurface(); } - return theGraph.Mesh().Poly().TriangulationRep(aTriRepId).Triangulation; + return GeomAdaptor_TransformedSurface( + aSurf, + theUFirst, + theULast, + theVFirst, + theVLast, + theRef.Location.IsIdentity() ? gp_Trsf() : theRef.Location.Transformation()); +} + +//================================================================================================= + +GeomAdaptor_TransformedSurface BRepGraph_Tool::Face::SurfaceAdaptor( + const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef, + const double theUFirst, + const double theULast, + const double theVFirst, + const double theVLast) +{ + const FaceUsage aUsage = Usage(theGraph, theFaceRef); + return aUsage.IsValid() + ? SurfaceAdaptor(theGraph, aUsage, theUFirst, theULast, theVFirst, theVLast) + : GeomAdaptor_TransformedSurface(); } //================================================================================================= uint32_t BRepGraph_Tool::Face::NbWires(const BRepGraph& theGraph, const BRepGraph_FaceId theFace) { - return static_cast(theGraph.Topo().Faces().Definition(theFace).WireRefIds.Size()); + return static_cast(theGraph.Topo().Faces().Relations(theFace).WireRefIds.Size()); +} + +//================================================================================================= + +uint32_t BRepGraph_Tool::Face::NbWires(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef) +{ + const BRepGraph_FaceId aFace = faceOf(theGraph, theFaceRef); + return aFace.IsValid() ? NbWires(theGraph, aFace) : 0; } //================================================================================================= @@ -862,29 +1224,46 @@ void BRepGraph_Tool::Face::Bounds(const BRepGraph& theGraph, //================================================================================================= +void BRepGraph_Tool::Face::Bounds(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef, + double& theUMin, + double& theUMax, + double& theVMin, + double& theVMax) +{ + const BRepGraph_FaceId aFace = faceOf(theGraph, theFaceRef); + if (aFace.IsValid()) + { + Bounds(theGraph, aFace, theUMin, theUMax, theVMin, theVMax); + return; + } + theUMin = theUMax = theVMin = theVMax = 0.0; +} + +//================================================================================================= + occ::handle BRepGraph_Tool::Edge::CurveOnSurface( const BRepGraph& theGraph, const BRepGraphInc::CoEdgeInstance& theRef, const BRepGraph_FaceId theFace) { - const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theRef.DefId); - const BRepGraphInc::FaceDef& aFace = theGraph.Topo().Faces().Definition(theFace); + const BRepGraphInc::CoEdgeDef& aCoEdge = theGraph.Topo().CoEdges().Definition(theRef.DefId); + const BRepGraphInc::FaceDef& aFace = theGraph.Topo().Faces().Definition(theFace); + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); - if (!aCoEdge.Curve2DRepId.IsValid() || !aFace.SurfaceRepId.IsValid()) + if (!aCoEdge.Curve2DRepId.IsValid() || !aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + || aStorage.IsRemoved(aCoEdge.Curve2DRepId) || !aFace.SurfaceRepId.IsValid() + || !aFace.SurfaceRepId.IsValid(aStorage.NbFaceSurfaces()) + || aStorage.IsRemoved(aFace.SurfaceRepId)) { return occ::handle(); } - const BRepGraphInc::Curve2DRep& aPCurveRep = - theGraph.Topo().Geometry().Curve2DRep(aCoEdge.Curve2DRepId); - const BRepGraphInc::SurfaceRep& aSurfaceRep = - theGraph.Topo().Geometry().SurfaceRep(aFace.SurfaceRepId); - if (aPCurveRep.IsRemoved || aSurfaceRep.IsRemoved) - { - return occ::handle(); - } - const occ::handle& aPCurve = aPCurveRep.Curve; - const occ::handle& aSurf = aSurfaceRep.Surface; + const BRepGraphInc::CoEdgeCurve2DRep& aPCurveUse = + aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + const BRepGraphInc::FaceSurfaceRep& aSurfaceRep = aStorage.FaceSurfaceRep(aFace.SurfaceRepId); + const occ::handle& aPCurve = aPCurveUse.Curve; + const occ::handle& aSurf = aSurfaceRep.Surface; if (aPCurve.IsNull() || aSurf.IsNull()) { @@ -892,23 +1271,58 @@ occ::handle BRepGraph_Tool::Edge::CurveOnSurface( } occ::handle aHC2d = - new Geom2dAdaptor_Curve(aPCurve, aCoEdge.ParamFirst, aCoEdge.ParamLast); + new Geom2dAdaptor_Curve(aPCurve, aPCurveUse.ParamFirst, aPCurveUse.ParamLast); occ::handle aHS = new GeomAdaptor_Surface(aSurf); return new Adaptor3d_CurveOnSurface(aHC2d, aHS); } //================================================================================================= +BRepGraph_Tool::WireUsage BRepGraph_Tool::Wire::Usage(const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef) +{ + if (!theWireRef.IsValid() || theGraph.Refs().Gen().IsRemoved(theWireRef)) + { + return WireUsage(); + } + const BRepGraphInc::WireRef& aRef = theGraph.Refs().Wires().Entry(theWireRef); + if (!aRef.ChildWireId.IsValid() || aRef.ChildWireId.IsRemoved(theGraph)) + { + return WireUsage(); + } + return WireUsage{aRef.ChildWireId, TopLoc_Location(), aRef.Orientation}; +} + +//================================================================================================= + bool BRepGraph_Tool::Wire::IsClosed(const BRepGraph& theGraph, const BRepGraph_WireId theWire) { - return theGraph.Topo().Wires().Definition(theWire).IsClosed; + bool aClosed = false; + return derivedStateCache(theGraph)->GetWireIsClosed(theWire, aClosed) && aClosed; +} + +//================================================================================================= + +bool BRepGraph_Tool::Wire::IsClosed(const BRepGraph& theGraph, const BRepGraph_WireRefId theWireRef) +{ + const BRepGraph_WireId aWire = wireOf(theGraph, theWireRef); + return aWire.IsValid() && IsClosed(theGraph, aWire); } //================================================================================================= uint32_t BRepGraph_Tool::Wire::NbCoEdges(const BRepGraph& theGraph, const BRepGraph_WireId theWire) { - return static_cast(theGraph.Topo().Wires().Definition(theWire).CoEdgeRefIds.Size()); + return static_cast(theGraph.Topo().Wires().Relations(theWire).CoEdgeIds.Size()); +} + +//================================================================================================= + +uint32_t BRepGraph_Tool::Wire::NbCoEdges(const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef) +{ + const BRepGraph_WireId aWire = wireOf(theGraph, theWireRef); + return aWire.IsValid() ? NbCoEdges(theGraph, aWire) : 0; } //================================================================================================= @@ -916,16 +1330,37 @@ uint32_t BRepGraph_Tool::Wire::NbCoEdges(const BRepGraph& theGraph, const BRepGr uint32_t BRepGraph_Tool::Wire::NbDistinctEdges(const BRepGraph& theGraph, const BRepGraph_WireId theWire) { - // Wires are small (typically 3-8 entries); a typed Map dedup on EdgeDefId is fine. - NCollection_Map aSeenEdges; - for (BRepGraph_RefsCoEdgeOfWire anIt(theGraph, theWire); anIt.More(); anIt.Next()) + uint32_t aCount = 0; + for (BRepGraph_CoEdgesOfWire anIt(theGraph, theWire); anIt.More(); anIt.Next()) { - const BRepGraphInc::CoEdgeRef& aRef = theGraph.Refs().CoEdges().Entry(anIt.CurrentId()); - if (!aRef.CoEdgeDefId.IsValid()) - continue; - aSeenEdges.Add(theGraph.Topo().CoEdges().Definition(aRef.CoEdgeDefId).EdgeDefId); + const BRepGraph_EdgeId anEdgeId = + theGraph.Topo().CoEdges().Definition(anIt.CurrentId()).ChildEdgeId; + bool wasSeen = false; + for (BRepGraph_CoEdgesOfWire aPrevIt(theGraph, theWire); + aPrevIt.More() && aPrevIt.Index() < anIt.Index(); + aPrevIt.Next()) + { + if (theGraph.Topo().CoEdges().Definition(aPrevIt.CurrentId()).ChildEdgeId == anEdgeId) + { + wasSeen = true; + break; + } + } + if (!wasSeen) + { + ++aCount; + } } - return static_cast(aSeenEdges.Extent()); + return aCount; +} + +//================================================================================================= + +uint32_t BRepGraph_Tool::Wire::NbDistinctEdges(const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef) +{ + const BRepGraph_WireId aWire = wireOf(theGraph, theWireRef); + return aWire.IsValid() ? NbDistinctEdges(theGraph, aWire) : 0; } //================================================================================================= @@ -933,32 +1368,51 @@ uint32_t BRepGraph_Tool::Wire::NbDistinctEdges(const BRepGraph& theGraph, BRepGraph_FaceId BRepGraph_Tool::Wire::FaceOf(const BRepGraph& theGraph, const BRepGraph_WireId theWire) { - const NCollection_DynamicArray& aFaces = theGraph.Topo().Wires().Faces(theWire); - if (aFaces.IsEmpty()) + const NCollection_LinearVector& aRefs = + theGraph.Topo().Wires().Relations(theWire).ParentWireRefIds; + for (const BRepGraph_WireRefId& aRefId : aRefs) { - return BRepGraph_FaceId(); + if (!aRefId.IsRemoved(theGraph)) + { + return theGraph.Refs().Wires().Entry(aRefId).ParentFaceId; + } } - return aFaces.First(); + return BRepGraph_FaceId(); +} + +//================================================================================================= + +BRepGraph_FaceId BRepGraph_Tool::Wire::FaceOf(const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef) +{ + const BRepGraph_WireId aWire = wireOf(theGraph, theWireRef); + return aWire.IsValid() ? FaceOf(theGraph, aWire) : BRepGraph_FaceId(); } //================================================================================================= bool BRepGraph_Tool::Wire::IsOuter(const BRepGraph& theGraph, const BRepGraph_WireId theWire) { - const NCollection_DynamicArray& aFaces = theGraph.Topo().Wires().Faces(theWire); - for (const BRepGraph_FaceId& aFaceId : aFaces) + const NCollection_LinearVector& aRefs = + theGraph.Topo().Wires().Relations(theWire).ParentWireRefIds; + for (const BRepGraph_WireRefId& aRefId : aRefs) { + if (aRefId.IsRemoved(theGraph)) + { + continue; + } + const BRepGraph_FaceId aFaceId = theGraph.Refs().Wires().Entry(aRefId).ParentFaceId; if (!aFaceId.IsValid()) { continue; } - const BRepGraph::RefsView& aRefs = theGraph.Refs(); + const BRepGraph::RefsView& aRefsView = theGraph.Refs(); for (BRepGraph_RefsWireOfFace aWireIt(theGraph, aFaceId); aWireIt.More(); aWireIt.Next()) { - const BRepGraphInc::WireRef& aRef = aRefs.Wires().Entry(aWireIt.CurrentId()); - if (!aRef.IsRemoved && aRef.WireDefId == theWire) + const BRepGraphInc::WireRef& aRef = aRefsView.Wires().Entry(aWireIt.CurrentId()); + if (!theGraph.Refs().Gen().IsRemoved(aWireIt.CurrentId()) && aRef.ChildWireId == theWire) { - return aRef.IsOuter; + return BRepGraph_Tool::Face::OuterWire(theGraph, aFaceId) == theWire; } } } @@ -967,197 +1421,59 @@ bool BRepGraph_Tool::Wire::IsOuter(const BRepGraph& theGraph, const BRepGraph_Wi //================================================================================================= +bool BRepGraph_Tool::Wire::IsOuter(const BRepGraph& theGraph, const BRepGraph_WireRefId theWireRef) +{ + const BRepGraph_WireId aWire = wireOf(theGraph, theWireRef); + return aWire.IsValid() && IsOuter(theGraph, aWire); +} + +//================================================================================================= + +BRepGraph_Tool::ShellUsage BRepGraph_Tool::Shell::Usage(const BRepGraph& theGraph, + const BRepGraph_ShellRefId theShellRef) +{ + if (!theShellRef.IsValid() || theGraph.Refs().Gen().IsRemoved(theShellRef)) + { + return ShellUsage(); + } + const BRepGraphInc::ShellRef& aRef = theGraph.Refs().Shells().Entry(theShellRef); + if (!aRef.ChildShellId.IsValid() || aRef.ChildShellId.IsRemoved(theGraph)) + { + return ShellUsage(); + } + return ShellUsage{aRef.ChildShellId, TopLoc_Location(), aRef.Orientation}; +} + +//================================================================================================= + bool BRepGraph_Tool::Shell::IsClosed(const BRepGraph& theGraph, const BRepGraph_ShellId theShell) { - return theGraph.Topo().Shells().Definition(theShell).IsClosed; + BRepGraph_CacheDerivedState::ShellEntry anEntry; + return derivedStateCache(theGraph)->GetShellStatus(theShell, anEntry) + && anEntry.Status == BRepGraph_CacheDerivedState::ShellClosureStatus::Closed; +} + +//================================================================================================= + +bool BRepGraph_Tool::Shell::IsClosed(const BRepGraph& theGraph, + const BRepGraph_ShellRefId theShellRef) +{ + const BRepGraph_ShellId aShell = shellOf(theGraph, theShellRef); + return aShell.IsValid() && IsClosed(theGraph, aShell); } //================================================================================================= uint32_t BRepGraph_Tool::Shell::NbFaces(const BRepGraph& theGraph, const BRepGraph_ShellId theShell) { - return static_cast(theGraph.Topo().Shells().Definition(theShell).FaceRefIds.Size()); + return static_cast(theGraph.Topo().Shells().Relations(theShell).FaceRefIds.Size()); } -//================================================================================================= -// BRepGraph_Tool::Mesh //================================================================================================= -BRepGraph_TriangulationRepId BRepGraph_Tool::Mesh::CreateTriangulationRep( - BRepGraph& theGraph, - const occ::handle& theTriangulation) +uint32_t BRepGraph_Tool::Shell::NbFaces(const BRepGraph& theGraph, + const BRepGraph_ShellRefId theShellRef) { - if (theTriangulation.IsNull()) - { - return BRepGraph_TriangulationRepId(); - } - - const BRepGraph_TriangulationRepId aRepId = - theGraph.data()->myIncStorage.AppendTriangulationRep(); - theGraph.data()->myIncStorage.ChangeTriangulationRep(aRepId).Triangulation = theTriangulation; - return aRepId; -} - -//================================================================================================= - -BRepGraph_Polygon3DRepId BRepGraph_Tool::Mesh::CreatePolygon3DRep( - BRepGraph& theGraph, - const occ::handle& thePolygon) -{ - if (thePolygon.IsNull()) - { - return BRepGraph_Polygon3DRepId(); - } - - const BRepGraph_Polygon3DRepId aRepId = theGraph.data()->myIncStorage.AppendPolygon3DRep(); - theGraph.data()->myIncStorage.ChangePolygon3DRep(aRepId).Polygon = thePolygon; - return aRepId; -} - -//================================================================================================= - -BRepGraph_PolygonOnTriRepId BRepGraph_Tool::Mesh::CreatePolygonOnTriRep( - BRepGraph& theGraph, - const occ::handle& thePolygon, - const BRepGraph_TriangulationRepId theTriRepId) -{ - if (thePolygon.IsNull() || !theTriRepId.IsValid()) - { - return BRepGraph_PolygonOnTriRepId(); - } - - const BRepGraph_PolygonOnTriRepId aRepId = theGraph.data()->myIncStorage.AppendPolygonOnTriRep(); - BRepGraphInc::PolygonOnTriRep& aRep = theGraph.data()->myIncStorage.ChangePolygonOnTriRep(aRepId); - aRep.Polygon = thePolygon; - aRep.TriangulationRepId = theTriRepId; - return aRepId; -} - -//================================================================================================= - -void BRepGraph_Tool::Mesh::AppendCachedTriangulation(BRepGraph& theGraph, - const BRepGraph_FaceId theFace, - const BRepGraph_TriangulationRepId theTriRepId) -{ - const BRepGraphInc_Storage& aStorage = theGraph.data()->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces()) || !theTriRepId.IsValid()) - { - return; - } - BRepGraph_MeshCache::FaceMeshEntry& anEntry = - theGraph.data()->myMeshCache.ChangeFaceMesh(theFace); - anEntry.TriangulationRepIds.Append(theTriRepId); - anEntry.StoredOwnGen = aStorage.Face(theFace).OwnGen; -} - -//================================================================================================= - -void BRepGraph_Tool::Mesh::SetCachedActiveIndex(BRepGraph& theGraph, - const BRepGraph_FaceId theFace, - const int theActiveIndex) -{ - const BRepGraphInc_Storage& aStorage = theGraph.data()->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) - { - return; - } - BRepGraph_MeshCache::FaceMeshEntry& anEntry = - theGraph.data()->myMeshCache.ChangeFaceMesh(theFace); - anEntry.ActiveTriangulationIndex = theActiveIndex; - anEntry.StoredOwnGen = aStorage.Face(theFace).OwnGen; -} - -//================================================================================================= - -void BRepGraph_Tool::Mesh::ClearFaceCache(BRepGraph& theGraph, const BRepGraph_FaceId theFace) -{ - BRepGraph_MeshCacheStorage& aMeshCache = theGraph.data()->myMeshCache; - aMeshCache.ClearFaceMesh(theFace); - - const BRepGraphInc_Storage& aStorage = theGraph.data()->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) - { - return; - } - const BRepGraphInc::FaceDef& aFace = aStorage.Face(theFace); - for (const BRepGraph_WireRefId& aWireRefId : aFace.WireRefIds) - { - if (!aWireRefId.IsValid(aStorage.NbWireRefs())) - { - continue; - } - const BRepGraph_WireId aWireId = aStorage.WireRef(aWireRefId).WireDefId; - if (!aWireId.IsValid(aStorage.NbWires())) - { - continue; - } - const BRepGraphInc::WireDef& aWire = aStorage.Wire(aWireId); - for (const BRepGraph_CoEdgeRefId& aCERefId : aWire.CoEdgeRefIds) - { - if (!aCERefId.IsValid(aStorage.NbCoEdgeRefs())) - { - continue; - } - const BRepGraph_CoEdgeId aCoEdgeId = aStorage.CoEdgeRef(aCERefId).CoEdgeDefId; - aMeshCache.ClearCoEdgeMesh(aCoEdgeId); - } - } -} - -//================================================================================================= - -void BRepGraph_Tool::Mesh::SetCachedPolygon3D(BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_Polygon3DRepId thePolyRepId) -{ - const BRepGraphInc_Storage& aStorage = theGraph.data()->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges())) - { - return; - } - BRepGraph_MeshCache::EdgeMeshEntry& anEntry = - theGraph.data()->myMeshCache.ChangeEdgeMesh(theEdge); - anEntry.Polygon3DRepId = thePolyRepId; - anEntry.StoredOwnGen = aStorage.Edge(theEdge).OwnGen; -} - -//================================================================================================= - -void BRepGraph_Tool::Mesh::ClearEdgeCache(BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) -{ - theGraph.data()->myMeshCache.ClearEdgeMesh(theEdge); -} - -//================================================================================================= - -void BRepGraph_Tool::Mesh::AppendCachedPolygonOnTri(BRepGraph& theGraph, - const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_PolygonOnTriRepId thePolyRepId) -{ - const BRepGraphInc_Storage& aStorage = theGraph.data()->myIncStorage; - if (!theCoEdge.IsValid(aStorage.NbCoEdges()) || !thePolyRepId.IsValid()) - { - return; - } - BRepGraph_MeshCache::CoEdgeMeshEntry& anEntry = - theGraph.data()->myMeshCache.ChangeCoEdgeMesh(theCoEdge); - anEntry.PolygonOnTriRepIds.Append(thePolyRepId); - anEntry.StoredOwnGen = aStorage.CoEdge(theCoEdge).OwnGen; -} - -//================================================================================================= - -void BRepGraph_Tool::Mesh::SetCachedPolygon2D(BRepGraph& theGraph, - const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_Polygon2DRepId thePolyRepId) -{ - const BRepGraphInc_Storage& aStorage = theGraph.data()->myIncStorage; - if (!theCoEdge.IsValid(aStorage.NbCoEdges())) - { - return; - } - BRepGraph_MeshCache::CoEdgeMeshEntry& anEntry = - theGraph.data()->myMeshCache.ChangeCoEdgeMesh(theCoEdge); - anEntry.Polygon2DRepId = thePolyRepId; - anEntry.StoredOwnGen = aStorage.CoEdge(theCoEdge).OwnGen; + const BRepGraph_ShellId aShell = shellOf(theGraph, theShellRef); + return aShell.IsValid() ? NbFaces(theGraph, aShell) : 0; } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.hxx index b9ff61f819..c508ce148d 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Tool.hxx @@ -15,17 +15,12 @@ #define _BRepGraph_Tool_HeaderFile #include -#include -#include -#include #include #include #include #include #include -#include -#include -#include +#include #include #include #include @@ -48,24 +43,31 @@ class Adaptor3d_CurveOnSurface; //! the queried property. //! //! Methods are grouped by topology kind via nested classes: -//! BRepGraph_Tool::Vertex, Edge, CoEdge, Face, Wire. +//! BRepGraph_Tool::Vertex, Edge, CoEdge, Face, Wire, Shell. class BRepGraph_Tool { public: using VertexUsage = BRepGraphInc::VertexInstance; using CoEdgeUsage = BRepGraphInc::CoEdgeInstance; - using VertexRef = BRepGraphInc::VertexRef; - using WireRef = BRepGraphInc::WireRef; - using CoEdgeDef = BRepGraphInc::CoEdgeDef; + using FaceUsage = BRepGraphInc::FaceInstance; + using WireUsage = BRepGraphInc::WireInstance; + using ShellUsage = BRepGraphInc::ShellInstance; //! @brief Vertex geometry accessors. //! - //! Provides 3D point retrieval (with or without Location applied), - //! tolerance access, and parameter lookup for vertex-on-curve and - //! vertex-on-surface representations. + //! Provides 3D point retrieval (with or without Location applied) and + //! tolerance access. class Vertex { public: + //! Resolves a vertex reference id to a lightweight usage value. + //! @param[in] theGraph source graph + //! @param[in] theVertexRef typed vertex reference identifier + //! @return vertex usage, or invalid usage if the reference is invalid or removed + [[nodiscard]] Standard_EXPORT static VertexUsage Usage( + const BRepGraph& theGraph, + const BRepGraph_VertexRefId theVertexRef); + //! Returns the vertex 3D point with VertexUsage Location applied. //! @param[in] theGraph source graph //! @param[in] theRef vertex incidence reference carrying Location @@ -80,6 +82,13 @@ public: [[nodiscard]] Standard_EXPORT static gp_Pnt Pnt(const BRepGraph& theGraph, const BRepGraph_VertexId theVertex); + //! Returns the vertex 3D point with vertex reference location applied. + //! @param[in] theGraph source graph + //! @param[in] theVertexRef typed vertex reference identifier + //! @return transformed 3D point + [[nodiscard]] Standard_EXPORT static gp_Pnt Pnt(const BRepGraph& theGraph, + const BRepGraph_VertexRefId theVertexRef); + //! Returns the vertex tolerance. //! @param[in] theGraph source graph //! @param[in] theVertex typed vertex definition identifier @@ -87,35 +96,12 @@ public: [[nodiscard]] Standard_EXPORT static double Tolerance(const BRepGraph& theGraph, const BRepGraph_VertexId theVertex); - //! Returns the vertex parameter on an edge's 3D curve. - //! @param[in] theGraph source graph - //! @param[in] theVertex typed vertex definition identifier - //! @param[in] theEdge typed edge definition identifier - //! @return curve parameter - //! @throws Standard_NoSuchObject if vertex has no PointOnCurve for this edge - [[nodiscard]] Standard_EXPORT static double Parameter(const BRepGraph& theGraph, - const BRepGraph_VertexId theVertex, - const BRepGraph_EdgeId theEdge); - - //! Returns the vertex (U,V) parameters on a face surface. - //! @param[in] theGraph source graph - //! @param[in] theVertex typed vertex definition identifier - //! @param[in] theFace typed face definition identifier - //! @return 2D point with (U,V) parameters - //! @throws Standard_NoSuchObject if vertex has no PointOnSurface for this face - [[nodiscard]] Standard_EXPORT static gp_Pnt2d Parameters(const BRepGraph& theGraph, - const BRepGraph_VertexId theVertex, - const BRepGraph_FaceId theFace); - - //! Returns the vertex parameter on a coedge's PCurve. - //! @param[in] theGraph source graph - //! @param[in] theVertex typed vertex definition identifier - //! @param[in] theCoEdge typed coedge definition identifier - //! @return PCurve parameter - //! @throws Standard_NoSuchObject if vertex has no PointOnPCurve for this coedge - [[nodiscard]] Standard_EXPORT static double PCurveParameter(const BRepGraph& theGraph, - const BRepGraph_VertexId theVertex, - const BRepGraph_CoEdgeId theCoEdge); + //! Returns the vertex tolerance by vertex reference identifier. + //! @param[in] theGraph source graph + //! @param[in] theVertexRef typed vertex reference identifier + //! @return tolerance value + [[nodiscard]] Standard_EXPORT static double Tolerance(const BRepGraph& theGraph, + const BRepGraph_VertexRefId theVertexRef); //! Returns the number of edges that reference this vertex. //! @param[in] theGraph source graph @@ -125,12 +111,11 @@ public: const BRepGraph_VertexId theVertex); }; - //! @brief Edge geometry, curve, polygon, and continuity accessors. + //! @brief Edge geometry, curve, and continuity accessors. //! //! Provides tolerance, degeneracy, and parameter flags; raw and - //! location-adjusted 3D curve access; polygon discretization; - //! continuity queries between adjacent faces; and PCurve lookup - //! for edge-face contexts including seam edge support. + //! location-adjusted 3D curve access; and PCurve lookup for edge-face + //! contexts including seam edge support. class Edge { public: @@ -141,27 +126,56 @@ public: [[nodiscard]] Standard_EXPORT static double Tolerance(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); - //! Returns true if the edge is degenerate (collapses to a point on surface). + //! Returns the tolerance of the edge referenced by the coedge. + [[nodiscard]] Standard_EXPORT static double Tolerance(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + + //! Returns true if the edge is degenerate, derived from current geometry. //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier //! @return true if degenerate [[nodiscard]] Standard_EXPORT static bool Degenerated(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); - //! Returns the SameParameter flag. + //! Returns true if the edge referenced by the coedge is degenerate. + [[nodiscard]] Standard_EXPORT static bool Degenerated(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + + //! Returns true if all PCurves are reparametrized to the same range as the 3D curve. + //! This is a derived geometry check, not a stored flag. //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier - //! @return true if all PCurves are reparametrized to the same range as the 3D curve + //! @return true if same parameter [[nodiscard]] Standard_EXPORT static bool SameParameter(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); - //! Returns the SameRange flag. + //! Returns SameParameter for the edge referenced by the coedge. + [[nodiscard]] Standard_EXPORT static bool SameParameter(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + + //! Returns true if PCurve parameter range equals the 3D curve range. + //! This is a derived check, not a stored flag. //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier - //! @return true if PCurve parameter range equals the 3D curve range + //! @return true if same range [[nodiscard]] Standard_EXPORT static bool SameRange(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); + //! Returns SameRange for the edge referenced by the coedge. + [[nodiscard]] Standard_EXPORT static bool SameRange(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + + //! Returns true if the edge forms a topological loop, derived from vertex topology. + //! @param[in] theGraph source graph + //! @param[in] theEdge typed edge definition identifier + //! @return true if closed + [[nodiscard]] Standard_EXPORT static bool IsClosed(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge); + + //! Returns the closed flag for the edge referenced by the coedge. + [[nodiscard]] Standard_EXPORT static bool IsClosed(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + //! Returns the 3D curve parameter range as (first, last). //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier @@ -170,39 +184,36 @@ public: const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); - //! Returns the start vertex reference entry (carries Location and Orientation). + //! Returns the 3D curve parameter range for the edge referenced by the coedge. + [[nodiscard]] Standard_EXPORT static std::pair Range( + const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + + //! Returns the start vertex reference id directly. //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier - //! @return const reference to the start VertexRef - [[nodiscard]] Standard_EXPORT static const VertexRef& StartVertexRef( + //! @return start vertex reference id + [[nodiscard]] Standard_EXPORT static BRepGraph_VertexRefId StartVertexId( const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); - //! Returns the end vertex reference entry (carries Location and Orientation). + //! Returns the coedge start vertex reference id, respecting coedge orientation. + [[nodiscard]] Standard_EXPORT static BRepGraph_VertexRefId StartVertexId( + const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + + //! Returns the end vertex reference id directly. //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier - //! @return const reference to the end VertexRef - [[nodiscard]] Standard_EXPORT static const VertexRef& EndVertexRef( + //! @return end vertex reference id + [[nodiscard]] Standard_EXPORT static BRepGraph_VertexRefId EndVertexId( const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); - //! Returns the start vertex definition id directly (shortcut for - //! `StartVertexRef(...).VertexDefId`). Invalid if the edge has no start vertex. - //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @return start vertex id - [[nodiscard]] Standard_EXPORT static BRepGraph_VertexId StartVertexId( - const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge); - - //! Returns the end vertex definition id directly (shortcut for - //! `EndVertexRef(...).VertexDefId`). Invalid if the edge has no end vertex. - //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @return end vertex id - [[nodiscard]] Standard_EXPORT static BRepGraph_VertexId EndVertexId( - const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge); + //! Returns the coedge end vertex reference id, respecting coedge orientation. + [[nodiscard]] Standard_EXPORT static BRepGraph_VertexRefId EndVertexId( + const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); //! Returns true if the edge has a 3D curve representation. //! @param[in] theGraph source graph @@ -211,6 +222,10 @@ public: [[nodiscard]] Standard_EXPORT static bool HasCurve(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); + //! Returns true if the edge referenced by the coedge has a 3D curve. + [[nodiscard]] Standard_EXPORT static bool HasCurve(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + //! Returns the raw 3D curve handle (definition frame, no copy). //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier @@ -219,6 +234,11 @@ public: const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); + //! Returns the raw 3D curve handle for the edge referenced by the coedge. + [[nodiscard]] Standard_EXPORT static const occ::handle& Curve( + const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + //! Returns the transformed 3D curve handle via CoEdgeUsage (applies Location, may copy). //! @param[in] theGraph source graph //! @param[in] theRef coedge incidence reference carrying Location @@ -234,6 +254,11 @@ public: const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); + //! Returns the 3D curve adaptor for the edge referenced by the coedge. + [[nodiscard]] Standard_EXPORT static GeomAdaptor_TransformedCurve CurveAdaptor( + const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + //! Returns the 3D curve adaptor via CoEdgeUsage (applies edge-in-wire Location in Trsf). //! Falls back to CurveOnSurface when no 3D curve exists. //! @param[in] theGraph source graph @@ -243,50 +268,63 @@ public: const BRepGraph& theGraph, const CoEdgeUsage& theRef); - //! Returns true if the edge has a 3D polygon discretization. + //! Find an active edge by its boundary vertices. //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @return true if edge has a polygon - [[nodiscard]] Standard_EXPORT static bool HasPolygon3D(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge); + //! @param[in] theStartVertex start vertex to match + //! @param[in] theEndVertex end vertex to match + //! @param[in] theToIgnoreOrientation when true, also matches the reverse vertex order + //! @return edge id, or invalid if no active edge matches + [[nodiscard]] Standard_EXPORT static BRepGraph_EdgeId FindByVertices( + const BRepGraph& theGraph, + const BRepGraph_VertexId theStartVertex, + const BRepGraph_VertexId theEndVertex, + const bool theToIgnoreOrientation = false); - //! Returns the 3D polygon handle (definition frame). + //! Find an active coedge carrying PCurve data for the given edge-face use. //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @return polygon handle, or null handle if no polygon - [[nodiscard]] Standard_EXPORT static const occ::handle& Polygon3D( + //! @param[in] theEdge edge definition to match + //! @param[in] theFace face definition to match + //! @return matching coedge id, or invalid if the edge/face pair has no active PCurve coedge + [[nodiscard]] Standard_EXPORT static BRepGraph_CoEdgeId FindPCurveCoEdgeId( const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge); + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace); - //! Returns true if the edge has continuity info between two faces. - //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @param[in] theFace1 typed first face definition identifier - //! @param[in] theFace2 typed second face definition identifier - //! @return true if continuity is recorded - [[nodiscard]] Standard_EXPORT static bool HasContinuity(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2); + //! Find an active PCurve coedge for the given edge-face use and preferred orientation. + //! @param[in] theGraph source graph + //! @param[in] theEdge edge definition to match + //! @param[in] theFace face definition to match + //! @param[in] theOrientation preferred coedge orientation + //! @return exact orientation match when present; otherwise the first active PCurve + //! coedge on the edge-face pair; invalid if there is no active PCurve coedge + [[nodiscard]] Standard_EXPORT static BRepGraph_CoEdgeId FindPCurveCoEdgeId( + const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace, + const TopAbs_Orientation theOrientation); - //! Returns the geometric continuity between two adjacent faces. + //! Find an active coedge for the given edge-face use. //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @param[in] theFace1 typed first face definition identifier - //! @param[in] theFace2 typed second face definition identifier - //! @return continuity order (GeomAbs_C0 if not found) - [[nodiscard]] Standard_EXPORT static GeomAbs_Shape Continuity(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace1, - const BRepGraph_FaceId theFace2); - - //! Returns the maximum continuity across all face pairs for this edge. - //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @return maximum continuity order - [[nodiscard]] Standard_EXPORT static GeomAbs_Shape MaxContinuity( + //! @param[in] theEdge edge definition to match + //! @param[in] theFace face definition to match + //! @return matching coedge id, or invalid if the edge/face pair has no active coedge + [[nodiscard]] Standard_EXPORT static BRepGraph_CoEdgeId FindCoEdgeId( const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge); + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace); + + //! Find an active coedge for the given edge-face use and preferred orientation. + //! @param[in] theGraph source graph + //! @param[in] theEdge edge definition to match + //! @param[in] theFace face definition to match + //! @param[in] theOrientation preferred coedge orientation + //! @return exact orientation match when present; otherwise the first active coedge on the + //! edge-face pair; invalid if there is no active coedge for the edge-face pair + [[nodiscard]] Standard_EXPORT static BRepGraph_CoEdgeId FindCoEdgeId( + const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace, + const TopAbs_Orientation theOrientation); //! Returns the number of faces that reference this edge via coedges. //! @param[in] theGraph source graph @@ -295,6 +333,10 @@ public: [[nodiscard]] Standard_EXPORT static uint32_t NbFaces(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); + //! Returns the number of faces referencing the edge referenced by the coedge. + [[nodiscard]] Standard_EXPORT static uint32_t NbFaces(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + //! Returns true if the edge is shared by exactly two faces (manifold). //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier @@ -302,6 +344,10 @@ public: [[nodiscard]] Standard_EXPORT static bool IsManifold(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); + //! Returns true if the edge referenced by the coedge is manifold. + [[nodiscard]] Standard_EXPORT static bool IsManifold(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + //! Returns true if the edge belongs to exactly one face (boundary / free edge). //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier @@ -309,36 +355,18 @@ public: [[nodiscard]] Standard_EXPORT static bool IsBoundary(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); - //! Returns true if the edge has two PCurves on a face (seam/closed surface). + //! Returns true if the edge referenced by the coedge is boundary. + [[nodiscard]] Standard_EXPORT static bool IsBoundary(const BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdge); + + //! Returns true if the edge is a seam on the given face. //! @param[in] theGraph source graph //! @param[in] theEdge typed edge definition identifier //! @param[in] theFace typed face definition identifier //! @return true if the edge is a seam on this face - [[nodiscard]] Standard_EXPORT static bool IsClosedOnFace(const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace); - - //! Finds the CoEdge entity for an edge on a face. - //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @param[in] theFace typed face definition identifier - //! @return pointer to CoEdgeDef, or nullptr if not found - [[nodiscard]] Standard_EXPORT static const CoEdgeDef* FindPCurve( - const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace); - - //! Finds the CoEdge entity with specific orientation (for seam edges). - //! @param[in] theGraph source graph - //! @param[in] theEdge typed edge definition identifier - //! @param[in] theFace typed face definition identifier - //! @param[in] theOri edge orientation on the face - //! @return pointer to CoEdgeDef, or nullptr if not found - [[nodiscard]] Standard_EXPORT static const CoEdgeDef* FindPCurve( - const BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace, - const TopAbs_Orientation theOri); + [[nodiscard]] Standard_EXPORT static bool IsSeamOnFace(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace); //! Returns a CurveOnSurface adaptor built from a CoEdgeUsage and face. //! @param[in] theGraph source graph @@ -351,11 +379,10 @@ public: const BRepGraph_FaceId theFace); }; - //! @brief CoEdge (half-edge) parametric curve and polygon accessors. + //! @brief CoEdge (half-edge) parametric curve accessors. //! //! Provides PCurve retrieval, adaptor construction, UV endpoint - //! access, parameter range queries, and polygon-on-surface access - //! for coedge definitions. + //! access, and parameter range queries for coedge definitions. class CoEdge { public: @@ -415,14 +442,6 @@ public: const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge); - //! Returns the raw PCurve handle from a CoEdgeDef (no Location - UV space). - //! @param[in] theGraph source graph - //! @param[in] theCoEdge coedge entity reference - //! @return curve handle, or null handle if no PCurve - [[nodiscard]] Standard_EXPORT static const occ::handle& PCurve( - const BRepGraph& theGraph, - const CoEdgeDef& theCoEdge); - //! Returns a PCurve adaptor by coedge identifier. //! If the coedge has a stored PCurve (Curve2DRepIdx >= 0), returns it directly. //! Otherwise, for planar face surfaces, computes the PCurve on-the-fly by projecting @@ -458,32 +477,22 @@ public: [[nodiscard]] Standard_EXPORT static std::pair Range( const BRepGraph& theGraph, const BRepGraph_CoEdgeId theCoEdge); - - //! Returns true if the coedge has a polygon-on-surface representation. - //! @param[in] theGraph source graph - //! @param[in] theCoEdge typed coedge definition identifier - //! @return true if polygon exists - [[nodiscard]] Standard_EXPORT static bool HasPolygonOnSurface( - const BRepGraph& theGraph, - const BRepGraph_CoEdgeId theCoEdge); - - //! Returns the polygon-on-surface (2D) for the coedge. - //! @param[in] theGraph source graph - //! @param[in] theCoEdge typed coedge definition identifier - //! @return polygon handle, or null handle if no polygon - [[nodiscard]] Standard_EXPORT static const occ::handle& PolygonOnSurface( - const BRepGraph& theGraph, - const BRepGraph_CoEdgeId theCoEdge); }; - //! @brief Face surface, triangulation, and property accessors. + //! @brief Face surface and property accessors. //! //! Provides tolerance, natural restriction flag, surface handle - //! and adaptor access (with optional UV bounds), active triangulation - //! retrieval, and outer wire lookup. + //! and adaptor access (with optional UV bounds), and outer wire lookup. class Face { public: + //! Resolves a face reference id to a lightweight usage value. + //! @param[in] theGraph source graph + //! @param[in] theFaceRef typed face reference identifier + //! @return face usage, or invalid usage if the reference is invalid or removed + [[nodiscard]] Standard_EXPORT static FaceUsage Usage(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef); + //! Returns the face tolerance. //! @param[in] theGraph source graph //! @param[in] theFace typed face definition identifier @@ -491,12 +500,9 @@ public: [[nodiscard]] Standard_EXPORT static double Tolerance(const BRepGraph& theGraph, const BRepGraph_FaceId theFace); - //! Returns the NaturalRestriction flag. - //! @param[in] theGraph source graph - //! @param[in] theFace typed face definition identifier - //! @return true if face has natural restriction - [[nodiscard]] Standard_EXPORT static bool NaturalRestriction(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace); + //! Returns the face tolerance by face reference identifier. + [[nodiscard]] Standard_EXPORT static double Tolerance(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef); //! Returns true if the face has a surface representation. //! @param[in] theGraph source graph @@ -505,28 +511,21 @@ public: [[nodiscard]] Standard_EXPORT static bool HasSurface(const BRepGraph& theGraph, const BRepGraph_FaceId theFace); - //! Returns true if the face has an active triangulation. - //! @param[in] theGraph source graph - //! @param[in] theFace typed face definition identifier - //! @return true if triangulation exists - [[nodiscard]] Standard_EXPORT static bool HasTriangulation(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace); + //! Returns true if the face reference resolves to a face with a surface. + [[nodiscard]] Standard_EXPORT static bool HasSurface(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef); - //! Returns the outer wire reference, or nullptr if none. + //! Returns the outer wire definition id directly. //! @param[in] theGraph source graph //! @param[in] theFace typed face definition identifier - //! @return pointer to the outer WireRef, or nullptr - [[nodiscard]] Standard_EXPORT static const WireRef* OuterWire(const BRepGraph& theGraph, - const BRepGraph_FaceId theFace); + //! @return outer wire id, or invalid if the face has no wire + [[nodiscard]] Standard_EXPORT static BRepGraph_WireId OuterWire(const BRepGraph& theGraph, + const BRepGraph_FaceId theFace); - //! Returns the outer wire definition id directly (shortcut for - //! `OuterWire(...)->WireDefId`). Invalid if the face has no outer wire. - //! @param[in] theGraph source graph - //! @param[in] theFace typed face definition identifier - //! @return outer wire id - [[nodiscard]] Standard_EXPORT static BRepGraph_WireId OuterWireId( - const BRepGraph& theGraph, - const BRepGraph_FaceId theFace); + //! Returns the outer wire definition id by face reference identifier. + [[nodiscard]] Standard_EXPORT static BRepGraph_WireId OuterWire( + const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef); //! Returns the raw surface handle (definition frame, no copy). //! @param[in] theGraph source graph @@ -536,6 +535,11 @@ public: const BRepGraph& theGraph, const BRepGraph_FaceId theFace); + //! Returns the raw surface handle by face reference identifier. + [[nodiscard]] Standard_EXPORT static const occ::handle& Surface( + const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef); + //! Returns a surface adaptor in definition frame. //! @param[in] theGraph source graph //! @param[in] theFace typed face definition identifier @@ -544,6 +548,16 @@ public: const BRepGraph& theGraph, const BRepGraph_FaceId theFace); + //! Returns a surface adaptor with FaceUsage Location applied. + [[nodiscard]] Standard_EXPORT static GeomAdaptor_TransformedSurface SurfaceAdaptor( + const BRepGraph& theGraph, + const FaceUsage& theRef); + + //! Returns a surface adaptor by face reference identifier with reference Location applied. + [[nodiscard]] Standard_EXPORT static GeomAdaptor_TransformedSurface SurfaceAdaptor( + const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef); + //! Returns a surface adaptor with explicit UV bounds. //! @param[in] theGraph source graph //! @param[in] theFace typed face definition identifier @@ -560,13 +574,23 @@ public: const double theVFirst, const double theVLast); - //! Returns the active triangulation for the face (definition frame). - //! @param[in] theGraph source graph - //! @param[in] theFace typed face definition identifier - //! @return triangulation handle, or null handle if none - [[nodiscard]] Standard_EXPORT static const occ::handle& Triangulation( - const BRepGraph& theGraph, - const BRepGraph_FaceId theFace); + //! Returns a surface adaptor with explicit UV bounds and FaceUsage Location applied. + [[nodiscard]] Standard_EXPORT static GeomAdaptor_TransformedSurface SurfaceAdaptor( + const BRepGraph& theGraph, + const FaceUsage& theRef, + const double theUFirst, + const double theULast, + const double theVFirst, + const double theVLast); + + //! Returns a surface adaptor with explicit UV bounds by face reference identifier. + [[nodiscard]] Standard_EXPORT static GeomAdaptor_TransformedSurface SurfaceAdaptor( + const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef, + const double theUFirst, + const double theULast, + const double theVFirst, + const double theVLast); //! Returns the number of wire references on the face (outer + holes). //! @param[in] theGraph source graph @@ -575,8 +599,11 @@ public: [[nodiscard]] Standard_EXPORT static uint32_t NbWires(const BRepGraph& theGraph, const BRepGraph_FaceId theFace); + //! Returns the number of wire references by face reference identifier. + [[nodiscard]] Standard_EXPORT static uint32_t NbWires(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef); + //! Returns the UV parameter bounds of the face surface. - //! For faces with NaturalRestriction the bounds come directly from the surface. //! Fills out-parameters with the surface bounds; all values are set to 0.0 if //! the face has no surface. //! @param[in] theGraph source graph @@ -591,24 +618,43 @@ public: double& theUMax, double& theVMin, double& theVMax); + + //! Returns the UV parameter bounds by face reference identifier. + Standard_EXPORT static void Bounds(const BRepGraph& theGraph, + const BRepGraph_FaceRefId theFaceRef, + double& theUMin, + double& theUMax, + double& theVMin, + double& theVMax); }; //! @brief Wire property accessors. //! //! Provides wire closure, size, and ownership queries. - //! For ordered edge traversal, use BRepGraphInc_WireExplorer or access - //! the WireDef::CoEdgeRefIds vector directly via TopoView. + //! For ordered coedge traversal, use BRepGraph_CoEdgesOfWire or + //! TopoView::Wires().Relations(theWire).CoEdgeIds. class Wire { public: - //! Returns true if the wire is topologically closed. + //! Resolves a wire reference id to a lightweight usage value. + //! @param[in] theGraph source graph + //! @param[in] theWireRef typed wire reference identifier + //! @return wire usage, or invalid usage if the reference is invalid or removed + [[nodiscard]] Standard_EXPORT static WireUsage Usage(const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef); + + //! Returns true if the wire is topologically closed, derived from ordered coedge chain. //! @param[in] theGraph source graph //! @param[in] theWire typed wire definition identifier //! @return true if closed [[nodiscard]] Standard_EXPORT static bool IsClosed(const BRepGraph& theGraph, const BRepGraph_WireId theWire); - //! Number of CoEdge references in the wire (raw count: seam halves count twice, + //! Returns true if the referenced wire is topologically closed. + [[nodiscard]] Standard_EXPORT static bool IsClosed(const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef); + + //! Number of CoEdge usages in the wire (raw count: seam halves count twice, //! matching TopoDS_Iterator(wire) semantics). //! @param[in] theGraph source graph //! @param[in] theWire typed wire definition identifier @@ -616,14 +662,23 @@ public: [[nodiscard]] Standard_EXPORT static uint32_t NbCoEdges(const BRepGraph& theGraph, const BRepGraph_WireId theWire); + //! Number of CoEdge usages in the referenced wire. + [[nodiscard]] Standard_EXPORT static uint32_t NbCoEdges(const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef); + //! Number of distinct underlying edges in the wire (seam halves count once). //! @param[in] theGraph source graph //! @param[in] theWire typed wire definition identifier - //! @return number of distinct EdgeDefIds reachable from the wire's CoEdgeRefIds + //! @return number of distinct ChildEdgeIds reachable from the wire's CoEdgeIds [[nodiscard]] Standard_EXPORT static uint32_t NbDistinctEdges(const BRepGraph& theGraph, const BRepGraph_WireId theWire); - //! Returns the first owning face for this wire via the reverse-index table. + //! Number of distinct underlying edges in the referenced wire. + [[nodiscard]] Standard_EXPORT static uint32_t NbDistinctEdges( + const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef); + + //! Returns the first owning face for this wire via relation tables. //! Returns an invalid id if the wire has no owning face (free wire). //! @param[in] theGraph source graph //! @param[in] theWire typed wire definition identifier @@ -631,7 +686,12 @@ public: [[nodiscard]] Standard_EXPORT static BRepGraph_FaceId FaceOf(const BRepGraph& theGraph, const BRepGraph_WireId theWire); - //! Returns true if this wire is the outer boundary (IsOuter flag) of its owning face. + //! Returns the first owning face for the referenced wire. + [[nodiscard]] Standard_EXPORT static BRepGraph_FaceId FaceOf( + const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef); + + //! Returns true if this wire is the first active wire of its owning face. //! Scans WireRefs that reference this wire. //! Returns false for free wires (no owning face). //! @param[in] theGraph source graph @@ -639,6 +699,10 @@ public: //! @return true if outer wire [[nodiscard]] Standard_EXPORT static bool IsOuter(const BRepGraph& theGraph, const BRepGraph_WireId theWire); + + //! Returns true if the referenced wire is the outer wire of its owning face. + [[nodiscard]] Standard_EXPORT static bool IsOuter(const BRepGraph& theGraph, + const BRepGraph_WireRefId theWireRef); }; //! @brief Shell property accessors. @@ -647,83 +711,37 @@ public: class Shell { public: - //! Returns true if the shell is topologically closed (watertight boundary). + //! Resolves a shell reference id to a lightweight usage value. + //! @param[in] theGraph source graph + //! @param[in] theShellRef typed shell reference identifier + //! @return shell usage, or invalid usage if the reference is invalid or removed + [[nodiscard]] Standard_EXPORT static ShellUsage Usage(const BRepGraph& theGraph, + const BRepGraph_ShellRefId theShellRef); + + //! Returns true if the shell is topologically closed, derived from face-boundary edge + //! incidence. //! @param[in] theGraph source graph //! @param[in] theShell typed shell definition identifier //! @return true if closed [[nodiscard]] Standard_EXPORT static bool IsClosed(const BRepGraph& theGraph, const BRepGraph_ShellId theShell); + //! Returns true if the referenced shell is topologically closed. + [[nodiscard]] Standard_EXPORT static bool IsClosed(const BRepGraph& theGraph, + const BRepGraph_ShellRefId theShellRef); + //! Returns the number of face references in the shell. //! @param[in] theGraph source graph //! @param[in] theShell typed shell definition identifier //! @return number of face entries (including removed) [[nodiscard]] Standard_EXPORT static uint32_t NbFaces(const BRepGraph& theGraph, const BRepGraph_ShellId theShell); + + //! Returns the number of face references in the referenced shell. + [[nodiscard]] Standard_EXPORT static uint32_t NbFaces(const BRepGraph& theGraph, + const BRepGraph_ShellRefId theShellRef); }; - //! @brief Mesh cache writes and representation creation. - //! - //! Static methods for creating mesh representations in storage and - //! writing to the mesh cache. These do NOT trigger markModified() - //! or mutation tracking -- mesh data is derived, not model data. - class Mesh - { - public: - //! Create a new TriangulationRep in storage. - //! @return typed identifier, or invalid if the handle is null - [[nodiscard]] Standard_EXPORT static BRepGraph_TriangulationRepId CreateTriangulationRep( - BRepGraph& theGraph, - const occ::handle& theTriangulation); - - //! Create a new Polygon3DRep in storage. - //! @return typed identifier, or invalid if the handle is null - [[nodiscard]] Standard_EXPORT static BRepGraph_Polygon3DRepId CreatePolygon3DRep( - BRepGraph& theGraph, - const occ::handle& thePolygon); - - //! Create a new PolygonOnTriRep in storage. - //! @return typed identifier, or invalid if polygon is null or theTriRepId is invalid - [[nodiscard]] Standard_EXPORT static BRepGraph_PolygonOnTriRepId CreatePolygonOnTriRep( - BRepGraph& theGraph, - const occ::handle& thePolygon, - const BRepGraph_TriangulationRepId theTriRepId); - - //! Append a triangulation rep to the face's cached mesh (multi-LOD support). - Standard_EXPORT static void AppendCachedTriangulation( - BRepGraph& theGraph, - const BRepGraph_FaceId theFace, - const BRepGraph_TriangulationRepId theTriRepId); - - //! Set the active triangulation index in the face's cached mesh. - Standard_EXPORT static void SetCachedActiveIndex(BRepGraph& theGraph, - const BRepGraph_FaceId theFace, - const int theActiveIndex); - - //! Clear cached mesh for a face and its coedges. - Standard_EXPORT static void ClearFaceCache(BRepGraph& theGraph, const BRepGraph_FaceId theFace); - - //! Set the polygon-3D rep in the edge's cached mesh. - Standard_EXPORT static void SetCachedPolygon3D(BRepGraph& theGraph, - const BRepGraph_EdgeId theEdge, - const BRepGraph_Polygon3DRepId thePolyRepId); - - //! Clear cached mesh for an edge. - Standard_EXPORT static void ClearEdgeCache(BRepGraph& theGraph, const BRepGraph_EdgeId theEdge); - - //! Append a polygon-on-tri rep to the coedge's cached mesh (seam edge support). - Standard_EXPORT static void AppendCachedPolygonOnTri( - BRepGraph& theGraph, - const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_PolygonOnTriRepId thePolyRepId); - - //! Set the polygon-2D rep in the coedge's cached mesh. - Standard_EXPORT static void SetCachedPolygon2D(BRepGraph& theGraph, - const BRepGraph_CoEdgeId theCoEdge, - const BRepGraph_Polygon2DRepId thePolyRepId); - }; - -private: BRepGraph_Tool() = delete; }; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.cxx index 66d4cb08dd..3dde93bfc5 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.cxx @@ -12,66 +12,29 @@ // commercial license or contractual agreement. #include + #include #include #include #include #include #include -#include #include -#include -#include - #include +#include #include +#include +#include #include +#include namespace { -constexpr int THE_TOPOVIEW_FACE_ADJACENCY_BLOCK_SIZE = 8; -constexpr int THE_TOPOVIEW_FACE_EDGE_BLOCK_SIZE = 8; -constexpr int THE_TOPOVIEW_EDGE_VERTEX_BLOCK_SIZE = 4; -constexpr int THE_TOPOVIEW_EDGE_ADJACENCY_BLOCK_SIZE = 8; -constexpr int THE_TOPOVIEW_SAME_DOMAIN_BLOCK_SIZE = 8; -constexpr int THE_TOPOVIEW_SHARED_EDGE_BLOCK_SIZE = 4; - -//! Collect unique edge IDs reachable from a face through its wire/coedge refs. -NCollection_DynamicArray collectFaceEdges( - const BRepGraph& theGraph, - const BRepGraph_FaceId theFace, - const occ::handle& theAllocator) -{ - NCollection_DynamicArray aResult(THE_TOPOVIEW_FACE_EDGE_BLOCK_SIZE, - theAllocator); - if (!theFace.IsValid(theGraph.Topo().Faces().Nb())) - { - return aResult; - } - - NCollection_Map anEdgeSet; - for (BRepGraph_DefsWireOfFace aWireIt(theGraph, theFace); aWireIt.More(); aWireIt.Next()) - { - for (BRepGraph_DefsEdgeOfWire anEdgeIt(theGraph, aWireIt.CurrentId()); anEdgeIt.More(); - anEdgeIt.Next()) - { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - if (anEdgeSet.Add(anEdgeId)) - { - aResult.Append(anEdgeId); - } - } - } - return aResult; -} - -//================================================================================================= - template -const NCollection_DynamicArray& emptyVector() +const NCollection_LinearVector& emptyVector() { - static const NCollection_DynamicArray THE_EMPTY_VECTOR; + static const NCollection_LinearVector THE_EMPTY_VECTOR; return THE_EMPTY_VECTOR; } @@ -83,14 +46,14 @@ const NCollection_DynamicArray& emptyVector() //================================================================================================= -int BRepGraph::TopoView::FaceOps::Nb() const +uint32_t BRepGraph::TopoView::FaceOps::Nb() const { return myGraph->myData->myIncStorage.NbFaces(); } //================================================================================================= -int BRepGraph::TopoView::FaceOps::NbActive() const +uint32_t BRepGraph::TopoView::FaceOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveFaces(); } @@ -105,208 +68,62 @@ const BRepGraphInc::FaceDef& BRepGraph::TopoView::FaceOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::FaceOps::Shells( +const BRepGraphInc::FaceRelations& BRepGraph::TopoView::FaceOps::Relations( const BRepGraph_FaceId theFace) const { - return myGraph->myData->myIncStorage.ReverseIndex().ShellsOfFaceRef(theFace); + return myGraph->myData->myIncStorage.FaceRelations(theFace); } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::FaceOps::Compounds( - const BRepGraph_FaceId theFace) const -{ - const NCollection_DynamicArray* aCompounds = - myGraph->myData->myIncStorage.ReverseIndex().CompoundsOfFace(theFace); - return aCompounds != nullptr ? *aCompounds : emptyVector(); -} - -//================================================================================================= - -BRepGraph_SurfaceRepId BRepGraph::TopoView::FaceOps::SurfaceRepId( +occ::handle BRepGraph::TopoView::FaceOps::Surface( const BRepGraph_FaceId theFace) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) + if (!theFace.IsValid(aStorage.NbFaces()) || aStorage.IsRemoved(theFace)) { - return BRepGraph_SurfaceRepId(); + return occ::handle(); } - const BRepGraph_SurfaceRepId aRepId = aStorage.Face(theFace).SurfaceRepId; - if (!aRepId.IsValid(aStorage.NbSurfaces()) || aStorage.SurfaceRep(aRepId).IsRemoved) + const BRepGraphInc::FaceDef& aFace = aStorage.Face(theFace); + if (!aFace.SurfaceRepId.IsValid(aStorage.NbFaceSurfaces()) + || aStorage.IsRemoved(aFace.SurfaceRepId)) { - return BRepGraph_SurfaceRepId(); + return occ::handle(); } - return aRepId; + return aStorage.FaceSurfaceRep(aFace.SurfaceRepId).Surface; } //================================================================================================= -BRepGraph_TriangulationRepId BRepGraph::TopoView::FaceOps::ActiveTriangulationRepId( +occ::handle BRepGraph::TopoView::FaceOps::ActiveTriangulation( const BRepGraph_FaceId theFace) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) + if (!theFace.IsValid(aStorage.NbFaces()) || aStorage.IsRemoved(theFace)) { - return BRepGraph_TriangulationRepId(); + return occ::handle(); } - const BRepGraph_TriangulationRepId aRepId = aStorage.Face(theFace).TriangulationRepId; - if (!aRepId.IsValid(aStorage.NbTriangulations()) || aStorage.TriangulationRep(aRepId).IsRemoved) + const BRepGraphInc::FaceDef& aFace = aStorage.Face(theFace); + if (!aFace.TriangulationRepId.IsValid(aStorage.NbFaceTriangulations()) + || aStorage.IsRemoved(aFace.TriangulationRepId)) { - return BRepGraph_TriangulationRepId(); + return occ::handle(); } - return aRepId; + return aStorage.FaceTriangulationRep(aFace.TriangulationRepId).Triangulation; } //================================================================================================= -NCollection_DynamicArray BRepGraph::TopoView::FaceOps::SameDomain( - const BRepGraph_FaceId theFace, - const occ::handle& theAllocator) const -{ - NCollection_DynamicArray aResult(THE_TOPOVIEW_SAME_DOMAIN_BLOCK_SIZE, - theAllocator); - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) - { - return aResult; - } - - const BRepGraphInc::FaceDef& aFaceDef = aStorage.Face(theFace); - if (!aFaceDef.SurfaceRepId.IsValid()) - { - return aResult; - } - - for (BRepGraph_FaceIterator aFaceIt(*myGraph); aFaceIt.More(); aFaceIt.Next()) - { - const BRepGraph_FaceId anOtherFaceId = aFaceIt.CurrentId(); - const BRepGraphInc::FaceDef& anOtherFace = aFaceIt.Current(); - if (anOtherFaceId != theFace && anOtherFace.SurfaceRepId == aFaceDef.SurfaceRepId) - { - aResult.Append(anOtherFaceId); - } - } - return aResult; -} - -//================================================================================================= - -NCollection_DynamicArray BRepGraph::TopoView::FaceOps::SharedEdges( - const BRepGraph_FaceId theFaceA, - const BRepGraph_FaceId theFaceB, - const occ::handle& theAllocator) const -{ - NCollection_DynamicArray aResult(THE_TOPOVIEW_SHARED_EDGE_BLOCK_SIZE, - theAllocator); - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theFaceA.IsValid(aStorage.NbFaces()) || !theFaceB.IsValid(aStorage.NbFaces())) - { - return aResult; - } - - const NCollection_DynamicArray aFaceAEdges = - collectFaceEdges(*myGraph, theFaceA, theAllocator); - const NCollection_DynamicArray aFaceBEdges = - collectFaceEdges(*myGraph, theFaceB, theAllocator); - NCollection_Map aFaceAEdgeSet; - NCollection_Map anAddedEdges; - - for (const BRepGraph_EdgeId& anEdgeId : aFaceAEdges) - { - aFaceAEdgeSet.Add(anEdgeId); - } - - for (const BRepGraph_EdgeId& anEdgeId : aFaceBEdges) - { - if (aFaceAEdgeSet.Contains(anEdgeId) && anAddedEdges.Add(anEdgeId)) - { - aResult.Append(anEdgeId); - } - } - return aResult; -} - -//================================================================================================= - -NCollection_DynamicArray BRepGraph::TopoView::FaceOps::Adjacent( - const BRepGraph_FaceId theFace, - const occ::handle& theAllocator) const -{ - NCollection_DynamicArray aResult(THE_TOPOVIEW_FACE_ADJACENCY_BLOCK_SIZE, - theAllocator); - NCollection_Map aFaceSet; - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) - { - return aResult; - } - - const NCollection_DynamicArray anEdges = - collectFaceEdges(*myGraph, theFace, theAllocator); - const BRepGraphInc_ReverseIndex& aRevIdx = aStorage.ReverseIndex(); - for (const BRepGraph_EdgeId& anEdgeId : anEdges) - { - const NCollection_DynamicArray* aFaces = aRevIdx.FacesOfEdge(anEdgeId); - if (aFaces == nullptr) - { - continue; - } - - for (const BRepGraph_FaceId& anAdjacentFaceId : *aFaces) - { - if (anAdjacentFaceId == theFace) - { - continue; - } - - const BRepGraphInc::FaceDef& anAdjacentFace = aStorage.Face(anAdjacentFaceId); - if (anAdjacentFace.IsRemoved) - { - continue; - } - - if (aFaceSet.Add(anAdjacentFaceId)) - { - aResult.Append(anAdjacentFaceId); - } - } - } - return aResult; -} - -//================================================================================================= - -BRepGraph_WireId BRepGraph::TopoView::FaceOps::OuterWire(const BRepGraph_FaceId theFace) const -{ - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theFace.IsValid(aStorage.NbFaces())) - { - return BRepGraph_WireId(); - } - - for (BRepGraph_RefsWireOfFace aWireIt(*myGraph, theFace); aWireIt.More(); aWireIt.Next()) - { - const BRepGraphInc::WireRef& aWireRef = myGraph->Refs().Wires().Entry(aWireIt.CurrentId()); - if (!aWireRef.IsRemoved && aWireRef.IsOuter) - { - return aWireRef.WireDefId; - } - } - return BRepGraph_WireId(); -} - -//================================================================================================= - -int BRepGraph::TopoView::EdgeOps::Nb() const +uint32_t BRepGraph::TopoView::EdgeOps::Nb() const { return myGraph->myData->myIncStorage.NbEdges(); } //================================================================================================= -int BRepGraph::TopoView::EdgeOps::NbActive() const +uint32_t BRepGraph::TopoView::EdgeOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveEdges(); } @@ -321,256 +138,93 @@ const BRepGraphInc::EdgeDef& BRepGraph::TopoView::EdgeOps::Definition( //================================================================================================= +const BRepGraphInc::EdgeRelations& BRepGraph::TopoView::EdgeOps::Relations( + const BRepGraph_EdgeId theEdge) const +{ + return myGraph->myData->myIncStorage.EdgeRelations(theEdge); +} + +//================================================================================================= + uint32_t BRepGraph::TopoView::EdgeOps::NbFaces(const BRepGraph_EdgeId theEdge) const { - return myGraph->myData->myIncStorage.ReverseIndex().NbFacesOfEdge(theEdge); + uint32_t aCount = 0; + for (BRepGraph_FacesOfEdge aFaceIt(*myGraph, theEdge); aFaceIt.More(); aFaceIt.Next()) + { + ++aCount; + } + return aCount; } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::EdgeOps::Wires( - const BRepGraph_EdgeId theEdge) const +BRepGraph_WiresOfEdge BRepGraph::TopoView::EdgeOps::WiresOf(const BRepGraph_EdgeId theEdge) const { - return myGraph->myData->myIncStorage.ReverseIndex().WiresOfEdgeRef(theEdge); + return BRepGraph_WiresOfEdge(*myGraph, theEdge); } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::EdgeOps::CoEdges( - const BRepGraph_EdgeId theEdge) const +BRepGraph_WiresOfEdge BRepGraph::TopoView::EdgeOps::WiresOf(const BRepGraph_EdgeId theEdge, + const uint32_t theStartIndex) const { - return myGraph->myData->myIncStorage.ReverseIndex().CoEdgesOfEdgeRef(theEdge); + return BRepGraph_WiresOfEdge(*myGraph, theEdge, theStartIndex); } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::EdgeOps::Faces( - const BRepGraph_EdgeId theEdge) const +BRepGraph_FacesOfEdge BRepGraph::TopoView::EdgeOps::FacesOf(const BRepGraph_EdgeId theEdge) const { - return myGraph->myData->myIncStorage.ReverseIndex().FacesOfEdgeRef(theEdge); + return BRepGraph_FacesOfEdge(*myGraph, theEdge); } //================================================================================================= -BRepGraph_Curve3DRepId BRepGraph::TopoView::EdgeOps::Curve3DRepId( +BRepGraph_FacesOfEdge BRepGraph::TopoView::EdgeOps::FacesOf(const BRepGraph_EdgeId theEdge, + const uint32_t theStartIndex) const +{ + return BRepGraph_FacesOfEdge(*myGraph, theEdge, theStartIndex); +} + +//================================================================================================= + +const NCollection_LinearVector& BRepGraph::TopoView::EdgeOps::CoEdges( const BRepGraph_EdgeId theEdge) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges())) - { - return BRepGraph_Curve3DRepId(); - } - - const BRepGraph_Curve3DRepId aRepId = aStorage.Edge(theEdge).Curve3DRepId; - if (!aRepId.IsValid(aStorage.NbCurves3D()) || aStorage.Curve3DRep(aRepId).IsRemoved) - { - return BRepGraph_Curve3DRepId(); - } - return aRepId; + return theEdge.IsValid(aStorage.NbEdges()) ? aStorage.EdgeRelations(theEdge).CoEdgeIds + : emptyVector(); } //================================================================================================= -NCollection_DynamicArray BRepGraph::TopoView::EdgeOps::Adjacent( - const BRepGraph_EdgeId theEdge, - const occ::handle& theAllocator) const -{ - NCollection_DynamicArray aResult(THE_TOPOVIEW_EDGE_ADJACENCY_BLOCK_SIZE, - theAllocator); - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges())) - { - return aResult; - } - - NCollection_DynamicArray aVertices(THE_TOPOVIEW_EDGE_VERTEX_BLOCK_SIZE, - theAllocator); - NCollection_Map aSeenVertices; - for (BRepGraph_DefsVertexOfEdge aVertexIt(*myGraph, theEdge); aVertexIt.More(); aVertexIt.Next()) - { - const BRepGraph_VertexId aVertexId = aVertexIt.CurrentId(); - if (aSeenVertices.Add(aVertexId)) - { - aVertices.Append(aVertexId); - } - } - - // Find adjacent edges via shared vertices. - const BRepGraphInc_ReverseIndex& aRevIdx = aStorage.ReverseIndex(); - NCollection_Map anEdgeSet; - for (const BRepGraph_VertexId& aVertexId : aVertices) - { - const NCollection_DynamicArray* anEdges = aRevIdx.EdgesOfVertex(aVertexId); - if (anEdges == nullptr) - { - continue; - } - - for (const BRepGraph_EdgeId& anAdjacentEdgeId : *anEdges) - { - if (anAdjacentEdgeId == theEdge) - { - continue; - } - - const BRepGraphInc::EdgeDef& anAdjacentEdge = aStorage.Edge(anAdjacentEdgeId); - if (anAdjacentEdge.IsRemoved) - { - continue; - } - - if (anEdgeSet.Add(anAdjacentEdgeId)) - { - aResult.Append(anAdjacentEdgeId); - } - } - } - return aResult; -} - -//================================================================================================= - -bool BRepGraph::TopoView::EdgeOps::IsBoundary(const BRepGraph_EdgeId theEdge) const -{ - return NbFaces(theEdge) == 1; -} - -//================================================================================================= - -bool BRepGraph::TopoView::EdgeOps::IsManifold(const BRepGraph_EdgeId theEdge) const -{ - return NbFaces(theEdge) == 2; -} - -//================================================================================================= - -const BRepGraphInc::CoEdgeDef* BRepGraph::TopoView::EdgeOps::FindPCurve( - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace) const +occ::handle BRepGraph::TopoView::EdgeOps::Curve3D(const BRepGraph_EdgeId theEdge) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges()) || !theFace.IsValid(aStorage.NbFaces())) + if (!theEdge.IsValid(aStorage.NbEdges()) || aStorage.IsRemoved(theEdge)) { - return nullptr; + return occ::handle(); } - const NCollection_DynamicArray& aCoEdges = - aStorage.ReverseIndex().CoEdgesOfEdgeRef(theEdge); - for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) + const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(theEdge); + if (!anEdge.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D()) + || aStorage.IsRemoved(anEdge.Curve3DRepId)) { - const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); - if (aCoEdge.EdgeDefId == theEdge && aCoEdge.FaceDefId == theFace) - { - return &aCoEdge; - } + return occ::handle(); } - return nullptr; + return aStorage.EdgeCurve3DRep(anEdge.Curve3DRepId).Curve; } //================================================================================================= -const BRepGraphInc::CoEdgeDef* BRepGraph::TopoView::EdgeOps::FindPCurve( - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace, - const TopAbs_Orientation theOrientation) const -{ - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges()) || !theFace.IsValid(aStorage.NbFaces())) - { - return nullptr; - } - - const NCollection_DynamicArray& aCoEdges = - aStorage.ReverseIndex().CoEdgesOfEdgeRef(theEdge); - const BRepGraphInc::CoEdgeDef* aFirstMatch = nullptr; - for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); - if (aCoEdge.EdgeDefId != theEdge || aCoEdge.FaceDefId != theFace) - { - continue; - } - if (aFirstMatch == nullptr) - { - aFirstMatch = &aCoEdge; - } - if (aCoEdge.Orientation == theOrientation) - { - return &aCoEdge; - } - } - return aFirstMatch; -} - -//================================================================================================= - -BRepGraph_CoEdgeId BRepGraph::TopoView::EdgeOps::FindCoEdgeId(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace) const -{ - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges()) || !theFace.IsValid(aStorage.NbFaces())) - { - return BRepGraph_CoEdgeId(); - } - - const NCollection_DynamicArray& aCoEdges = - aStorage.ReverseIndex().CoEdgesOfEdgeRef(theEdge); - for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); - if (aCoEdge.EdgeDefId == theEdge && aCoEdge.FaceDefId == theFace) - { - return aCoEdgeId; - } - } - return BRepGraph_CoEdgeId(); -} - -//================================================================================================= - -BRepGraph_CoEdgeId BRepGraph::TopoView::EdgeOps::FindCoEdgeId( - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace, - const TopAbs_Orientation theOrientation) const -{ - const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theEdge.IsValid(aStorage.NbEdges()) || !theFace.IsValid(aStorage.NbFaces())) - { - return BRepGraph_CoEdgeId(); - } - - const NCollection_DynamicArray& aCoEdges = - aStorage.ReverseIndex().CoEdgesOfEdgeRef(theEdge); - BRepGraph_CoEdgeId aFirstMatch; - for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); - if (aCoEdge.EdgeDefId != theEdge || aCoEdge.FaceDefId != theFace) - { - continue; - } - if (!aFirstMatch.IsValid()) - { - aFirstMatch = aCoEdgeId; - } - if (aCoEdge.Orientation == theOrientation) - { - return aCoEdgeId; - } - } - return aFirstMatch; -} - -//================================================================================================= - -int BRepGraph::TopoView::VertexOps::Nb() const +uint32_t BRepGraph::TopoView::VertexOps::Nb() const { return myGraph->myData->myIncStorage.NbVertices(); } //================================================================================================= -int BRepGraph::TopoView::VertexOps::NbActive() const +uint32_t BRepGraph::TopoView::VertexOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveVertices(); } @@ -585,22 +239,33 @@ const BRepGraphInc::VertexDef& BRepGraph::TopoView::VertexOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::VertexOps::Edges( +const BRepGraphInc::VertexRelations& BRepGraph::TopoView::VertexOps::Relations( const BRepGraph_VertexId theVertex) const { - return myGraph->myData->myIncStorage.ReverseIndex().EdgesOfVertexRef(theVertex); + return myGraph->myData->myIncStorage.VertexRelations(theVertex); } //================================================================================================= -int BRepGraph::TopoView::WireOps::Nb() const +const NCollection_LinearVector& BRepGraph::TopoView::VertexOps::Edges( + const BRepGraph_VertexId theVertex) const +{ + static const NCollection_LinearVector anEmpty; + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + return theVertex.IsValid(aStorage.NbVertices()) ? aStorage.VertexRelations(theVertex).EdgeIds + : anEmpty; +} + +//================================================================================================= + +uint32_t BRepGraph::TopoView::WireOps::Nb() const { return myGraph->myData->myIncStorage.NbWires(); } //================================================================================================= -int BRepGraph::TopoView::WireOps::NbActive() const +uint32_t BRepGraph::TopoView::WireOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveWires(); } @@ -615,22 +280,22 @@ const BRepGraphInc::WireDef& BRepGraph::TopoView::WireOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::WireOps::Faces( +const BRepGraphInc::WireRelations& BRepGraph::TopoView::WireOps::Relations( const BRepGraph_WireId theWire) const { - return myGraph->myData->myIncStorage.ReverseIndex().FacesOfWireRef(theWire); + return myGraph->myData->myIncStorage.WireRelations(theWire); } //================================================================================================= -int BRepGraph::TopoView::ShellOps::Nb() const +uint32_t BRepGraph::TopoView::ShellOps::Nb() const { return myGraph->myData->myIncStorage.NbShells(); } //================================================================================================= -int BRepGraph::TopoView::ShellOps::NbActive() const +uint32_t BRepGraph::TopoView::ShellOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveShells(); } @@ -645,32 +310,22 @@ const BRepGraphInc::ShellDef& BRepGraph::TopoView::ShellOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::ShellOps::Solids( +const BRepGraphInc::ShellRelations& BRepGraph::TopoView::ShellOps::Relations( const BRepGraph_ShellId theShell) const { - return myGraph->myData->myIncStorage.ReverseIndex().SolidsOfShellRef(theShell); + return myGraph->myData->myIncStorage.ShellRelations(theShell); } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::ShellOps::Compounds( - const BRepGraph_ShellId theShell) const -{ - const NCollection_DynamicArray* aCompounds = - myGraph->myData->myIncStorage.ReverseIndex().CompoundsOfShell(theShell); - return aCompounds != nullptr ? *aCompounds : emptyVector(); -} - -//================================================================================================= - -int BRepGraph::TopoView::SolidOps::Nb() const +uint32_t BRepGraph::TopoView::SolidOps::Nb() const { return myGraph->myData->myIncStorage.NbSolids(); } //================================================================================================= -int BRepGraph::TopoView::SolidOps::NbActive() const +uint32_t BRepGraph::TopoView::SolidOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveSolids(); } @@ -685,34 +340,22 @@ const BRepGraphInc::SolidDef& BRepGraph::TopoView::SolidOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::SolidOps::CompSolids( +const BRepGraphInc::SolidRelations& BRepGraph::TopoView::SolidOps::Relations( const BRepGraph_SolidId theSolid) const { - const NCollection_DynamicArray* aCompSolids = - myGraph->myData->myIncStorage.ReverseIndex().CompSolidsOfSolid(theSolid); - return aCompSolids != nullptr ? *aCompSolids : emptyVector(); + return myGraph->myData->myIncStorage.SolidRelations(theSolid); } //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::SolidOps::Compounds( - const BRepGraph_SolidId theSolid) const -{ - const NCollection_DynamicArray* aCompounds = - myGraph->myData->myIncStorage.ReverseIndex().CompoundsOfSolid(theSolid); - return aCompounds != nullptr ? *aCompounds : emptyVector(); -} - -//================================================================================================= - -int BRepGraph::TopoView::CoEdgeOps::Nb() const +uint32_t BRepGraph::TopoView::CoEdgeOps::Nb() const { return myGraph->myData->myIncStorage.NbCoEdges(); } //================================================================================================= -int BRepGraph::TopoView::CoEdgeOps::NbActive() const +uint32_t BRepGraph::TopoView::CoEdgeOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveCoEdges(); } @@ -727,16 +370,6 @@ const BRepGraphInc::CoEdgeDef& BRepGraph::TopoView::CoEdgeOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::CoEdgeOps::Wires( - const BRepGraph_CoEdgeId theCoEdge) const -{ - const NCollection_DynamicArray* aWires = - myGraph->myData->myIncStorage.ReverseIndex().WiresOfCoEdge(theCoEdge); - return aWires != nullptr ? *aWires : emptyVector(); -} - -//================================================================================================= - BRepGraph_EdgeId BRepGraph::TopoView::CoEdgeOps::Edge(const BRepGraph_CoEdgeId theCoEdge) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; @@ -745,16 +378,20 @@ BRepGraph_EdgeId BRepGraph::TopoView::CoEdgeOps::Edge(const BRepGraph_CoEdgeId t return BRepGraph_EdgeId(); } + if (aStorage.IsRemoved(theCoEdge)) + { + return BRepGraph_EdgeId(); + } const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(theCoEdge); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid(aStorage.NbEdges())) + if (!aCoEdge.ChildEdgeId.IsValid(aStorage.NbEdges())) { return BRepGraph_EdgeId(); } - if (aStorage.Edge(aCoEdge.EdgeDefId).IsRemoved) + if (aStorage.IsRemoved(aCoEdge.ChildEdgeId)) { return BRepGraph_EdgeId(); } - return aCoEdge.EdgeDefId; + return aCoEdge.ChildEdgeId; } //================================================================================================= @@ -767,79 +404,70 @@ BRepGraph_FaceId BRepGraph::TopoView::CoEdgeOps::Face(const BRepGraph_CoEdgeId t return BRepGraph_FaceId(); } - const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(theCoEdge); - if (aCoEdge.IsRemoved || !aCoEdge.FaceDefId.IsValid(aStorage.NbFaces())) + if (aStorage.IsRemoved(theCoEdge)) { return BRepGraph_FaceId(); } - if (aStorage.Face(aCoEdge.FaceDefId).IsRemoved) + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(theCoEdge); + if (!aCoEdge.FaceId.IsValid(aStorage.NbFaces())) { return BRepGraph_FaceId(); } - return aCoEdge.FaceDefId; + if (aStorage.IsRemoved(aCoEdge.FaceId)) + { + return BRepGraph_FaceId(); + } + return aCoEdge.FaceId; } //================================================================================================= -BRepGraph_Curve2DRepId BRepGraph::TopoView::CoEdgeOps::Curve2DRepId( - const BRepGraph_CoEdgeId theCoEdge) const +BRepGraph_WireId BRepGraph::TopoView::CoEdgeOps::Wire(const BRepGraph_CoEdgeId theCoEdge) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theCoEdge.IsValid(aStorage.NbCoEdges())) + if (!theCoEdge.IsValid(aStorage.NbCoEdges()) || aStorage.IsRemoved(theCoEdge)) { - return BRepGraph_Curve2DRepId(); + return BRepGraph_WireId(); } - const BRepGraph_Curve2DRepId aRepId = aStorage.CoEdge(theCoEdge).Curve2DRepId; - if (!aRepId.IsValid(aStorage.NbCurves2D()) || aStorage.Curve2DRep(aRepId).IsRemoved) + const BRepGraph_WireId aWireId = aStorage.CoEdge(theCoEdge).ParentWireId; + if (!aWireId.IsValid(aStorage.NbWires()) || aStorage.IsRemoved(aWireId)) { - return BRepGraph_Curve2DRepId(); + return BRepGraph_WireId(); } - return aRepId; + return aWireId; } //================================================================================================= -BRepGraph_CoEdgeId BRepGraph::TopoView::CoEdgeOps::SeamPair( +occ::handle BRepGraph::TopoView::CoEdgeOps::Curve2D( const BRepGraph_CoEdgeId theCoEdge) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; - if (!theCoEdge.IsValid(aStorage.NbCoEdges())) + if (!theCoEdge.IsValid(aStorage.NbCoEdges()) || aStorage.IsRemoved(theCoEdge)) { - return BRepGraph_CoEdgeId(); + return occ::handle(); } + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(theCoEdge); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid() || !aCoEdge.FaceDefId.IsValid()) + if (!aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + || aStorage.IsRemoved(aCoEdge.Curve2DRepId)) { - return BRepGraph_CoEdgeId(); + return occ::handle(); } - // The seam mate is the sibling CoEdge on the same face with opposite orientation. - for (BRepGraph_CoEdgesOfEdge anIt(*myGraph, myGraph->Topo().Edges().CoEdges(aCoEdge.EdgeDefId)); - anIt.More(); - anIt.Next()) - { - const BRepGraph_CoEdgeId aOther = anIt.CurrentId(); - if (aOther == theCoEdge) - continue; - const BRepGraphInc::CoEdgeDef& aOtherDef = anIt.Definition(); - if (aOtherDef.FaceDefId == aCoEdge.FaceDefId && aOtherDef.Orientation != aCoEdge.Orientation) - { - return aOther; - } - } - return BRepGraph_CoEdgeId(); + return aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId).Curve; } //================================================================================================= -int BRepGraph::TopoView::CompoundOps::Nb() const +uint32_t BRepGraph::TopoView::CompoundOps::Nb() const { return myGraph->myData->myIncStorage.NbCompounds(); } //================================================================================================= -int BRepGraph::TopoView::CompoundOps::NbActive() const +uint32_t BRepGraph::TopoView::CompoundOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveCompounds(); } @@ -854,24 +482,22 @@ const BRepGraphInc::CompoundDef& BRepGraph::TopoView::CompoundOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::CompoundOps:: - ParentCompounds(const BRepGraph_CompoundId theCompound) const +const BRepGraphInc::CompoundRelations& BRepGraph::TopoView::CompoundOps::Relations( + const BRepGraph_CompoundId theCompound) const { - const NCollection_DynamicArray* aCompounds = - myGraph->myData->myIncStorage.ReverseIndex().CompoundsOfCompound(theCompound); - return aCompounds != nullptr ? *aCompounds : emptyVector(); + return myGraph->myData->myIncStorage.CompoundRelations(theCompound); } //================================================================================================= -int BRepGraph::TopoView::CompSolidOps::Nb() const +uint32_t BRepGraph::TopoView::CompSolidOps::Nb() const { return myGraph->myData->myIncStorage.NbCompSolids(); } //================================================================================================= -int BRepGraph::TopoView::CompSolidOps::NbActive() const +uint32_t BRepGraph::TopoView::CompSolidOps::NbActive() const { return myGraph->incStorage().NbActiveCompSolids(); } @@ -886,24 +512,22 @@ const BRepGraphInc::CompSolidDef& BRepGraph::TopoView::CompSolidOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::CompSolidOps::Compounds( +const BRepGraphInc::CompSolidRelations& BRepGraph::TopoView::CompSolidOps::Relations( const BRepGraph_CompSolidId theCompSolid) const { - const NCollection_DynamicArray* aCompounds = - myGraph->myData->myIncStorage.ReverseIndex().CompoundsOfCompSolid(theCompSolid); - return aCompounds != nullptr ? *aCompounds : emptyVector(); + return myGraph->myData->myIncStorage.CompSolidRelations(theCompSolid); } //================================================================================================= -int BRepGraph::TopoView::ProductOps::Nb() const +uint32_t BRepGraph::TopoView::ProductOps::Nb() const { return myGraph->myData->myIncStorage.NbProducts(); } //================================================================================================= -int BRepGraph::TopoView::ProductOps::NbActive() const +uint32_t BRepGraph::TopoView::ProductOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveProducts(); } @@ -918,12 +542,10 @@ const BRepGraphInc::ProductDef& BRepGraph::TopoView::ProductOps::Definition( //================================================================================================= -const NCollection_DynamicArray& BRepGraph::TopoView::ProductOps::Instances( +const BRepGraphInc::ProductRelations& BRepGraph::TopoView::ProductOps::Relations( const BRepGraph_ProductId theProduct) const { - const NCollection_DynamicArray* anOccurrences = - myGraph->myData->myIncStorage.ReverseIndex().OccurrencesOfProduct(theProduct); - return anOccurrences != nullptr ? *anOccurrences : emptyVector(); + return myGraph->myData->myIncStorage.ProductRelations(theProduct); } //================================================================================================= @@ -937,35 +559,34 @@ BRepGraph_NodeId BRepGraph::TopoView::ProductOps::ShapeRoot( return BRepGraph_NodeId(); } - const BRepGraphInc::ProductDef& aProductDef = aStorage.Product(theProduct); - if (aProductDef.IsRemoved) + if (aStorage.IsRemoved(theProduct)) { return BRepGraph_NodeId(); } - - // Scan occurrences to find the first with a topology ChildDefId. - for (const BRepGraph_OccurrenceRefId& aRefId : aProductDef.OccurrenceRefIds) + // Scan occurrences to find the first with a topology ChildNodeId. + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.ProductRelations(theProduct).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); - if (aRef.IsRemoved) + if (aStorage.IsRemoved(aRefId)) { continue; } - const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.OccurrenceDefId); - if (anOccDef.IsRemoved) + const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.ChildOccurrenceId); + if (aStorage.IsRemoved(aRef.ChildOccurrenceId)) { continue; } - if (!anOccDef.ChildDefId.IsValid() - || BRepGraph_NodeId::IsAssemblyKind(anOccDef.ChildDefId.NodeKind)) + if (!anOccDef.ChildNodeId.IsValid() + || BRepGraph_NodeId::IsAssemblyKind(anOccDef.ChildNodeId.NodeKind)) { continue; } - const BRepGraphInc::BaseDef* aRoot = myGraph->Topo().Gen().TopoEntity(anOccDef.ChildDefId); - if (aRoot != nullptr && !aRoot->IsRemoved) + const BRepGraphInc::BaseDef* aRoot = myGraph->Topo().Gen().TopoEntity(anOccDef.ChildNodeId); + if (aRoot != nullptr && !anOccDef.ChildNodeId.IsRemoved(*myGraph)) { - return anOccDef.ChildDefId; + return anOccDef.ChildNodeId; } } return BRepGraph_NodeId(); @@ -979,26 +600,25 @@ bool BRepGraph::TopoView::ProductOps::IsAssembly(const BRepGraph_ProductId thePr return false; } - const BRepGraphInc::ProductDef& aProductDef = aStorage.Product(theProduct); - if (aProductDef.IsRemoved) + if (aStorage.IsRemoved(theProduct)) { return false; } - // Assembly if any active occurrence references a product child. - for (const BRepGraph_OccurrenceRefId& aRefId : aProductDef.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.ProductRelations(theProduct).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); - if (aRef.IsRemoved) + if (aStorage.IsRemoved(aRefId)) { continue; } - const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.OccurrenceDefId); - if (anOccDef.IsRemoved) + const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.ChildOccurrenceId); + if (aStorage.IsRemoved(aRef.ChildOccurrenceId)) { continue; } - if (anOccDef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) + if (anOccDef.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Product) { return true; } @@ -1016,27 +636,26 @@ bool BRepGraph::TopoView::ProductOps::IsPart(const BRepGraph_ProductId theProduc return false; } - const BRepGraphInc::ProductDef& aProductDef = aStorage.Product(theProduct); - if (aProductDef.IsRemoved) + if (aStorage.IsRemoved(theProduct)) { return false; } - // Part if any active occurrence references a topology child. - for (const BRepGraph_OccurrenceRefId& aRefId : aProductDef.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.ProductRelations(theProduct).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); - if (aRef.IsRemoved) + if (aStorage.IsRemoved(aRefId)) { continue; } - const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.OccurrenceDefId); - if (anOccDef.IsRemoved) + const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.ChildOccurrenceId); + if (aStorage.IsRemoved(aRef.ChildOccurrenceId)) { continue; } - if (anOccDef.ChildDefId.IsValid() - && !BRepGraph_NodeId::IsAssemblyKind(anOccDef.ChildDefId.NodeKind)) + if (anOccDef.ChildNodeId.IsValid() + && !BRepGraph_NodeId::IsAssemblyKind(anOccDef.ChildNodeId.NodeKind)) { return true; } @@ -1055,29 +674,28 @@ BRepGraph_NodeId BRepGraph::TopoView::ProductOps::ShapeRootNode( return BRepGraph_NodeId(); } - const BRepGraphInc::ProductDef& aProductDef = aStorage.Product(theProduct); - if (aProductDef.IsRemoved) + if (aStorage.IsRemoved(theProduct)) { return BRepGraph_NodeId(); } - - // Find the first occurrence with a topology ChildDefId. - for (const BRepGraph_OccurrenceRefId& aRefId : aProductDef.OccurrenceRefIds) + // Find the first occurrence with a topology ChildNodeId. + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.ProductRelations(theProduct).OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); - if (aRef.IsRemoved) + if (aStorage.IsRemoved(aRefId)) { continue; } - const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.OccurrenceDefId); - if (anOccDef.IsRemoved) + const BRepGraphInc::OccurrenceDef& anOccDef = aStorage.Occurrence(aRef.ChildOccurrenceId); + if (aStorage.IsRemoved(aRef.ChildOccurrenceId)) { continue; } - if (anOccDef.ChildDefId.IsValid() - && !BRepGraph_NodeId::IsAssemblyKind(anOccDef.ChildDefId.NodeKind)) + if (anOccDef.ChildNodeId.IsValid() + && !BRepGraph_NodeId::IsAssemblyKind(anOccDef.ChildNodeId.NodeKind)) { - return anOccDef.ChildDefId; + return anOccDef.ChildNodeId; } } return BRepGraph_NodeId(); @@ -1085,7 +703,7 @@ BRepGraph_NodeId BRepGraph::TopoView::ProductOps::ShapeRootNode( //================================================================================================= -int BRepGraph::TopoView::ProductOps::NbComponents(const BRepGraph_ProductId theProduct) const +uint32_t BRepGraph::TopoView::ProductOps::NbComponents(const BRepGraph_ProductId theProduct) const { const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; if (!theProduct.IsValid(aStorage.NbProducts())) @@ -1093,18 +711,16 @@ int BRepGraph::TopoView::ProductOps::NbComponents(const BRepGraph_ProductId theP return 0; } - const BRepGraphInc::ProductDef& aProductDef = aStorage.Product(theProduct); - if (aProductDef.IsRemoved) + if (aStorage.IsRemoved(theProduct)) { return 0; } - int aCount = 0; for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*myGraph, theProduct); anOccIt.More(); anOccIt.Next()) { const BRepGraphInc::OccurrenceRef& anOccRef = aStorage.OccurrenceRef(anOccIt.CurrentId()); - if (!aStorage.Occurrence(anOccRef.OccurrenceDefId).IsRemoved) + if (!aStorage.IsRemoved(anOccRef.ChildOccurrenceId)) { ++aCount; } @@ -1124,25 +740,23 @@ BRepGraph_OccurrenceId BRepGraph::TopoView::ProductOps::Component( return BRepGraph_OccurrenceId(); } - const BRepGraphInc::ProductDef& aProductDef = aStorage.Product(theProduct); - if (aProductDef.IsRemoved) + if (aStorage.IsRemoved(theProduct)) { return BRepGraph_OccurrenceId(); } - int anActiveIndex = 0; for (BRepGraph_RefsOccurrenceOfProduct anOccIt(*myGraph, theProduct); anOccIt.More(); anOccIt.Next()) { const BRepGraphInc::OccurrenceRef& anOccRef = aStorage.OccurrenceRef(anOccIt.CurrentId()); - if (aStorage.Occurrence(anOccRef.OccurrenceDefId).IsRemoved) + if (aStorage.IsRemoved(anOccRef.ChildOccurrenceId)) { continue; } if (anActiveIndex == theComponentIdx) { - return anOccRef.OccurrenceDefId; + return anOccRef.ChildOccurrenceId; } ++anActiveIndex; } @@ -1151,14 +765,14 @@ BRepGraph_OccurrenceId BRepGraph::TopoView::ProductOps::Component( //================================================================================================= -int BRepGraph::TopoView::OccurrenceOps::Nb() const +uint32_t BRepGraph::TopoView::OccurrenceOps::Nb() const { return myGraph->myData->myIncStorage.NbOccurrences(); } //================================================================================================= -int BRepGraph::TopoView::OccurrenceOps::NbActive() const +uint32_t BRepGraph::TopoView::OccurrenceOps::NbActive() const { return myGraph->myData->myIncStorage.NbActiveOccurrences(); } @@ -1173,6 +787,14 @@ const BRepGraphInc::OccurrenceDef& BRepGraph::TopoView::OccurrenceOps::Definitio //================================================================================================= +const BRepGraphInc::OccurrenceRelations& BRepGraph::TopoView::OccurrenceOps::Relations( + const BRepGraph_OccurrenceId theOccurrence) const +{ + return myGraph->myData->myIncStorage.OccurrenceRelations(theOccurrence); +} + +//================================================================================================= + BRepGraph_ProductId BRepGraph::TopoView::OccurrenceOps::Product( const BRepGraph_OccurrenceId theOccurrence) const { @@ -1182,14 +804,18 @@ BRepGraph_ProductId BRepGraph::TopoView::OccurrenceOps::Product( return BRepGraph_ProductId(); } - const BRepGraphInc::OccurrenceDef& anOccurrence = aStorage.Occurrence(theOccurrence); - if (anOccurrence.IsRemoved || !anOccurrence.ChildDefId.IsValid() - || anOccurrence.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Product) + if (aStorage.IsRemoved(theOccurrence)) { return BRepGraph_ProductId(); } - const BRepGraph_ProductId aProductId = BRepGraph_ProductId::FromNodeId(anOccurrence.ChildDefId); - if (!aProductId.IsValid(aStorage.NbProducts()) || aStorage.Product(aProductId).IsRemoved) + const BRepGraphInc::OccurrenceDef& anOccurrence = aStorage.Occurrence(theOccurrence); + if (!anOccurrence.ChildNodeId.IsValid() + || anOccurrence.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Product) + { + return BRepGraph_ProductId(); + } + const BRepGraph_ProductId aProductId = BRepGraph_ProductId::FromNodeId(anOccurrence.ChildNodeId); + if (!aProductId.IsValid(aStorage.NbProducts()) || aStorage.IsRemoved(aProductId)) { return BRepGraph_ProductId(); } @@ -1207,35 +833,23 @@ BRepGraph_ProductId BRepGraph::TopoView::OccurrenceOps::ParentProduct( return BRepGraph_ProductId(); } - const BRepGraphInc::OccurrenceDef& anOccurrence = aStorage.Occurrence(theOccurrence); - if (anOccurrence.IsRemoved) + if (aStorage.IsRemoved(theOccurrence)) { return BRepGraph_ProductId(); } - - // Find the OccurrenceRef that owns this OccurrenceDef to get ParentId. - for (BRepGraph_OccurrenceRefId aRefId = myGraph->Refs().Occurrences().StartId(); - aRefId < myGraph->Refs().Occurrences().EndId(); - ++aRefId) + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.OccurrenceRelations(theOccurrence).ParentOccurrenceRefIds) { - const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); - if (aRef.IsRemoved || aRef.OccurrenceDefId != theOccurrence) + if (!aRefId.IsValid(aStorage.NbOccurrenceRefs()) || aStorage.IsRemoved(aRefId)) { continue; } - - if (aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Product) + const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); + if (aRef.ParentProductId.IsValid(aStorage.NbProducts()) + && !aStorage.IsRemoved(aRef.ParentProductId)) { - return BRepGraph_ProductId(); + return aRef.ParentProductId; } - - const BRepGraph_ProductId aParentProduct = BRepGraph_ProductId::FromNodeId(aRef.ParentId); - if (!aParentProduct.IsValid(aStorage.NbProducts()) - || aStorage.Product(aParentProduct).IsRemoved) - { - return BRepGraph_ProductId(); - } - return aParentProduct; } return BRepGraph_ProductId(); } @@ -1251,87 +865,63 @@ TopLoc_Location BRepGraph::TopoView::OccurrenceOps::OccurrenceLocation( return TopLoc_Location(); } - // Placement is now on OccurrenceRef::LocalLocation. - // Find the OccurrenceRef that owns this OccurrenceDef. - for (BRepGraph_OccurrenceRefId aRefId = myGraph->Refs().Occurrences().StartId(); - aRefId < myGraph->Refs().Occurrences().EndId(); - ++aRefId) + for (const BRepGraph_OccurrenceRefId& aRefId : + aStorage.OccurrenceRelations(theOccurrence).ParentOccurrenceRefIds) { - const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); - if (!aRef.IsRemoved && aRef.OccurrenceDefId == theOccurrence) + if (!aRefId.IsValid(aStorage.NbOccurrenceRefs()) || aStorage.IsRemoved(aRefId)) { - return aRef.LocalLocation; + continue; } + const BRepGraphInc::OccurrenceRef& aRef = aStorage.OccurrenceRef(aRefId); + return aRef.LocalLocation; } return TopLoc_Location(); } //================================================================================================= -int BRepGraph::TopoView::GeometryOps::NbSurfaces() const +uint32_t BRepGraph::TopoView::GeometryOps::NbFaceSurfaces() const { - return myGraph->myData->myIncStorage.NbSurfaces(); + return myGraph->myData->myIncStorage.NbFaceSurfaces(); } //================================================================================================= -int BRepGraph::TopoView::GeometryOps::NbCurves3D() const +uint32_t BRepGraph::TopoView::GeometryOps::NbEdgeCurves3D() const { - return myGraph->myData->myIncStorage.NbCurves3D(); + return myGraph->myData->myIncStorage.NbEdgeCurves3D(); } //================================================================================================= -int BRepGraph::TopoView::GeometryOps::NbCurves2D() const +uint32_t BRepGraph::TopoView::GeometryOps::NbCoEdgeCurves2D() const { - return myGraph->myData->myIncStorage.NbCurves2D(); + return myGraph->myData->myIncStorage.NbCoEdgeCurves2D(); } //================================================================================================= -int BRepGraph::TopoView::GeometryOps::NbActiveSurfaces() const +uint32_t BRepGraph::TopoView::GeometryOps::NbActiveFaceSurfaces() const { - return myGraph->myData->myIncStorage.NbActiveSurfaces(); + return myGraph->myData->myIncStorage.NbActiveFaceSurfaces(); } //================================================================================================= -int BRepGraph::TopoView::GeometryOps::NbActiveCurves3D() const +uint32_t BRepGraph::TopoView::GeometryOps::NbActiveEdgeCurves3D() const { - return myGraph->myData->myIncStorage.NbActiveCurves3D(); + return myGraph->myData->myIncStorage.NbActiveEdgeCurves3D(); } //================================================================================================= -int BRepGraph::TopoView::GeometryOps::NbActiveCurves2D() const +uint32_t BRepGraph::TopoView::GeometryOps::NbActiveCoEdgeCurves2D() const { - return myGraph->myData->myIncStorage.NbActiveCurves2D(); + return myGraph->myData->myIncStorage.NbActiveCoEdgeCurves2D(); } //================================================================================================= -const BRepGraphInc::SurfaceRep& BRepGraph::TopoView::GeometryOps::SurfaceRep( - const BRepGraph_SurfaceRepId theRep) const -{ - return myGraph->myData->myIncStorage.SurfaceRep(theRep); -} - -//================================================================================================= - -const BRepGraphInc::Curve3DRep& BRepGraph::TopoView::GeometryOps::Curve3DRep( - const BRepGraph_Curve3DRepId theRep) const -{ - return myGraph->myData->myIncStorage.Curve3DRep(theRep); -} - -//================================================================================================= - -const BRepGraphInc::Curve2DRep& BRepGraph::TopoView::GeometryOps::Curve2DRep( - const BRepGraph_Curve2DRepId theRep) const -{ - return myGraph->myData->myIncStorage.Curve2DRep(theRep); -} - const BRepGraphInc::BaseDef* BRepGraph::TopoView::GenOps::TopoEntity( const BRepGraph_NodeId theId) const { @@ -1340,7 +930,37 @@ const BRepGraphInc::BaseDef* BRepGraph::TopoView::GenOps::TopoEntity( //================================================================================================= -int BRepGraph::TopoView::GenOps::NbNodes() const +const NCollection_LinearVector& BRepGraph::TopoView::GenOps::CompoundRefIds( + const BRepGraph_NodeId theChild) const +{ + return myGraph->myData->myIncStorage.CompoundRefsOfNode(theChild); +} + +//================================================================================================= + +const NCollection_LinearVector& BRepGraph::TopoView::GenOps:: + OccurrenceRefIds(const BRepGraph_NodeId theChild) const +{ + return myGraph->myData->myIncStorage.OccurrenceRefsOfNode(theChild); +} + +//================================================================================================= + +bool BRepGraph::TopoView::GenOps::HasCompoundParents(const BRepGraph_NodeId theNode) const +{ + return myGraph->myData->myIncStorage.HasCompoundParent(theNode); +} + +//================================================================================================= + +bool BRepGraph::TopoView::GenOps::HasOccurrenceParents(const BRepGraph_NodeId theNode) const +{ + return myGraph->myData->myIncStorage.HasOccurrenceParent(theNode); +} + +//================================================================================================= + +uint32_t BRepGraph::TopoView::GenOps::NbNodes() const { const BRepGraphInc_Storage& aS = myGraph->myData->myIncStorage; return aS.NbSolids() + aS.NbShells() + aS.NbFaces() + aS.NbWires() + aS.NbCoEdges() + aS.NbEdges() @@ -1349,12 +969,60 @@ int BRepGraph::TopoView::GenOps::NbNodes() const } //================================================================================================= -bool BRepGraph::TopoView::GenOps::IsRemoved(const BRepGraph_NodeId theNode) const + +uint32_t BRepGraph::TopoView::GenOps::Nb(const BRepGraph_NodeId::Kind theKind) const { - const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr) + const BRepGraphInc_Storage& aS = myGraph->myData->myIncStorage; + switch (theKind) + { + case BRepGraph_NodeId::Kind::Vertex: + return aS.NbVertices(); + case BRepGraph_NodeId::Kind::Edge: + return aS.NbEdges(); + case BRepGraph_NodeId::Kind::CoEdge: + return aS.NbCoEdges(); + case BRepGraph_NodeId::Kind::Wire: + return aS.NbWires(); + case BRepGraph_NodeId::Kind::Face: + return aS.NbFaces(); + case BRepGraph_NodeId::Kind::Shell: + return aS.NbShells(); + case BRepGraph_NodeId::Kind::Solid: + return aS.NbSolids(); + case BRepGraph_NodeId::Kind::Compound: + return aS.NbCompounds(); + case BRepGraph_NodeId::Kind::CompSolid: + return aS.NbCompSolids(); + case BRepGraph_NodeId::Kind::Product: + return aS.NbProducts(); + case BRepGraph_NodeId::Kind::Occurrence: + return aS.NbOccurrences(); + } + + return 0; +} + +//================================================================================================= + +bool BRepGraph::TopoView::GenOps::IsValid(const BRepGraph_NodeId theNode) const +{ + if (!theNode.IsValid()) { return false; } - return aDef->IsRemoved; + return theNode.Index < Nb(theNode.NodeKind); +} + +//================================================================================================= + +bool BRepGraph::TopoView::GenOps::IsActive(const BRepGraph_NodeId theNode) const +{ + return IsValid(theNode) && !theNode.IsRemoved(*myGraph); +} + +//================================================================================================= + +bool BRepGraph::TopoView::GenOps::IsRemoved(const BRepGraph_NodeId theNode) const +{ + return !IsValid(theNode) || theNode.IsRemoved(*myGraph); } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.hxx index 7fd1815c08..9425e6d62d 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TopoView.hxx @@ -15,14 +15,22 @@ #define _BRepGraph_TopoView_HeaderFile #include -#include +#include #include +#include #include #include #include +#include #include class Adaptor3d_CurveOnSurface; +class Geom_Surface; +class Geom_Curve; +class Geom2d_Curve; +class Poly_Triangulation; +class BRepGraph_FacesOfEdge; +class BRepGraph_WiresOfEdge; //! @brief Unified read-only view over topology definitions, adjacency, and representations. //! @@ -44,9 +52,10 @@ class Adaptor3d_CurveOnSurface; //! reference IDs (BRepGraph_FaceRefId, BRepGraph_ShellRefId) and return //! reference-entry structs carrying per-use orientation and location. //! -//! Reverse-index accessors return const references to internal vectors. The -//! reference itself is always valid; the returned vector may be empty when the -//! queried entity has no parents of that kind. +//! Relations() is the single entry point for ordered topology relation containers. +//! Adjacency helpers return references only into existing relation storage. +//! Ref-owned parent links are exposed as reference-id containers; callers resolve +//! parent definitions through RefsView entries or typed iterators. class BRepGraph::TopoView { public: @@ -54,324 +63,436 @@ public: class FaceOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of face definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) face definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid face identifier for iteration. [[nodiscard]] BRepGraph_FaceId StartId() const { return BRepGraph_FaceId::Start(); } + //! Return the past-the-end face identifier (one past the last valid id). [[nodiscard]] BRepGraph_FaceId EndId() const { return BRepGraph_FaceId(Nb()); } + //! Return the definition struct for the given face. + //! @param[in] theFace typed face identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::FaceDef& Definition( const BRepGraph_FaceId theFace) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Shells( + + //! Return the relation struct (adjacency lists) for the given face. + //! @param[in] theFace typed face identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::FaceRelations& Relations( const BRepGraph_FaceId theFace) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Compounds( + + //! Return the surface handle for the given face. + //! May be null if the face has no surface representation. + //! @param[in] theFace typed face identifier + [[nodiscard]] Standard_EXPORT occ::handle Surface( + const BRepGraph_FaceId theFace) const; + + //! Return the active triangulation for the given face. + //! Returns null if the face has no triangulation or it has been invalidated. + //! @param[in] theFace typed face identifier + [[nodiscard]] Standard_EXPORT occ::handle ActiveTriangulation( const BRepGraph_FaceId theFace) const; - [[nodiscard]] Standard_EXPORT BRepGraph_SurfaceRepId - SurfaceRepId(const BRepGraph_FaceId theFace) const; - [[nodiscard]] Standard_EXPORT BRepGraph_TriangulationRepId - ActiveTriangulationRepId(const BRepGraph_FaceId theFace) const; - [[nodiscard]] Standard_EXPORT NCollection_DynamicArray SameDomain( - const BRepGraph_FaceId theFace, - const occ::handle& theAllocator) const; - [[nodiscard]] Standard_EXPORT NCollection_DynamicArray SharedEdges( - const BRepGraph_FaceId theFaceA, - const BRepGraph_FaceId theFaceB, - const occ::handle& theAllocator) const; - [[nodiscard]] Standard_EXPORT NCollection_DynamicArray Adjacent( - const BRepGraph_FaceId theFace, - const occ::handle& theAllocator) const; - [[nodiscard]] Standard_EXPORT BRepGraph_WireId OuterWire(const BRepGraph_FaceId theFace) const; private: friend class TopoView; - explicit FaceOps(const BRepGraph* theGraph) + explicit FaceOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Edge-oriented topology queries. class EdgeOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of edge definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) edge definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid edge identifier for iteration. [[nodiscard]] BRepGraph_EdgeId StartId() const { return BRepGraph_EdgeId::Start(); } + //! Return the past-the-end edge identifier (one past the last valid id). [[nodiscard]] BRepGraph_EdgeId EndId() const { return BRepGraph_EdgeId(Nb()); } + //! Return the definition struct for the given edge. + //! @param[in] theEdge typed edge identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::EdgeDef& Definition( const BRepGraph_EdgeId theEdge) const; + + //! Return the relation struct (adjacency lists) for the given edge. + //! @param[in] theEdge typed edge identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::EdgeRelations& Relations( + const BRepGraph_EdgeId theEdge) const; + + //! Return the number of active faces adjacent to the given edge through active coedges. + //! @param[in] theEdge typed edge definition identifier + //! @return active adjacent face count [[nodiscard]] Standard_EXPORT uint32_t NbFaces(const BRepGraph_EdgeId theEdge) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Wires( - const BRepGraph_EdgeId theEdge) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& CoEdges( - const BRepGraph_EdgeId theEdge) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Faces( - const BRepGraph_EdgeId theEdge) const; - [[nodiscard]] Standard_EXPORT BRepGraph_Curve3DRepId - Curve3DRepId(const BRepGraph_EdgeId theEdge) const; - [[nodiscard]] Standard_EXPORT NCollection_DynamicArray Adjacent( - const BRepGraph_EdgeId theEdge, - const occ::handle& theAllocator) const; - [[nodiscard]] Standard_EXPORT bool IsBoundary(const BRepGraph_EdgeId theEdge) const; - [[nodiscard]] Standard_EXPORT bool IsManifold(const BRepGraph_EdgeId theEdge) const; - [[nodiscard]] Standard_EXPORT const BRepGraphInc::CoEdgeDef* FindPCurve( - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace) const; - [[nodiscard]] Standard_EXPORT const BRepGraphInc::CoEdgeDef* FindPCurve( - const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace, - const TopAbs_Orientation theOrientation) const; - //! Find the CoEdgeId for a given (edge, face) pair. - //! @param[in] theEdge edge to look up - //! @param[in] theFace face the edge belongs to - //! @return CoEdgeId, or invalid if no coedge binds this edge to this face - [[nodiscard]] Standard_EXPORT BRepGraph_CoEdgeId - FindCoEdgeId(const BRepGraph_EdgeId theEdge, const BRepGraph_FaceId theFace) const; + //! Return an iterator over active wires that reference the given edge through active coedges. + //! @param[in] theEdge typed edge definition identifier + //! @return iterator positioned at the first active wire, or at end if none exists + [[nodiscard]] Standard_EXPORT BRepGraph_WiresOfEdge + WiresOf(const BRepGraph_EdgeId theEdge) const; - //! Find the CoEdgeId for a given (edge, face, orientation) triple. - //! Useful for seam edges where two coedges share the same face. - //! @param[in] theEdge edge to look up - //! @param[in] theFace face the edge belongs to - //! @param[in] theOrientation orientation to match (FORWARD or REVERSED) - //! @return CoEdgeId, or invalid if no coedge matches - [[nodiscard]] Standard_EXPORT BRepGraph_CoEdgeId - FindCoEdgeId(const BRepGraph_EdgeId theEdge, - const BRepGraph_FaceId theFace, - const TopAbs_Orientation theOrientation) const; + //! Return an iterator over active wires from a stored edge-coedge relation index. + //! @param[in] theEdge typed edge definition identifier + //! @param[in] theStartIndex zero-based index in EdgeRelations::CoEdgeIds to resume from + //! @return iterator positioned at the first active wire at or after theStartIndex + [[nodiscard]] Standard_EXPORT BRepGraph_WiresOfEdge WiresOf(const BRepGraph_EdgeId theEdge, + const uint32_t theStartIndex) const; + + //! Return an iterator over active faces adjacent to the given edge through active coedges. + //! @param[in] theEdge typed edge definition identifier + //! @return iterator positioned at the first active face, or at end if none exists + [[nodiscard]] Standard_EXPORT BRepGraph_FacesOfEdge + FacesOf(const BRepGraph_EdgeId theEdge) const; + + //! Return an iterator over active faces from a stored edge-coedge relation index. + //! @param[in] theEdge typed edge definition identifier + //! @param[in] theStartIndex zero-based index in EdgeRelations::CoEdgeIds to resume from + //! @return iterator positioned at the first active face at or after theStartIndex + [[nodiscard]] Standard_EXPORT BRepGraph_FacesOfEdge FacesOf(const BRepGraph_EdgeId theEdge, + const uint32_t theStartIndex) const; + + //! Return the coedges that reference the given edge. + //! @param[in] theEdge typed edge identifier + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& CoEdges( + const BRepGraph_EdgeId theEdge) const; + + //! Return the 3D curve handle for the given edge. + //! May be null if the edge has no 3D curve representation. + //! @param[in] theEdge typed edge identifier + [[nodiscard]] Standard_EXPORT occ::handle Curve3D( + const BRepGraph_EdgeId theEdge) const; private: friend class TopoView; - explicit EdgeOps(const BRepGraph* theGraph) + explicit EdgeOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Vertex-oriented topology queries. class VertexOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of vertex definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) vertex definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid vertex identifier for iteration. [[nodiscard]] BRepGraph_VertexId StartId() const { return BRepGraph_VertexId::Start(); } + //! Return the past-the-end vertex identifier (one past the last valid id). [[nodiscard]] BRepGraph_VertexId EndId() const { return BRepGraph_VertexId(Nb()); } + //! Return the definition struct for the given vertex. + //! @param[in] theVertex typed vertex identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::VertexDef& Definition( const BRepGraph_VertexId theVertex) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Edges( + + //! Return the relation struct (adjacency lists) for the given vertex. + //! @param[in] theVertex typed vertex identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::VertexRelations& Relations( + const BRepGraph_VertexId theVertex) const; + + //! Return the edges incident to the given vertex. + //! @param[in] theVertex typed vertex identifier + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& Edges( const BRepGraph_VertexId theVertex) const; private: friend class TopoView; - explicit VertexOps(const BRepGraph* theGraph) + explicit VertexOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Wire-oriented topology queries. class WireOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of wire definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) wire definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid wire identifier for iteration. [[nodiscard]] BRepGraph_WireId StartId() const { return BRepGraph_WireId::Start(); } + //! Return the past-the-end wire identifier (one past the last valid id). [[nodiscard]] BRepGraph_WireId EndId() const { return BRepGraph_WireId(Nb()); } + //! Return the definition struct for the given wire. + //! @param[in] theWire typed wire identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::WireDef& Definition( const BRepGraph_WireId theWire) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Faces( + + //! Return the relation struct (adjacency lists) for the given wire. + //! @param[in] theWire typed wire identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::WireRelations& Relations( const BRepGraph_WireId theWire) const; private: friend class TopoView; - explicit WireOps(const BRepGraph* theGraph) + explicit WireOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Shell-oriented topology queries. class ShellOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of shell definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) shell definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid shell identifier for iteration. [[nodiscard]] BRepGraph_ShellId StartId() const { return BRepGraph_ShellId::Start(); } + //! Return the past-the-end shell identifier (one past the last valid id). [[nodiscard]] BRepGraph_ShellId EndId() const { return BRepGraph_ShellId(Nb()); } + //! Return the definition struct for the given shell. + //! @param[in] theShell typed shell identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::ShellDef& Definition( const BRepGraph_ShellId theShell) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Solids( - const BRepGraph_ShellId theShell) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Compounds( + + //! Return the relation struct (adjacency lists) for the given shell. + //! @param[in] theShell typed shell identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::ShellRelations& Relations( const BRepGraph_ShellId theShell) const; private: friend class TopoView; - explicit ShellOps(const BRepGraph* theGraph) + explicit ShellOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Solid-oriented topology queries. class SolidOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of solid definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) solid definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid solid identifier for iteration. [[nodiscard]] BRepGraph_SolidId StartId() const { return BRepGraph_SolidId::Start(); } + //! Return the past-the-end solid identifier (one past the last valid id). [[nodiscard]] BRepGraph_SolidId EndId() const { return BRepGraph_SolidId(Nb()); } + //! Return the definition struct for the given solid. + //! @param[in] theSolid typed solid identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::SolidDef& Definition( const BRepGraph_SolidId theSolid) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& CompSolids( - const BRepGraph_SolidId theSolid) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Compounds( + + //! Return the relation struct (adjacency lists) for the given solid. + //! @param[in] theSolid typed solid identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::SolidRelations& Relations( const BRepGraph_SolidId theSolid) const; private: friend class TopoView; - explicit SolidOps(const BRepGraph* theGraph) + explicit SolidOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Coedge-oriented topology and representation queries. class CoEdgeOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of coedge definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) coedge definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid coedge identifier for iteration. [[nodiscard]] BRepGraph_CoEdgeId StartId() const { return BRepGraph_CoEdgeId::Start(); } + //! Return the past-the-end coedge identifier (one past the last valid id). [[nodiscard]] BRepGraph_CoEdgeId EndId() const { return BRepGraph_CoEdgeId(Nb()); } + //! Return the definition struct for the given coedge. + //! @param[in] theCoEdge typed coedge identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::CoEdgeDef& Definition( const BRepGraph_CoEdgeId theCoEdge) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Wires( - const BRepGraph_CoEdgeId theCoEdge) const; + + //! Return the parent edge of the given coedge. + //! @param[in] theCoEdge typed coedge identifier [[nodiscard]] Standard_EXPORT BRepGraph_EdgeId Edge(const BRepGraph_CoEdgeId theCoEdge) const; + + //! Return the face that owns the given coedge. + //! @param[in] theCoEdge typed coedge identifier [[nodiscard]] Standard_EXPORT BRepGraph_FaceId Face(const BRepGraph_CoEdgeId theCoEdge) const; - [[nodiscard]] Standard_EXPORT BRepGraph_Curve2DRepId - Curve2DRepId(const BRepGraph_CoEdgeId theCoEdge) const; - [[nodiscard]] Standard_EXPORT BRepGraph_CoEdgeId - SeamPair(const BRepGraph_CoEdgeId theCoEdge) const; + + //! Return the wire that owns the given coedge. + //! @param[in] theCoEdge typed coedge identifier + [[nodiscard]] Standard_EXPORT BRepGraph_WireId Wire(const BRepGraph_CoEdgeId theCoEdge) const; + + //! Return the 2D PCurve handle for the given coedge. + //! May be null if the coedge has no PCurve representation. + //! @param[in] theCoEdge typed coedge identifier + [[nodiscard]] Standard_EXPORT occ::handle Curve2D( + const BRepGraph_CoEdgeId theCoEdge) const; private: friend class TopoView; - explicit CoEdgeOps(const BRepGraph* theGraph) + explicit CoEdgeOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Compound-oriented topology queries. class CompoundOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of compound definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) compound definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid compound identifier for iteration. [[nodiscard]] BRepGraph_CompoundId StartId() const { return BRepGraph_CompoundId::Start(); } + //! Return the past-the-end compound identifier (one past the last valid id). [[nodiscard]] BRepGraph_CompoundId EndId() const { return BRepGraph_CompoundId(Nb()); } + //! Return the definition struct for the given compound. + //! @param[in] theCompound typed compound identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::CompoundDef& Definition( const BRepGraph_CompoundId theCompound) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& - ParentCompounds(const BRepGraph_CompoundId theCompound) const; + + //! Return the relation struct (child references) for the given compound. + //! @param[in] theCompound typed compound identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::CompoundRelations& Relations( + const BRepGraph_CompoundId theCompound) const; private: friend class TopoView; - explicit CompoundOps(const BRepGraph* theGraph) + explicit CompoundOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Comp-solid oriented topology queries. class CompSolidOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of comp-solid definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) comp-solid definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid comp-solid identifier for iteration. [[nodiscard]] BRepGraph_CompSolidId StartId() const { return BRepGraph_CompSolidId::Start(); } + //! Return the past-the-end comp-solid identifier (one past the last valid id). [[nodiscard]] BRepGraph_CompSolidId EndId() const { return BRepGraph_CompSolidId(Nb()); } + //! Return the definition struct for the given comp-solid. + //! @param[in] theCompSolid typed comp-solid identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::CompSolidDef& Definition( const BRepGraph_CompSolidId theCompSolid) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Compounds( + + //! Return the relation struct (child solids) for the given comp-solid. + //! @param[in] theCompSolid typed comp-solid identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::CompSolidRelations& Relations( const BRepGraph_CompSolidId theCompSolid) const; private: friend class TopoView; - explicit CompSolidOps(const BRepGraph* theGraph) + explicit CompSolidOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Product-oriented raw assembly queries. class ProductOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of product definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) product definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid product identifier for iteration. [[nodiscard]] BRepGraph_ProductId StartId() const { return BRepGraph_ProductId::Start(); } + //! Return the past-the-end product identifier (one past the last valid id). [[nodiscard]] BRepGraph_ProductId EndId() const { return BRepGraph_ProductId(Nb()); } + //! Return the definition struct for the given product. + //! @param[in] theProduct typed product definition identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::ProductDef& Definition( const BRepGraph_ProductId theProduct) const; - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& Instances( + + //! Return the relation struct (occurrences, shape root) for the given product. + //! @param[in] theProduct typed product definition identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::ProductRelations& Relations( const BRepGraph_ProductId theProduct) const; + + //! Return the topology root NodeId for the given product. + //! For assemblies (no topology root) returns an invalid NodeId. + //! @param[in] theProduct typed product definition identifier [[nodiscard]] Standard_EXPORT BRepGraph_NodeId ShapeRoot(const BRepGraph_ProductId theProduct) const; @@ -391,7 +512,7 @@ public: //! Number of active child occurrences of a product. //! @param[in] theProduct typed product definition identifier - [[nodiscard]] Standard_EXPORT int NbComponents(const BRepGraph_ProductId theProduct) const; + [[nodiscard]] Standard_EXPORT uint32_t NbComponents(const BRepGraph_ProductId theProduct) const; //! Return the i-th active child occurrence identifier of a product. //! @param[in] theProduct typed product definition identifier @@ -402,29 +523,47 @@ public: private: friend class TopoView; - explicit ProductOps(const BRepGraph* theGraph) + explicit ProductOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Occurrence-oriented raw assembly queries. class OccurrenceOps { public: - [[nodiscard]] Standard_EXPORT int Nb() const; - [[nodiscard]] Standard_EXPORT int NbActive() const; + //! Return the total number of occurrence definitions (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb() const; + //! Return the number of active (non-soft-removed) occurrence definitions. + [[nodiscard]] Standard_EXPORT uint32_t NbActive() const; + + //! Return the first valid occurrence identifier for iteration. [[nodiscard]] BRepGraph_OccurrenceId StartId() const { return BRepGraph_OccurrenceId::Start(); } + //! Return the past-the-end occurrence identifier (one past the last valid id). [[nodiscard]] BRepGraph_OccurrenceId EndId() const { return BRepGraph_OccurrenceId(Nb()); } + //! Return the definition struct for the given occurrence. + //! @param[in] theOccurrence typed occurrence identifier [[nodiscard]] Standard_EXPORT const BRepGraphInc::OccurrenceDef& Definition( const BRepGraph_OccurrenceId theOccurrence) const; + + //! Return the relation struct (parent, placement) for the given occurrence. + //! @param[in] theOccurrence typed occurrence identifier + [[nodiscard]] Standard_EXPORT const BRepGraphInc::OccurrenceRelations& Relations( + const BRepGraph_OccurrenceId theOccurrence) const; + + //! Return the product that this occurrence instantiates. + //! @param[in] theOccurrence typed occurrence identifier [[nodiscard]] Standard_EXPORT BRepGraph_ProductId Product(const BRepGraph_OccurrenceId theOccurrence) const; + + //! Return the parent product that owns this occurrence. + //! @param[in] theOccurrence typed occurrence identifier [[nodiscard]] Standard_EXPORT BRepGraph_ProductId ParentProduct(const BRepGraph_OccurrenceId theOccurrence) const; //! Return the local placement of an occurrence (OccurrenceRef::LocalLocation). @@ -438,62 +577,100 @@ public: private: friend class TopoView; - explicit OccurrenceOps(const BRepGraph* theGraph) + explicit OccurrenceOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Generic topology and assembly count / meta queries. class GenOps { public: + //! Return the base definition pointer for any topology node (polymorphic). + //! Returns null if the node id is invalid, out of range, or soft-removed. + //! @param[in] theId node identifier (any kind) [[nodiscard]] Standard_EXPORT const BRepGraphInc::BaseDef* TopoEntity( const BRepGraph_NodeId theId) const; - [[nodiscard]] Standard_EXPORT int NbNodes() const; + + //! Return the compound (child) reference identifiers that point to the given node. + //! @param[in] theChild node identifier + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& + CompoundRefIds(const BRepGraph_NodeId theChild) const; + + //! Return the occurrence reference identifiers that point to the given node. + //! @param[in] theChild node identifier + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& + OccurrenceRefIds(const BRepGraph_NodeId theChild) const; + + //! True if the node has at least one compound parent. + //! @param[in] theNode node identifier + [[nodiscard]] Standard_EXPORT bool HasCompoundParents(const BRepGraph_NodeId theNode) const; + + //! True if the node has at least one occurrence parent. + //! @param[in] theNode node identifier + [[nodiscard]] Standard_EXPORT bool HasOccurrenceParents(const BRepGraph_NodeId theNode) const; + + //! Return the total number of nodes across all topology kinds (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t NbNodes() const; + + //! Return the number of node definitions of the specified kind (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t Nb(const BRepGraph_NodeId::Kind theKind) const; + + //! Return true if the node id kind and index are within storage bounds. + [[nodiscard]] Standard_EXPORT bool IsValid(const BRepGraph_NodeId theNode) const; + + //! Return true if the node id is valid and not soft-removed. + [[nodiscard]] Standard_EXPORT bool IsActive(const BRepGraph_NodeId theNode) const; + + //! Return true if the given node is invalid or has been soft-removed. + //! @param[in] theNode node identifier [[nodiscard]] Standard_EXPORT bool IsRemoved(const BRepGraph_NodeId theNode) const; private: friend class TopoView; - explicit GenOps(const BRepGraph* theGraph) + explicit GenOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! @brief Analytic geometry representation queries. class GeometryOps { public: - [[nodiscard]] Standard_EXPORT int NbSurfaces() const; - [[nodiscard]] Standard_EXPORT int NbCurves3D() const; - [[nodiscard]] Standard_EXPORT int NbCurves2D() const; + //! Return the total number of face surface representations (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t NbFaceSurfaces() const; - [[nodiscard]] Standard_EXPORT int NbActiveSurfaces() const; - [[nodiscard]] Standard_EXPORT int NbActiveCurves3D() const; - [[nodiscard]] Standard_EXPORT int NbActiveCurves2D() const; + //! Return the total number of edge 3D curve representations (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t NbEdgeCurves3D() const; - [[nodiscard]] Standard_EXPORT const BRepGraphInc::SurfaceRep& SurfaceRep( - const BRepGraph_SurfaceRepId theRep) const; - [[nodiscard]] Standard_EXPORT const BRepGraphInc::Curve3DRep& Curve3DRep( - const BRepGraph_Curve3DRepId theRep) const; - [[nodiscard]] Standard_EXPORT const BRepGraphInc::Curve2DRep& Curve2DRep( - const BRepGraph_Curve2DRepId theRep) const; + //! Return the total number of coedge 2D PCurve representations (including soft-removed). + [[nodiscard]] Standard_EXPORT uint32_t NbCoEdgeCurves2D() const; + + //! Return the number of active (non-soft-removed) face surface representations. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveFaceSurfaces() const; + + //! Return the number of active (non-soft-removed) edge 3D curve representations. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveEdgeCurves3D() const; + + //! Return the number of active (non-soft-removed) coedge 2D PCurve representations. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveCoEdgeCurves2D() const; private: friend class TopoView; - explicit GeometryOps(const BRepGraph* theGraph) + explicit GeometryOps(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; //! Grouped face-oriented queries. @@ -537,10 +714,10 @@ public: //! Representations use dense 0-based indexing. Iterate through grouped accessors: //! @code - //! for (int i = 0; i < aGraph.Topo().Geometry().NbSurfaces(); ++i) + //! for (BRepGraph_FaceId aFId = aGraph.Topo().Faces().StartId(); + //! aFId < aGraph.Topo().Faces().EndId(); aFId = aFId.Next()) //! { - //! const BRepGraphInc::SurfaceRep& aRep = - //! aGraph.Topo().Geometry().SurfaceRep(BRepGraph_SurfaceRepId(i)); + //! occ::handle aSurf = aGraph.Topo().Faces().Surface(aFId); //! } //! @endcode @@ -549,7 +726,7 @@ private: friend struct BRepGraph_Data; friend class BRepGraph_Tool; - explicit TopoView(const BRepGraph* theGraph) + explicit TopoView(BRepGraph* theGraph) : myGraph(theGraph), myFaces(theGraph), myEdges(theGraph), @@ -567,20 +744,20 @@ private: { } - const BRepGraph* myGraph; - FaceOps myFaces; - EdgeOps myEdges; - VertexOps myVertices; - WireOps myWires; - ShellOps myShells; - SolidOps mySolids; - CoEdgeOps myCoEdges; - CompoundOps myCompounds; - CompSolidOps myCompSolids; - ProductOps myProducts; - OccurrenceOps myOccurrences; - GenOps myGen; - GeometryOps myGeometry; + BRepGraph* myGraph; + FaceOps myFaces; + EdgeOps myEdges; + VertexOps myVertices; + WireOps myWires; + ShellOps myShells; + SolidOps mySolids; + CoEdgeOps myCoEdges; + CompoundOps myCompounds; + CompSolidOps myCompSolids; + ProductOps myProducts; + OccurrenceOps myOccurrences; + GenOps myGen; + GeometryOps myGeometry; }; #endif // _BRepGraph_TopoView_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.cxx index bce22b3be3..c9e78b53f0 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.cxx @@ -23,11 +23,13 @@ #include #include #include +#include +#include #include #include -#include #include #include +#include #include #include #include @@ -35,6 +37,50 @@ namespace { +struct GraphCounts +{ + uint32_t NbVertices = 0; + uint32_t NbEdges = 0; + uint32_t NbCoEdges = 0; + uint32_t NbFaces = 0; + uint32_t NbProducts = 0; +}; + +GraphCounts graphCounts(const BRepGraph& theGraph) +{ + GraphCounts aCounts; + aCounts.NbVertices = theGraph.Topo().Vertices().Nb(); + aCounts.NbEdges = theGraph.Topo().Edges().Nb(); + aCounts.NbCoEdges = theGraph.Topo().CoEdges().Nb(); + aCounts.NbFaces = theGraph.Topo().Faces().Nb(); + aCounts.NbProducts = theGraph.Topo().Products().Nb(); + return aCounts; +} + +bool isInCopiedRange(const BRepGraph_VertexId theId, const GraphCounts& theCounts) +{ + return theId.Index >= theCounts.NbVertices; +} + +bool isInCopiedRange(const BRepGraph_EdgeId theId, const GraphCounts& theCounts) +{ + return theId.Index >= theCounts.NbEdges; +} + +bool isInCopiedRange(const BRepGraph_CoEdgeId theId, const GraphCounts& theCounts) +{ + return theId.Index >= theCounts.NbCoEdges; +} + +bool isInCopiedRange(const BRepGraph_FaceId theId, const GraphCounts& theCounts) +{ + return theId.Index >= theCounts.NbFaces; +} + +bool isInCopiedRange(const BRepGraph_ProductId theId, const GraphCounts& theCounts) +{ + return theId.Index >= theCounts.NbProducts; +} template void forEachRootProduct(BRepGraph& theGraph, ApplyProductFn&& theApplyProduct) @@ -43,7 +89,24 @@ void forEachRootProduct(BRepGraph& theGraph, ApplyProductFn&& theApplyProduct) for (BRepGraph_RootProductIterator aRootIt(theGraph); aRootIt.More(); aRootIt.Next()) { const BRepGraph_ProductId aProductId = aRootIt.Current(); - if (aProductId.IsValid(aProducts.Nb()) && !aProducts.Definition(aProductId).IsRemoved) + if (aProductId.IsValid(aProducts.Nb()) && !aProductId.IsRemoved(theGraph)) + { + theApplyProduct(aProductId); + } + } +} + +template +void forEachRootProductInCopiedRange(BRepGraph& theGraph, + const GraphCounts& theCounts, + ApplyProductFn&& theApplyProduct) +{ + const BRepGraph::TopoView::ProductOps& aProducts = theGraph.Topo().Products(); + for (BRepGraph_RootProductIterator aRootIt(theGraph); aRootIt.More(); aRootIt.Next()) + { + const BRepGraph_ProductId aProductId = aRootIt.Current(); + if (aProductId.IsValid(aProducts.Nb()) && isInCopiedRange(aProductId, theCounts) + && !aProductId.IsRemoved(theGraph)) { theApplyProduct(aProductId); } @@ -118,88 +181,105 @@ void transformPolygon3D(const occ::handle& thePoly, const gp_Trs } } -//! Copy mesh representations from theSource into theDest, optionally transforming them. +void transformExistingMesh(BRepGraph& theGraph, + const GraphCounts& theCounts, + const gp_Trsf& theTrsf, + const bool theDoTransform) +{ + for (BRepGraph_FaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + if (!isInCopiedRange(aFaceId, theCounts)) + { + continue; + } + const occ::handle& aTri = + theGraph.Mesh().Persistent().Faces().Triangulation(aFaceId); + if (!aTri.IsNull() && theDoTransform) + { + transformTriangulation(aTri, theTrsf, BRepGraph_Tool::Face::Surface(theGraph, aFaceId)); + } + theGraph.Mesh().Editor().Faces().Clear(aFaceId); + } + + for (BRepGraph_EdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) + { + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + if (!isInCopiedRange(anEdgeId, theCounts)) + { + continue; + } + const occ::handle& aPoly3D = + theGraph.Mesh().Persistent().Edges().Polygon3D(anEdgeId); + if (!aPoly3D.IsNull() && theDoTransform) + { + transformPolygon3D(aPoly3D, theTrsf); + } + theGraph.Mesh().Editor().Edges().Clear(anEdgeId); + } + + for (BRepGraph_CoEdgeIterator aCoEdgeIt(theGraph); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + if (!isInCopiedRange(aCoEdgeId, theCounts)) + { + continue; + } + const occ::handle& aPolyOnTri = + theGraph.Mesh().Persistent().CoEdges().PolygonOnTriangulation(aCoEdgeId); + if (!aPolyOnTri.IsNull() && theDoTransform) + { + aPolyOnTri->Deflection(aPolyOnTri->Deflection() * std::abs(theTrsf.ScaleFactor())); + } + theGraph.Mesh().Editor().CoEdges().Clear(aCoEdgeId); + } +} + +//! Copy persistent mesh representations from theSource into theDest, optionally transforming them. //! -//! Handles all three mesh layers: -//! - Triangulations (persistent FaceDef entry + all cached LOD entries). -//! - Polygon3D reps (edge mesh cache). -//! - PolygonOnTriangulation reps (coedge mesh cache); TriangulationRepId references -//! are remapped to point to the newly created triangulation reps. +//! Runtime mesh cache is intentionally not copied or transformed. //! //! @param[in] theSource source graph whose face IDs and mesh cache are iterated //! @param[in] theDest destination graph (mesh storage is populated by this call) //! @param[in] theTrsf transformation to apply (ignored when !theDoTransform) //! @param[in] theDoTransform true: copy + transform; false: copy only (location-only mode) -//! @param[in] thePolySource graph whose poly storage is used for TriangulationRep lookups; -//! when nullptr, theSource is used. Pass the original source graph -//! when theSource is a CopyNode result whose poly storage is empty -//! yet its FaceMeshEntry TriangulationRepIds still reference the -//! original graph's poly storage. -//! @param[in] theSourceCache raw mesh cache storage to read LOD entries from, bypassing the -//! OwnGen freshness check. Use when theSource's face OwnGens may -//! have been bumped since the LOD entries were stored (e.g. by -//! Mut guards in the geometry-transform pass that runs before this -//! call). When nullptr, theSource.Mesh().Faces().CachedMesh() is -//! used (includes the freshness check). -void applyMeshCopy(const BRepGraph& theSource, - BRepGraph& theDest, - const gp_Trsf& theTrsf, - const bool theDoTransform, - const BRepGraph* thePolySource = nullptr, - const BRepGraph_MeshCacheStorage* theSourceCache = nullptr) +//! @param[in] thePolySource graph whose persistent poly storage is used for mesh rep lookups; +//! when nullptr, theSource is used. +void applyMeshCopy(const BRepGraph& theSource, + BRepGraph& theDest, + const gp_Trsf& theTrsf, + const bool theDoTransform, + const BRepGraph* thePolySource = nullptr) { - const BRepGraph::MeshView::PolyOps& aSrcPoly = - thePolySource != nullptr ? thePolySource->Mesh().Poly() : theSource.Mesh().Poly(); + const BRepGraph* aPolySrc = thePolySource != nullptr ? thePolySource : &theSource; - // Old TriangulationRepId.Index -> new TriangulationRepId in theDest storage. - NCollection_DataMap aTriRepMap; + // Dedup maps: source handle -> transformed handle. + NCollection_FlatDataMap, occ::handle> + aTriRepMap; + NCollection_FlatDataMap, occ::handle> + aPolygon3DRepMap; + NCollection_FlatDataMap, occ::handle> + aPolygon2DRepMap; + NCollection_FlatDataMap, + occ::handle> + aPolygonOnTriRepMap; - // -- Triangulations (faces) -- + // -- Persistent triangulations (faces) -- for (BRepGraph_FaceIterator aFaceIt(theSource); aFaceIt.More(); aFaceIt.Next()) { const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - // Snapshot LOD cache entries. When theSourceCache is provided it is used directly - // (bypassing the OwnGen freshness check) because the geometry-transform Mut guards - // may have bumped the face OwnGen since the entries were stored, making them appear - // stale to CachedMesh() even though the triangulation data is still intact. - // The snapshot is also required for the self-aliasing case (theSource == theDest): - // ClearFaceCache below would invalidate a raw pointer into the same storage. - NCollection_DynamicArray aSrcLODs; - int aSrcActiveIdx = -1; - { - const BRepGraph_MeshCache::FaceMeshEntry* aSrcEntry = - theSourceCache != nullptr ? theSourceCache->FindFaceMesh(aFaceId) - : theSource.Mesh().Faces().CachedMesh(aFaceId); - if (aSrcEntry != nullptr && aSrcEntry->IsPresent()) - { - for (int i = 0; i < aSrcEntry->TriangulationRepIds.Length(); ++i) - { - aSrcLODs.Append(aSrcEntry->TriangulationRepIds.Value(i)); - } - aSrcActiveIdx = aSrcEntry->ActiveTriangulationIndex; - } - } + theDest.Mesh().Editor().Faces().Clear(aFaceId); - BRepGraph_Tool::Mesh::ClearFaceCache(theDest, aFaceId); - - // Helper lambda: copy one triangulation from source, optionally transform, register in dest. auto copyOneTri = - [&](const BRepGraph_TriangulationRepId aSrcRepId) -> BRepGraph_TriangulationRepId { - if (!aSrcRepId.IsValid(aSrcPoly.NbTriangulations())) - { - return BRepGraph_TriangulationRepId(); - } - if (const BRepGraph_TriangulationRepId* aExisting = aTriRepMap.Seek(aSrcRepId.Index)) - { - return *aExisting; - } - - const occ::handle& aSrcTri = - aSrcPoly.TriangulationRep(aSrcRepId).Triangulation; + [&](const occ::handle& aSrcTri) -> occ::handle { if (aSrcTri.IsNull()) { - return BRepGraph_TriangulationRepId(); + return {}; + } + if (const occ::handle* aExisting = aTriRepMap.Seek(aSrcTri)) + { + return *aExisting; } occ::handle aNewTri = aSrcTri->Copy(); @@ -208,56 +288,28 @@ void applyMeshCopy(const BRepGraph& theSource, const occ::handle& aSurf = BRepGraph_Tool::Face::Surface(theDest, aFaceId); transformTriangulation(aNewTri, theTrsf, aSurf); } - const BRepGraph_TriangulationRepId aNewRepId = - BRepGraph_Tool::Mesh::CreateTriangulationRep(theDest, aNewTri); - aTriRepMap.Bind(aSrcRepId.Index, aNewRepId); - return aNewRepId; + aTriRepMap.Bind(aSrcTri, aNewTri); + return aNewTri; }; - // Persistent triangulation stored on FaceDef (not in the cache; safe to read after clear). - const BRepGraph_TriangulationRepId aSrcPersistId = - theSource.Topo().Faces().Definition(aFaceId).TriangulationRepId; - const BRepGraph_TriangulationRepId aNewPersistId = copyOneTri(aSrcPersistId); - theDest.Editor().Faces().SetTriangulationRep(aFaceId, aNewPersistId); - - // Cached LOD entries (MeshLayer) - use the pre-clear snapshot. - for (int i = 0; i < aSrcLODs.Length(); ++i) + const occ::handle& aSrcPersistTri = + aPolySrc->Mesh().Persistent().Faces().Triangulation(aFaceId); + const occ::handle aNewPersistTri = copyOneTri(aSrcPersistTri); + if (!aNewPersistTri.IsNull()) { - const BRepGraph_TriangulationRepId aNewRepId = copyOneTri(aSrcLODs.Value(i)); - if (aNewRepId.IsValid()) - { - BRepGraph_Tool::Mesh::AppendCachedTriangulation(theDest, aFaceId, aNewRepId); - } - } - if (aSrcActiveIdx >= 0) - { - BRepGraph_Tool::Mesh::SetCachedActiveIndex(theDest, aFaceId, aSrcActiveIdx); + theDest.Editor().Faces().SetPersistentTriangulation(aFaceId, aNewPersistTri); } } - // -- Polygon3D (edges) -- - for (BRepGraph_EdgeIterator anEdgeIt(theSource); anEdgeIt.More(); anEdgeIt.Next()) - { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - BRepGraph_Tool::Mesh::ClearEdgeCache(theDest, anEdgeId); - - const BRepGraph_MeshCache::EdgeMeshEntry* aSrcEdge = - theSource.Mesh().Edges().CachedMesh(anEdgeId); - if (aSrcEdge == nullptr || !aSrcEdge->IsPresent()) - { - continue; - } - - const BRepGraph_Polygon3DRepId aSrcPolyRepId = aSrcEdge->Polygon3DRepId; - if (!aSrcPolyRepId.IsValid(aSrcPoly.NbPolygons3D())) - { - continue; - } - - const occ::handle& aSrcPoly3D = aSrcPoly.Polygon3DRep(aSrcPolyRepId).Polygon; + auto copyOnePolygon3D = + [&](const occ::handle& aSrcPoly3D) -> occ::handle { if (aSrcPoly3D.IsNull()) { - continue; + return {}; + } + if (const occ::handle* aExisting = aPolygon3DRepMap.Seek(aSrcPoly3D)) + { + return *aExisting; } occ::handle aNewPoly3D = aSrcPoly3D->Copy(); @@ -266,71 +318,106 @@ void applyMeshCopy(const BRepGraph& theSource, transformPolygon3D(aNewPoly3D, theTrsf); } - const BRepGraph_Polygon3DRepId aNewRepId = - BRepGraph_Tool::Mesh::CreatePolygon3DRep(theDest, aNewPoly3D); - BRepGraph_Tool::Mesh::SetCachedPolygon3D(theDest, anEdgeId, aNewRepId); + aPolygon3DRepMap.Bind(aSrcPoly3D, aNewPoly3D); + return aNewPoly3D; + }; + + // -- Persistent Polygon3D (edges) -- + for (BRepGraph_EdgeIterator anEdgeIt(theSource); anEdgeIt.More(); anEdgeIt.Next()) + { + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + const occ::handle& aSrcPersistentPoly3D = + aPolySrc->Mesh().Persistent().Edges().Polygon3D(anEdgeId); + if (!aSrcPersistentPoly3D.IsNull()) + { + const occ::handle aNewPersistentPoly3D = + copyOnePolygon3D(aSrcPersistentPoly3D); + if (!aNewPersistentPoly3D.IsNull()) + { + theDest.Editor().Edges().SetPersistentPolygon3D(anEdgeId, aNewPersistentPoly3D); + } + } + theDest.Mesh().Editor().Edges().Clear(anEdgeId); } - // -- PolygonOnTriangulation (coedges) -- + auto copyOnePolygon2D = + [&](const occ::handle& aSrcPoly2D) -> occ::handle { + if (aSrcPoly2D.IsNull()) + { + return {}; + } + if (const occ::handle* aExisting = aPolygon2DRepMap.Seek(aSrcPoly2D)) + { + return *aExisting; + } + + occ::handle aNewPoly2D = aSrcPoly2D->Copy(); + aPolygon2DRepMap.Bind(aSrcPoly2D, aNewPoly2D); + return aNewPoly2D; + }; + + auto copyOnePolygonOnTri = + [&](const occ::handle& aSrcPoly, + const BRepGraph_CoEdgeId) -> occ::handle { + if (aSrcPoly.IsNull()) + { + return {}; + } + if (const occ::handle* aExisting = + aPolygonOnTriRepMap.Seek(aSrcPoly)) + { + return *aExisting; + } + + occ::handle aNewPoly = aSrcPoly->Copy(); + if (theDoTransform) + { + aNewPoly->Deflection(aNewPoly->Deflection() * std::abs(theTrsf.ScaleFactor())); + } + + aPolygonOnTriRepMap.Bind(aSrcPoly, aNewPoly); + return aNewPoly; + }; + + // -- Persistent coedge mesh (Polygon2D + PolygonOnTriangulation) -- for (BRepGraph_CoEdgeIterator aCoEdgeIt(theSource); aCoEdgeIt.More(); aCoEdgeIt.Next()) { const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); - const BRepGraph_MeshCache::CoEdgeMeshEntry* aSrcCoEdge = - theSource.Mesh().CoEdges().CachedMesh(aCoEdgeId); - if (aSrcCoEdge == nullptr || !aSrcCoEdge->IsPresent()) + // Persistent coedge mesh (Polygon2D + PolygonOnTri). + const occ::handle& aSrcPersistPoly2D = + aPolySrc->Mesh().Persistent().CoEdges().PolygonOnSurface(aCoEdgeId); + const occ::handle& aSrcPersistPolyOnTri = + aPolySrc->Mesh().Persistent().CoEdges().PolygonOnTriangulation(aCoEdgeId); + if (!aSrcPersistPoly2D.IsNull() || !aSrcPersistPolyOnTri.IsNull()) { - continue; - } - - for (int i = 0; i < aSrcCoEdge->PolygonOnTriRepIds.Length(); ++i) - { - const BRepGraph_PolygonOnTriRepId aSrcRepId = aSrcCoEdge->PolygonOnTriRepIds.Value(i); - if (!aSrcRepId.IsValid(aSrcPoly.NbPolygonsOnTri())) + if (!aSrcPersistPoly2D.IsNull()) { - continue; + theDest.Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, + copyOnePolygon2D(aSrcPersistPoly2D)); } - - const BRepGraphInc::PolygonOnTriRep& aSrcRep = aSrcPoly.PolygonOnTriRep(aSrcRepId); - if (aSrcRep.Polygon.IsNull()) + if (!aSrcPersistPolyOnTri.IsNull()) { - continue; + theDest.Editor().CoEdges().SetPersistentPolygonOnTri( + aCoEdgeId, + copyOnePolygonOnTri(aSrcPersistPolyOnTri, aCoEdgeId)); } - - occ::handle aNewPoly = aSrcRep.Polygon->Copy(); - if (theDoTransform) - { - aNewPoly->Deflection(aNewPoly->Deflection() * std::abs(theTrsf.ScaleFactor())); - } - - // Remap triangulation reference to the newly created dest rep. - BRepGraph_TriangulationRepId aNewTriRepId; - if (const BRepGraph_TriangulationRepId* aFound = - aTriRepMap.Seek(aSrcRep.TriangulationRepId.Index)) - { - aNewTriRepId = *aFound; - } - - const BRepGraph_PolygonOnTriRepId aNewRepId = - BRepGraph_Tool::Mesh::CreatePolygonOnTriRep(theDest, aNewPoly, aNewTriRepId); - BRepGraph_Tool::Mesh::AppendCachedPolygonOnTri(theDest, aCoEdgeId, aNewRepId); } + theDest.Mesh().Editor().CoEdges().Clear(aCoEdgeId); } } -//! Geometry-level transform: deep-copy geometry is already done by Copy, -//! so transform geometry handles in-place. +//! Geometry-level transform: create new transformed geometry handles and set them +//! via the editor. Never mutates shared handles in-place. //! Matches BRepBuilderAPI_Transform with theCopyGeom=true. //! When theCopyMesh=true triangulations are copied and transformed; //! otherwise they are invalidated. //! @param[in] thePolySource forwarded to applyMeshCopy; see that function's documentation. -//! @param[in] theSourceCache forwarded to applyMeshCopy; see that function's documentation. -void applyGeometryTransform(const BRepGraph& theSource, - BRepGraph& theGraph, - const gp_Trsf& theTrsf, - const bool theCopyMesh, - const BRepGraph* thePolySource = nullptr, - const BRepGraph_MeshCacheStorage* theSourceCache = nullptr) +void applyGeometryTransform(const BRepGraph& theSource, + BRepGraph& theGraph, + const gp_Trsf& theTrsf, + const bool theCopyMesh, + const BRepGraph* thePolySource = nullptr) { // Transform absolute vertex points. for (BRepGraph_VertexIterator aVertexIt(theGraph); aVertexIt.More(); aVertexIt.Next()) @@ -341,59 +428,128 @@ void applyGeometryTransform(const BRepGraph& theSource, theGraph.Editor().Vertices().SetPoint(aVertId, aPnt); } - // Transform surface geometry handles directly on surface reps. - // Use visited set to avoid transforming shared handles twice. - NCollection_Map aVisitedSurfReps; + // Transform surface geometry: create new handles via Transformed() and set via editor. for (BRepGraph_FaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - BRepGraph_MutGuard aFace = theGraph.Editor().Faces().Mut(aFaceId); - if (BRepGraph_Tool::Face::HasSurface(theGraph, aFaceId) - && aVisitedSurfReps.Add(aFace->SurfaceRepId)) + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + if (BRepGraph_Tool::Face::HasSurface(theGraph, aFaceId)) { const occ::handle& aSurf = BRepGraph_Tool::Face::Surface(theGraph, aFaceId); if (!aSurf.IsNull()) { - aSurf->Transform(theTrsf); - aFace.MarkDirty(); + theGraph.Editor().Faces().SetSurface( + aFaceId, + occ::down_cast(aSurf->Transformed(theTrsf))); } } if (!theCopyMesh) { // Invalidate triangulations - meshes are no longer valid after geometry transform. - // Use the MutGuard overload so the clear is folded into the open guard scope and - // the destructor fires a single markModified for the face. - theGraph.Editor().Faces().SetTriangulationRep(aFace, BRepGraph_TriangulationRepId()); - BRepGraph_Tool::Mesh::ClearFaceCache(theGraph, aFaceId); + theGraph.Editor().Faces().ClearPersistentTriangulation(aFaceId); + theGraph.Mesh().Editor().Faces().Clear(aFaceId); } } - // Transform curve geometry handles directly on curve reps. - NCollection_Map aVisitedCurveReps; + // Transform curve geometry: create new handles via Transformed() and set via editor. for (BRepGraph_EdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - BRepGraph_MutGuard anEdge = theGraph.Editor().Edges().Mut(anEdgeId); - if (BRepGraph_Tool::Edge::HasCurve(theGraph, anEdgeId) - && aVisitedCurveReps.Add(anEdge->Curve3DRepId)) + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + if (BRepGraph_Tool::Edge::HasCurve(theGraph, anEdgeId)) { const occ::handle& aCurve3d = BRepGraph_Tool::Edge::Curve(theGraph, anEdgeId); if (!aCurve3d.IsNull()) { - aCurve3d->Transform(theTrsf); - anEdge.MarkDirty(); + const auto [aFirst, aLast] = BRepGraph_Tool::Edge::Range(theGraph, anEdgeId); + theGraph.Editor().Edges().SetCurve( + anEdgeId, + occ::down_cast(aCurve3d->Transformed(theTrsf)), + aFirst, + aLast); } } if (!theCopyMesh) { - BRepGraph_Tool::Mesh::ClearEdgeCache(theGraph, anEdgeId); + theGraph.Mesh().Editor().Edges().Clear(anEdgeId); } } // PCurves are in UV space - they are not affected by 3D transforms. if (theCopyMesh) { - applyMeshCopy(theSource, theGraph, theTrsf, true, thePolySource, theSourceCache); + applyMeshCopy(theSource, theGraph, theTrsf, true, thePolySource); + } +} + +void applyGeometryTransformInCopiedRange(BRepGraph& theGraph, + const GraphCounts& theCounts, + const gp_Trsf& theTrsf, + const bool theCopyMesh) +{ + for (BRepGraph_VertexIterator aVertexIt(theGraph); aVertexIt.More(); aVertexIt.Next()) + { + const BRepGraph_VertexId aVertId = aVertexIt.CurrentId(); + if (!isInCopiedRange(aVertId, theCounts)) + { + continue; + } + gp_Pnt aPnt = theGraph.Topo().Vertices().Definition(aVertId).Point; + aPnt.Transform(theTrsf); + theGraph.Editor().Vertices().SetPoint(aVertId, aPnt); + } + + for (BRepGraph_FaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + if (!isInCopiedRange(aFaceId, theCounts)) + { + continue; + } + if (BRepGraph_Tool::Face::HasSurface(theGraph, aFaceId)) + { + const occ::handle& aSurf = BRepGraph_Tool::Face::Surface(theGraph, aFaceId); + if (!aSurf.IsNull()) + { + theGraph.Editor().Faces().SetSurface( + aFaceId, + occ::down_cast(aSurf->Transformed(theTrsf))); + } + } + if (!theCopyMesh) + { + theGraph.Editor().Faces().ClearPersistentTriangulation(aFaceId); + theGraph.Mesh().Editor().Faces().Clear(aFaceId); + } + } + + for (BRepGraph_EdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) + { + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + if (!isInCopiedRange(anEdgeId, theCounts)) + { + continue; + } + if (BRepGraph_Tool::Edge::HasCurve(theGraph, anEdgeId)) + { + const occ::handle& aCurve3d = BRepGraph_Tool::Edge::Curve(theGraph, anEdgeId); + if (!aCurve3d.IsNull()) + { + const auto [aFirst, aLast] = BRepGraph_Tool::Edge::Range(theGraph, anEdgeId); + theGraph.Editor().Edges().SetCurve( + anEdgeId, + occ::down_cast(aCurve3d->Transformed(theTrsf)), + aFirst, + aLast); + } + } + if (!theCopyMesh) + { + theGraph.Mesh().Editor().Edges().Clear(anEdgeId); + } + } + + if (theCopyMesh) + { + transformExistingMesh(theGraph, theCounts, theTrsf, true); } } @@ -408,11 +564,10 @@ void BRepGraph_Transform::applyLocationTransform(BRepGraph& theGraph, const gp_T // Compose the transform into all top-level OccurrenceRefs of each root product. // This handles both parts (shape-root occurrence) and assemblies (sub-product occurrences). forEachRootProduct(theGraph, [&](const BRepGraph_ProductId theProductId) { - const BRepGraphInc::ProductDef& aProduct = theGraph.Topo().Products().Definition(theProductId); - for (const BRepGraph_OccurrenceRefId& aRefId : aProduct.OccurrenceRefIds) + for (const BRepGraph_OccurrenceRefId& aRefId : + theGraph.Topo().Products().Relations(theProductId).OccurrenceRefIds) { - const BRepGraphInc::OccurrenceRef& aOccRef = theGraph.Refs().Occurrences().Entry(aRefId); - if (aOccRef.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aRefId)) { continue; } @@ -425,173 +580,222 @@ void BRepGraph_Transform::applyLocationTransform(BRepGraph& theGraph, const gp_T //================================================================================================= -BRepGraph BRepGraph_Transform::Perform(const BRepGraph& theGraph, - const gp_Trsf& theTrsf, - const bool theCopyGeom, - const bool theCopyMesh) +void applyLocationTransformInCopiedRange(BRepGraph& theGraph, + const GraphCounts& theCounts, + const gp_Trsf& theTrsf) { - if (!theGraph.IsDone()) - { - return BRepGraph(); - } - - // Determine if we need geometry-level modification (like BRepBuilderAPI_Transform). - const bool useGeomModif = - theCopyGeom || theTrsf.IsNegative() - || (std::abs(std::abs(theTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec()); - - if (useGeomModif) - { - // Geometry-level: deep-copy then transform geometry handles in-place. - BRepGraph aResult = BRepGraph_Copy::Perform(theGraph, true); - if (!aResult.IsDone()) + const TopLoc_Location aLoc(theTrsf); + forEachRootProductInCopiedRange(theGraph, theCounts, [&](const BRepGraph_ProductId theProductId) { + for (const BRepGraph_OccurrenceRefId& aRefId : + theGraph.Topo().Products().Relations(theProductId).OccurrenceRefIds) { - return aResult; + if (theGraph.Refs().Gen().IsRemoved(aRefId)) + { + continue; + } + BRepGraph_MutGuard aMutRef = + theGraph.Editor().Occurrences().MutRef(aRefId); + theGraph.Editor().Occurrences().SetRefLocalLocation(aMutRef, aLoc * aMutRef->LocalLocation); } - - applyGeometryTransform(theGraph, aResult, theTrsf, theCopyMesh); - return aResult; - } - - // Root-level (location-only): light-copy, multiply transform into node locations. - // Matches BRepBuilderAPI_Transform with theCopyGeom=false (shape.Moved(trsf)). - BRepGraph aResult = BRepGraph_Copy::Perform(theGraph, false); - if (!aResult.IsDone()) - { - return aResult; - } - - applyLocationTransform(aResult, theTrsf); - if (theCopyMesh) - { - applyMeshCopy(theGraph, aResult, theTrsf, false); - } - return aResult; + }); } //================================================================================================= -BRepGraph BRepGraph_Transform::TransformNode(const BRepGraph& theGraph, - const BRepGraph_NodeId theNodeId, - const gp_Trsf& theTrsf, - const bool theCopyGeom, - const bool theCopyMesh) +bool BRepGraph_Transform::Perform(const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + const gp_Trsf& theTrsf, + const BRepGraph_Copy::GeomPolicy theGeomPolicy, + const BRepGraph_Copy::MeshPolicy theMeshPolicy) { - if (!theGraph.IsDone()) + if (theSourceGraph.IsEmpty()) { - return BRepGraph(); + return false; } + // Determine if the transform requires geometry-level modification + // (matching BRepBuilderAPI_Transform semantics). + const bool isNegative = theTrsf.IsNegative(); + const bool isScaled = + std::abs(std::abs(theTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec(); + const bool useGeomModif = - theCopyGeom || theTrsf.IsNegative() - || (std::abs(std::abs(theTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec()); + (theGeomPolicy == BRepGraph_Copy::GeomPolicy::Copy) || isNegative || isScaled; + + // GeomPolicy::Drop is invalid when geometry-level modification is required: + // can't transform geometry that doesn't exist. + if (useGeomModif && theGeomPolicy == BRepGraph_Copy::GeomPolicy::Drop) + { + return false; + } + + // MeshPolicy::Share is invalid when geometry-level modification is required: + // mesh handles become stale after geometry transforms. + if (useGeomModif && theMeshPolicy == BRepGraph_Copy::MeshPolicy::Share) + { + return false; + } + + // Determine if mesh needs transformation (same trigger as geometry). + const bool doCopyMesh = (theMeshPolicy == BRepGraph_Copy::MeshPolicy::Copy); + + // Self-transform: apply transform in-place on theTargetGraph. + if (&theSourceGraph == &theTargetGraph) + { + if (useGeomModif) + { + applyGeometryTransform(theTargetGraph, theTargetGraph, theTrsf, doCopyMesh); + } + else + { + applyLocationTransform(theTargetGraph, theTrsf); + if (doCopyMesh) + { + applyMeshCopy(theTargetGraph, theTargetGraph, theTrsf, false); + } + } + return true; + } + + // Copy source into target, then transform. + const GraphCounts aTargetCounts = graphCounts(theTargetGraph); + if (!BRepGraph_Copy::Perform(theSourceGraph, theTargetGraph, theGeomPolicy, theMeshPolicy)) + { + return false; + } + + if (useGeomModif) + { + applyGeometryTransformInCopiedRange(theTargetGraph, aTargetCounts, theTrsf, doCopyMesh); + } + else + { + applyLocationTransformInCopiedRange(theTargetGraph, aTargetCounts, theTrsf); + if (doCopyMesh) + { + transformExistingMesh(theTargetGraph, aTargetCounts, theTrsf, false); + } + } + return true; +} + +//================================================================================================= + +BRepGraph_NodeId BRepGraph_Transform::TransformNode(const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + const BRepGraph_NodeId theNodeId, + const gp_Trsf& theTrsf, + const BRepGraph_Copy::GeomPolicy theGeomPolicy, + const BRepGraph_Copy::MeshPolicy theMeshPolicy) +{ + if (theSourceGraph.IsEmpty()) + { + return BRepGraph_NodeId(); + } + + // Determine if the transform requires geometry-level modification. + const bool isNegative = theTrsf.IsNegative(); + const bool isScaled = + std::abs(std::abs(theTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec(); + + const bool isPlacementNode = theNodeId.NodeKind == BRepGraph_NodeId::Kind::Product + || theNodeId.NodeKind == BRepGraph_NodeId::Kind::Occurrence; + const bool useGeomModif = !isPlacementNode || (theGeomPolicy == BRepGraph_Copy::GeomPolicy::Copy) + || isNegative || isScaled; + + // GeomPolicy::Drop is invalid when geometry-level modification is required. + if (useGeomModif && theGeomPolicy == BRepGraph_Copy::GeomPolicy::Drop) + { + return BRepGraph_NodeId(); + } + + // MeshPolicy::Share is invalid when geometry-level modification is required: + // mesh handles become stale after geometry transforms. + if (useGeomModif && theMeshPolicy == BRepGraph_Copy::MeshPolicy::Share) + { + return BRepGraph_NodeId(); + } // Assembly nodes (Product / Occurrence) carry topology only via locations on // OccurrenceRef. The geometry-modification path warps shared geometry but never // updates locations, producing an inconsistent result regardless of traversal mode. // Reject the combination explicitly rather than silently mis-transforming. - if (useGeomModif - && (theNodeId.NodeKind == BRepGraph_NodeId::Kind::Product - || theNodeId.NodeKind == BRepGraph_NodeId::Kind::Occurrence)) + if (useGeomModif && isPlacementNode) { - return BRepGraph(); + return BRepGraph_NodeId(); } - constexpr bool THE_RESERVE_CACHE = false; - - // Pull mesh data through the copy only when the caller asked for it: the - // geometry-transform path otherwise clears triangulations face-by-face, and - // skipping the inbound copy keeps allocations down. - BRepGraph aSubgraph = - BRepGraph_Copy::CopyNode(theGraph, theNodeId, theCopyGeom, theCopyMesh, THE_RESERVE_CACHE); - if (!aSubgraph.IsDone()) + // Copy node into target, then transform. + const GraphCounts aTargetCounts = graphCounts(theTargetGraph); + const BRepGraph_NodeId aRootId = BRepGraph_Copy::CopyNode(theSourceGraph, + theTargetGraph, + theNodeId, + theGeomPolicy, + theMeshPolicy); + if (!aRootId.IsValid()) { - return aSubgraph; + return BRepGraph_NodeId(); } + const bool doCopyMesh = (theMeshPolicy == BRepGraph_Copy::MeshPolicy::Copy); + if (useGeomModif) { - // theGraph supplies the poly storage (CopyNode does not duplicate it); aSubgraph - // raw cache bypasses the OwnGen freshness check that geometry-transform Mut guards - // would otherwise invalidate. - const BRepGraph_MeshCacheStorage& aSubgraphCache = aSubgraph.meshCache(); - applyGeometryTransform(aSubgraph, aSubgraph, theTrsf, theCopyMesh, &theGraph, &aSubgraphCache); + applyGeometryTransformInCopiedRange(theTargetGraph, aTargetCounts, theTrsf, doCopyMesh); } else { - applyLocationTransform(aSubgraph, theTrsf); - if (theCopyMesh) + applyLocationTransformInCopiedRange(theTargetGraph, aTargetCounts, theTrsf); + if (doCopyMesh) { - applyMeshCopy(theGraph, aSubgraph, theTrsf, false); + transformExistingMesh(theTargetGraph, aTargetCounts, theTrsf, false); } } - return aSubgraph; + return aRootId; } //================================================================================================= -bool BRepGraph_Transform::MoveRef(BRepGraph& theGraph, - const BRepGraph_RefId& theRefId, - const gp_Trsf& theTrsf) +bool BRepGraph_Transform::MoveRef(BRepGraph& theGraph, + const BRepGraph_ChildRefId theRefId, + const gp_Trsf& theTrsf) { if (std::abs(std::abs(theTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec()) { return false; } - - const TopLoc_Location aLoc = TopLoc_Location(theTrsf); - BRepGraph::EditorView& anEditor = theGraph.Editor(); - - switch (theRefId.RefKind) + if (!theRefId.IsValid(theGraph.Refs().Children().Nb()) + || theGraph.Refs().Gen().IsRemoved(theRefId)) { - case BRepGraph_RefId::Kind::Shell: { - const BRepGraph_ShellRefId aShellRef = BRepGraph_ShellRefId::FromRefId(theRefId); - const TopLoc_Location aOldLoc = theGraph.Refs().Shells().Entry(aShellRef).LocalLocation; - anEditor.Shells().SetRefLocalLocation(aShellRef, aLoc * aOldLoc); - return true; - } - case BRepGraph_RefId::Kind::Face: { - const BRepGraph_FaceRefId aFaceRef = BRepGraph_FaceRefId::FromRefId(theRefId); - const TopLoc_Location aOldLoc = theGraph.Refs().Faces().Entry(aFaceRef).LocalLocation; - anEditor.Faces().SetRefLocalLocation(aFaceRef, aLoc * aOldLoc); - return true; - } - case BRepGraph_RefId::Kind::Wire: { - const BRepGraph_WireRefId aWireRef = BRepGraph_WireRefId::FromRefId(theRefId); - const TopLoc_Location aOldLoc = theGraph.Refs().Wires().Entry(aWireRef).LocalLocation; - anEditor.Wires().SetRefLocalLocation(aWireRef, aLoc * aOldLoc); - return true; - } - case BRepGraph_RefId::Kind::CoEdge: { - const BRepGraph_CoEdgeRefId aCoEdgeRef = BRepGraph_CoEdgeRefId::FromRefId(theRefId); - const TopLoc_Location aOldLoc = theGraph.Refs().CoEdges().Entry(aCoEdgeRef).LocalLocation; - anEditor.CoEdges().SetRefLocalLocation(aCoEdgeRef, aLoc * aOldLoc); - return true; - } - case BRepGraph_RefId::Kind::Vertex: { - const BRepGraph_VertexRefId aVertexRef = BRepGraph_VertexRefId::FromRefId(theRefId); - const TopLoc_Location aOldLoc = theGraph.Refs().Vertices().Entry(aVertexRef).LocalLocation; - anEditor.Vertices().SetRefLocalLocation(aVertexRef, aLoc * aOldLoc); - return true; - } - case BRepGraph_RefId::Kind::Solid: { - const BRepGraph_SolidRefId aSolidRef = BRepGraph_SolidRefId::FromRefId(theRefId); - const TopLoc_Location aOldLoc = theGraph.Refs().Solids().Entry(aSolidRef).LocalLocation; - anEditor.Solids().SetRefLocalLocation(aSolidRef, aLoc * aOldLoc); - return true; - } - case BRepGraph_RefId::Kind::Child: { - const BRepGraph_ChildRefId aChildRef = BRepGraph_ChildRefId::FromRefId(theRefId); - const TopLoc_Location aOldLoc = theGraph.Refs().Children().Entry(aChildRef).LocalLocation; - anEditor.Gen().SetChildRefLocalLocation(aChildRef, aLoc * aOldLoc); - return true; - } - case BRepGraph_RefId::Kind::Occurrence: { - const BRepGraph_OccurrenceRefId aOccRef = BRepGraph_OccurrenceRefId::FromRefId(theRefId); - const TopLoc_Location aOldLoc = theGraph.Refs().Occurrences().Entry(aOccRef).LocalLocation; - anEditor.Occurrences().SetRefLocalLocation(aOccRef, aLoc * aOldLoc); - return true; - } + return false; } - return false; + + const TopLoc_Location aLoc = TopLoc_Location(theTrsf); + BRepGraph::EditorView anEditor = theGraph.Editor(); + const TopLoc_Location aOldLoc = theGraph.Refs().Children().Entry(theRefId).LocalLocation; + anEditor.Gen().SetChildRefLocalLocation(theRefId, aLoc * aOldLoc); + return true; +} + +//================================================================================================= + +bool BRepGraph_Transform::MoveRef(BRepGraph& theGraph, + const BRepGraph_OccurrenceRefId theRefId, + const gp_Trsf& theTrsf) +{ + if (std::abs(std::abs(theTrsf.ScaleFactor()) - 1.) > TopLoc_Location::ScalePrec()) + { + return false; + } + if (!theRefId.IsValid(theGraph.Refs().Occurrences().Nb()) + || theGraph.Refs().Gen().IsRemoved(theRefId)) + { + return false; + } + + const TopLoc_Location aLoc = TopLoc_Location(theTrsf); + BRepGraph::EditorView anEditor = theGraph.Editor(); + const TopLoc_Location aOldLoc = theGraph.Refs().Occurrences().Entry(theRefId).LocalLocation; + anEditor.Occurrences().SetRefLocalLocation(theRefId, aLoc * aOldLoc); + return true; } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.hxx index 5ded63041c..d00b0173c3 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Transform.hxx @@ -15,42 +15,44 @@ #define _BRepGraph_Transform_HeaderFile #include +#include #include -#include - +#include #include #include //! @brief Graph-to-graph transformation. //! -//! Produces a new BRepGraph by copying and then applying a geometric -//! transformation to vertex points and geometry node locations. +//! Applies a geometric transformation to vertex points and geometry node +//! locations by copying into a target graph, then transforming in-place. //! //! Two geometry modes (matching BRepBuilderAPI_Transform semantics): -//! - theCopyGeom = true (geometry-level): deep-copy geometry, transform handles -//! in-place via Geom_Surface::Transform() etc., reset locations to identity. -//! - theCopyGeom = false (root-level): light-copy with shared geometry, apply +//! - GeomPolicy::Copy (geometry-level): deep-copy geometry, create new +//! transformed handles via Geom_Geometry::Transformed(), reset locations +//! to identity. +//! - GeomPolicy::Share (root-level): light-copy with shared geometry, apply //! transform via location modification only. //! -//! Mesh handling (theCopyMesh parameter): -//! - theCopyMesh = false (default): triangulations and polygons are discarded -//! after a geometry-level transform and must be recomputed. -//! - theCopyMesh = true: all mesh data (Poly_Triangulation on FaceDefs and the +//! Mesh handling (MeshPolicy parameter): +//! - MeshPolicy::Drop (default for Transform): triangulations and polygons are +//! discarded after a geometry-level transform and must be recomputed. +//! - MeshPolicy::Copy: all mesh data (Poly_Triangulation on FaceDefs and the //! MeshLayer cache, Poly_Polygon3D on edges, Poly_PolygonOnTriangulation on //! coedges) is copied and transformed in sync with the geometry. //! In location-only mode the mesh data is copied as-is (nodes stay in the //! graph coordinate system, which is unaffected by a pure location compose). //! -//! @note Returns BRepGraph directly (not a Result struct) because this is an -//! immutable operation producing a new graph. Check IsDone() for success. +//! @note Check the return value for success: Perform returns bool, +//! TransformNode returns the mapped root NodeId (invalid on failure). //! //! ## Typical usage //! @code //! BRepGraph aGraph; -//! BRepGraph_Builder::Add(aGraph, myShape); +//! aGraph.Shapes().Add(myShape); //! gp_Trsf aTrsf; //! aTrsf.SetTranslation(gp_Vec(10.0, 0.0, 0.0)); -//! BRepGraph aTransformed = BRepGraph_Transform::Perform(aGraph, aTrsf); +//! BRepGraph aTransformed; +//! BRepGraph_Transform::Perform(aGraph, aTransformed, aTrsf); //! TopoDS_Shape aShape = aTransformed.Shapes().Shape(); //! @endcode class BRepGraph_Transform @@ -58,56 +60,83 @@ class BRepGraph_Transform public: DEFINE_STANDARD_ALLOC - //! Transform the entire graph. - //! @param[in] theGraph a pre-built BRepGraph (must have IsDone() == true) - //! @param[in] theTrsf the transformation to apply - //! @param[in] theCopyGeom if true, geometry is deep-copied before transforming; - //! if false, light-copy then transform locations/points only - //! @param[in] theCopyMesh if true, mesh data (triangulations, polygons) is copied and - //! transformed; if false, meshes are discarded after transform - //! @return a new BRepGraph with the transformation applied - [[nodiscard]] Standard_EXPORT static BRepGraph Perform(const BRepGraph& theGraph, - const gp_Trsf& theTrsf, - const bool theCopyGeom = true, - const bool theCopyMesh = false); + //! Transform the entire graph into a target graph. + //! + //! Self-transform (theSourceGraph == theTargetGraph): + //! Applies transform in-place on theTargetGraph. + //! + //! External transform to empty target (theTargetGraph.IsEmpty()): + //! Copies source into target, then transforms. + //! + //! External transform to non-empty target: + //! Appends source entities into target with explicit mapping, then transforms. + //! + //! @param[in] theSourceGraph a pre-built BRepGraph (must not be empty) + //! @param[in,out] theTargetGraph destination graph (may already contain data) + //! @param[in] theTrsf the transformation to apply + //! @param[in] theGeomPolicy geometry handle policy (default: Copy) + //! @param[in] theMeshPolicy mesh data policy (default: Drop) + //! @return true on success, false on failure (empty source, or Drop + + //! geometry-modification-required) + Standard_EXPORT static bool Perform( + const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + const gp_Trsf& theTrsf, + const BRepGraph_Copy::GeomPolicy theGeomPolicy = BRepGraph_Copy::GeomPolicy::Copy, + const BRepGraph_Copy::MeshPolicy theMeshPolicy = BRepGraph_Copy::MeshPolicy::Drop); - //! Transform a single node sub-graph of any kind (Face, Shell, Solid, Wire, Edge, Vertex). - //! Produces a new BRepGraph containing only the specified node and its referenced sub-graph. - //! The transform is applied to all copied geometry (same rules as Perform()). - //! @param[in] theGraph a pre-built BRepGraph - //! @param[in] theNodeId node identifier (any kind: Face, Shell, Solid, Wire, Edge, Vertex, - //! Compound, CompSolid, Product, Occurrence) - //! @param[in] theTrsf the transformation to apply - //! @param[in] theCopyGeom if true, geometry is deep-copied before transforming - //! @param[in] theCopyMesh if true, mesh data is copied and transformed - //! @return a new BRepGraph containing only the specified sub-graph, transformed - [[nodiscard]] Standard_EXPORT static BRepGraph TransformNode(const BRepGraph& theGraph, - const BRepGraph_NodeId theNodeId, - const gp_Trsf& theTrsf, - const bool theCopyGeom = true, - const bool theCopyMesh = false); + //! Transform a single node sub-graph of any kind. + //! Topology nodes are copied and transformed by baking the transform into their definitions. + //! + //! Self-transform (theSourceGraph == theTargetGraph): + //! Duplicates the sub-graph with new entity IDs, then transforms the copy. + //! + //! External transform: + //! Copies the sub-graph into theTargetGraph, then transforms. + //! + //! @param[in] theSourceGraph a pre-built BRepGraph + //! @param[in,out] theTargetGraph destination graph (may already contain data) + //! @param[in] theNodeId node identifier (any kind) + //! @param[in] theTrsf the transformation to apply + //! @param[in] theGeomPolicy geometry handle policy (default: Copy; Drop is invalid for topology) + //! @param[in] theMeshPolicy mesh data policy (default: Drop) + //! @return the mapped root NodeId in theTargetGraph, or invalid NodeId on failure + [[nodiscard]] Standard_EXPORT static BRepGraph_NodeId TransformNode( + const BRepGraph& theSourceGraph, + BRepGraph& theTargetGraph, + const BRepGraph_NodeId theNodeId, + const gp_Trsf& theTrsf, + const BRepGraph_Copy::GeomPolicy theGeomPolicy = BRepGraph_Copy::GeomPolicy::Copy, + const BRepGraph_Copy::MeshPolicy theMeshPolicy = BRepGraph_Copy::MeshPolicy::Drop); - //! Apply an in-place location-only transform to a single reference. - //! Composes theTrsf into the reference's LocalLocation field without copying - //! any geometry. This is O(1) and equivalent to TopoDS_Shape::Moved(trsf). + //! Apply an in-place location-only transform to a child reference. + //! Composes theTrsf into ChildRef placement without copying any geometry. //! Cached mesh data on entities downstream of the moved ref is stored in the //! entity's local frame and is unaffected; callers that bake a world transform //! into a cache key own the invalidation responsibility. //! @note Only pure rotation/translation transforms (scale == 1) are supported. - //! The method is a no-op and returns false if |scaleFactor| != 1. + //! The method returns false if |scaleFactor| != 1. //! @param[in] theGraph the graph containing the reference - //! @param[in] theRefId reference to move (any ref kind) + //! @param[in] theRefId child reference to move //! @param[in] theTrsf the transformation to compose into the location - //! @return true on success; false if theTrsf has a non-unit scale factor - Standard_EXPORT static bool MoveRef(BRepGraph& theGraph, - const BRepGraph_RefId& theRefId, - const gp_Trsf& theTrsf); + //! @return true on success; false if the ref is invalid/removed or theTrsf has non-unit scale + Standard_EXPORT static bool MoveRef(BRepGraph& theGraph, + const BRepGraph_ChildRefId theRefId, + const gp_Trsf& theTrsf); + + //! Apply an in-place location-only transform to an occurrence reference. + //! Composes theTrsf into OccurrenceRef placement without copying any geometry. + //! @note Only pure rotation/translation transforms (scale == 1) are supported. + //! @return true on success; false if the ref is invalid/removed or theTrsf has non-unit scale + Standard_EXPORT static bool MoveRef(BRepGraph& theGraph, + const BRepGraph_OccurrenceRefId theRefId, + const gp_Trsf& theTrsf); + + BRepGraph_Transform() = delete; private: //! Apply location-only transform by storing per-node locations. static void applyLocationTransform(BRepGraph& theGraph, const gp_Trsf& theTrsf); - - BRepGraph_Transform() = delete; }; #endif // _BRepGraph_Transform_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.cxx deleted file mode 100644 index 749cb752e0..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.cxx +++ /dev/null @@ -1,427 +0,0 @@ -// 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 - -#include -#include - -IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_CacheKind, Standard_Transient) - -namespace -{ - -struct BRepGraph_CacheKindRegistryData -{ - NCollection_DataMap GuidToSlot; - NCollection_DynamicArray> Kinds; - std::shared_mutex Mutex; -}; - -BRepGraph_CacheKindRegistryData& cacheKindRegistryData() -{ - static BRepGraph_CacheKindRegistryData aData; - return aData; -} - -} // namespace - -//================================================================================================= - -BRepGraph_CacheKind::BRepGraph_CacheKind(const Standard_GUID& theID, - const TCollection_AsciiString& theName, - const int theNodeKindsMask) - : myID(theID), - myName(theName), - myNodeKindsMask(theNodeKindsMask) -{ -} - -//================================================================================================= - -int BRepGraph_CacheKindRegistry::Register(const occ::handle& theKind) -{ - if (theKind.IsNull()) - { - return -1; - } - - BRepGraph_CacheKindRegistryData& aData = cacheKindRegistryData(); - { - std::shared_lock aReadLock(aData.Mutex); - const int* aSlot = aData.GuidToSlot.Seek(theKind->ID()); - if (aSlot != nullptr) - { - return *aSlot; - } - } - - std::unique_lock aWriteLock(aData.Mutex); - const int* aSlot = aData.GuidToSlot.Seek(theKind->ID()); - if (aSlot != nullptr) - { - return *aSlot; - } - - const int aNewSlot = aData.Kinds.Length(); - aData.Kinds.Append(theKind); - aData.GuidToSlot.Bind(theKind->ID(), aNewSlot); - return aNewSlot; -} - -//================================================================================================= - -int BRepGraph_CacheKindRegistry::FindSlot(const Standard_GUID& theGUID) -{ - BRepGraph_CacheKindRegistryData& aData = cacheKindRegistryData(); - std::shared_lock aLock(aData.Mutex); - const int* aSlot = aData.GuidToSlot.Seek(theGUID); - return aSlot != nullptr ? *aSlot : -1; -} - -//================================================================================================= - -bool BRepGraph_CacheKindRegistry::FindSlot(const Standard_GUID& theGUID, int& theSlot) -{ - theSlot = FindSlot(theGUID); - return theSlot >= 0; -} - -//================================================================================================= - -occ::handle BRepGraph_CacheKindRegistry::FindKind(const Standard_GUID& theGUID) -{ - const int aSlot = FindSlot(theGUID); - return aSlot >= 0 ? FindKind(aSlot) : occ::handle(); -} - -//================================================================================================= - -occ::handle BRepGraph_CacheKindRegistry::FindKind(const int theSlot) -{ - BRepGraph_CacheKindRegistryData& aData = cacheKindRegistryData(); - std::shared_lock aLock(aData.Mutex); - if (theSlot < 0 || theSlot >= aData.Kinds.Length()) - { - return occ::handle(); - } - return aData.Kinds.Value(theSlot); -} - -//================================================================================================= - -bool BRepGraph_CacheKindRegistry::Contains(const Standard_GUID& theGUID) -{ - return FindSlot(theGUID) >= 0; -} - -//================================================================================================= - -bool BRepGraph_CacheKindRegistry::Contains(const int theSlot) -{ - BRepGraph_CacheKindRegistryData& aData = cacheKindRegistryData(); - std::shared_lock aLock(aData.Mutex); - return theSlot >= 0 && theSlot < aData.Kinds.Length() && !aData.Kinds.Value(theSlot).IsNull(); -} - -//================================================================================================= - -int BRepGraph_CacheKindRegistry::NbRegistered() -{ - BRepGraph_CacheKindRegistryData& aData = cacheKindRegistryData(); - std::shared_lock aLock(aData.Mutex); - return aData.Kinds.Length(); -} - -//================================================================================================= - -void BRepGraph_TransientCache::ensureKind(const int theKindSlot) -{ - if (theKindSlot >= myKinds.Length()) - { - myKinds.SetValue(theKindSlot, CacheKindSlot()); - } -} - -//================================================================================================= - -BRepGraph_TransientCache::CacheSlot& BRepGraph_TransientCache::changeSlot( - const BRepGraph_NodeId theNode, - const int theKindSlot) -{ - ensureKind(theKindSlot); - const int aKindIdx = static_cast(theNode.NodeKind); - Standard_ASSERT_VOID(aKindIdx >= 0 && aKindIdx < THE_KIND_COUNT, - "BRepGraph_TransientCache: NodeKind out of range"); - NCollection_DynamicArray& aVec = - myKinds.ChangeValue(theKindSlot).myNodeKinds[aKindIdx].mySlots; - if (theNode.Index >= aVec.Size()) - { - return aVec.SetValue(static_cast(theNode.Index), CacheSlot()); - } - return aVec.ChangeValue(static_cast(theNode.Index)); -} - -//================================================================================================= - -const BRepGraph_TransientCache::CacheSlot* BRepGraph_TransientCache::seekSlot( - const BRepGraph_NodeId theNode, - const int theKindSlot) const -{ - if (theKindSlot < 0 || theKindSlot >= myKinds.Length()) - { - return nullptr; - } - - const int aKindIdx = static_cast(theNode.NodeKind); - Standard_ASSERT_VOID(aKindIdx >= 0 && aKindIdx < THE_KIND_COUNT, - "BRepGraph_TransientCache: NodeKind out of range"); - const NCollection_DynamicArray& aVec = - myKinds.Value(theKindSlot).myNodeKinds[aKindIdx].mySlots; - if (!theNode.IsValidIn(aVec)) - { - return nullptr; - } - return &aVec.Value(static_cast(theNode.Index)); -} - -//================================================================================================= - -void BRepGraph_TransientCache::Reserve(const int theKindCount, const int theCounts[THE_KIND_COUNT]) -{ - std::unique_lock aLock(myMutex); - - const int aKindCount = theKindCount > 0 ? theKindCount : 0; - if (aKindCount > 0) - { - ensureKind(aKindCount - 1); - } - - for (int aKindSlot = 0; aKindSlot < aKindCount; ++aKindSlot) - { - CacheKindSlot& aKindSlotData = myKinds.ChangeValue(aKindSlot); - for (int aKindIdx = 0; aKindIdx < THE_KIND_COUNT; ++aKindIdx) - { - const int aCount = theCounts[aKindIdx]; - if (aCount > 0 && aCount > aKindSlotData.myNodeKinds[aKindIdx].mySlots.Length()) - { - aKindSlotData.myNodeKinds[aKindIdx].mySlots.SetValue(aCount - 1, CacheSlot()); - } - } - } - - myIsReserved.store(true, std::memory_order_release); -} - -//================================================================================================= - -void BRepGraph_TransientCache::Set(const BRepGraph_NodeId theNode, - const occ::handle& theKind, - const occ::handle& theValue, - const uint32_t theCurrentSubtreeGen) -{ - if (!theNode.IsValid() || theKind.IsNull()) - { - return; - } - - const int aKindSlot = BRepGraph_CacheKindRegistry::Register(theKind); - if (aKindSlot < 0) - { - return; - } - - Set(theNode, aKindSlot, theValue, theCurrentSubtreeGen); -} - -//================================================================================================= - -void BRepGraph_TransientCache::Set(const BRepGraph_NodeId theNode, - const int theKindSlot, - const occ::handle& theValue, - const uint32_t theCurrentSubtreeGen) -{ - if (!theNode.IsValid() || theKindSlot < 0) - { - return; - } - - if (myIsReserved.load(std::memory_order_acquire) && theKindSlot < myKinds.Length()) - { - const int aKindIdx = static_cast(theNode.NodeKind); - NCollection_DynamicArray& aVec = - myKinds.ChangeValue(theKindSlot).myNodeKinds[aKindIdx].mySlots; - if (theNode.Index < aVec.Size()) - { - CacheSlot& aSlot = aVec.ChangeValue(static_cast(theNode.Index)); - aSlot.Value = theValue; - aSlot.StoredSubtreeGen = theCurrentSubtreeGen; - return; - } - } - - std::unique_lock aLock(myMutex); - CacheSlot& aSlot = changeSlot(theNode, theKindSlot); - aSlot.Value = theValue; - aSlot.StoredSubtreeGen = theCurrentSubtreeGen; -} - -//================================================================================================= - -occ::handle BRepGraph_TransientCache::Get( - const BRepGraph_NodeId theNode, - const occ::handle& theKind, - const uint32_t theCurrentSubtreeGen) const -{ - if (!theNode.IsValid() || theKind.IsNull()) - { - return occ::handle(); - } - - const int aKindSlot = BRepGraph_CacheKindRegistry::FindSlot(theKind->ID()); - if (aKindSlot < 0) - { - return occ::handle(); - } - - return Get(theNode, aKindSlot, theCurrentSubtreeGen); -} - -//================================================================================================= - -occ::handle BRepGraph_TransientCache::Get( - const BRepGraph_NodeId theNode, - const int theKindSlot, - const uint32_t theCurrentSubtreeGen) const -{ - if (!theNode.IsValid() || theKindSlot < 0) - { - return occ::handle(); - } - - if (myIsReserved.load(std::memory_order_acquire) && theKindSlot < myKinds.Length()) - { - const int aKindIdx = static_cast(theNode.NodeKind); - const NCollection_DynamicArray& aVec = - myKinds.Value(theKindSlot).myNodeKinds[aKindIdx].mySlots; - if (theNode.Index < aVec.Size()) - { - const CacheSlot& aSlot = aVec.Value(static_cast(theNode.Index)); - if (aSlot.Value.IsNull()) - { - return occ::handle(); - } - if (aSlot.StoredSubtreeGen != theCurrentSubtreeGen) - { - return occ::handle(); - } - return aSlot.Value; - } - } - - std::shared_lock aLock(myMutex); - const CacheSlot* aSlot = seekSlot(theNode, theKindSlot); - if (aSlot == nullptr || aSlot->Value.IsNull()) - { - return occ::handle(); - } - if (aSlot->StoredSubtreeGen != theCurrentSubtreeGen) - { - return occ::handle(); - } - return aSlot->Value; -} - -//================================================================================================= - -bool BRepGraph_TransientCache::Remove(const BRepGraph_NodeId theNode, - const occ::handle& theKind) -{ - if (!theNode.IsValid() || theKind.IsNull()) - { - return false; - } - - const int aKindSlot = BRepGraph_CacheKindRegistry::FindSlot(theKind->ID()); - if (aKindSlot < 0) - { - return false; - } - - return Remove(theNode, aKindSlot); -} - -//================================================================================================= - -bool BRepGraph_TransientCache::Remove(const BRepGraph_NodeId theNode, const int theKindSlot) -{ - if (!theNode.IsValid() || theKindSlot < 0) - { - return false; - } - - std::unique_lock aLock(myMutex); - if (static_cast(theKindSlot) >= myKinds.Size()) - { - return false; - } - - const int aKindIdx = static_cast(theNode.NodeKind); - NCollection_DynamicArray& aVec = - myKinds.ChangeValue(theKindSlot).myNodeKinds[aKindIdx].mySlots; - if (theNode.Index >= aVec.Size()) - { - return false; - } - - CacheSlot& aSlot = aVec.ChangeValue(static_cast(theNode.Index)); - if (aSlot.Value.IsNull()) - { - return false; - } - aSlot.Value.Nullify(); - aSlot.StoredSubtreeGen = 0; - return true; -} - -//================================================================================================= - -int BRepGraph_TransientCache::CollectCacheKindSlots(const BRepGraph_NodeId theNode, - const uint32_t theCurrentSubtreeGen, - int theSlots[]) const -{ - int aCount = 0; - if (!theNode.IsValid()) - { - return aCount; - } - - for (int aKindSlot = 0; aKindSlot < myKinds.Length(); ++aKindSlot) - { - const CacheSlot* aSlot = seekSlot(theNode, aKindSlot); - if (aSlot != nullptr && !aSlot->Value.IsNull() - && aSlot->StoredSubtreeGen == theCurrentSubtreeGen) - { - theSlots[aCount++] = aKindSlot; - } - } - return aCount; -} - -//================================================================================================= - -void BRepGraph_TransientCache::Clear() noexcept -{ - myKinds.Clear(); - myIsReserved.store(false, std::memory_order_relaxed); -} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.hxx deleted file mode 100644 index 122644a5cb..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_TransientCache.hxx +++ /dev/null @@ -1,362 +0,0 @@ -// 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. - -#ifndef _BRepGraph_TransientCache_HeaderFile -#define _BRepGraph_TransientCache_HeaderFile - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -//! @brief Descriptor of one transient cache family. -//! -//! A cache kind defines stable public identity for one class of transient, -//! recomputable per-node data such as bounding boxes or UV bounds. -//! Instances are process-global descriptors registered in -//! BRepGraph_CacheKindRegistry and referenced from graphs by dense runtime slots. -class BRepGraph_CacheKind : public Standard_Transient -{ -public: - //! Create a cache kind descriptor. - //! @param[in] theID stable public GUID identity - //! @param[in] theName display-only name - //! @param[in] theNodeKindsMask optional node-kind applicability mask; - //! 0 means "unspecified / unrestricted" - Standard_EXPORT BRepGraph_CacheKind( - const Standard_GUID& theID, - const TCollection_AsciiString& theName = TCollection_AsciiString(), - const int theNodeKindsMask = 0); - - //! Stable public identity. - [[nodiscard]] const Standard_GUID& ID() const { return myID; } - - //! Display-only metadata. - [[nodiscard]] const TCollection_AsciiString& Name() const { return myName; } - - //! Optional node-kind applicability mask. - [[nodiscard]] int NodeKindsMask() const { return myNodeKindsMask; } - - //! True if this cache kind is applicable to the given node kind. - //! Cache kinds with NodeKindsMask() == 0 are treated as unrestricted. - [[nodiscard]] bool SupportsNodeKind(const BRepGraph_NodeId::Kind theKind) const - { - return myNodeKindsMask == 0 || (myNodeKindsMask & KindBit(theKind)) != 0; - } - - //! Convenience: bitmask bit for a given node kind. - static int KindBit(const BRepGraph_NodeId::Kind theKind) - { - return 1 << static_cast(theKind); - } - - DEFINE_STANDARD_RTTIEXT(BRepGraph_CacheKind, Standard_Transient) - -private: - Standard_GUID myID; - TCollection_AsciiString myName; - int myNodeKindsMask = 0; -}; - -//! @brief Process-global registry of cache kind descriptors. -//! -//! Maps stable GUID identity to dense runtime slot index. Slot indices are an -//! internal storage detail used by BRepGraph_TransientCache for O(1) indexing. -//! The registry is shared across all BRepGraph instances in the current process, -//! so cache-kind GUIDs should be globally unique. -class BRepGraph_CacheKindRegistry -{ -public: - //! Register a cache kind descriptor. - //! Idempotent: the same GUID always yields the same slot. - //! Slot assignment is process-global and graph-instance independent. - //! @return dense runtime slot, or -1 for null input - [[nodiscard]] Standard_EXPORT static int Register( - const occ::handle& theKind); - - //! Find slot by GUID. Returns -1 if not found. - [[nodiscard]] Standard_EXPORT static int FindSlot(const Standard_GUID& theGUID); - - //! Find slot by GUID. - //! @param[out] theSlot dense runtime slot if found - //! @return true if the GUID is registered - Standard_EXPORT static bool FindSlot(const Standard_GUID& theGUID, int& theSlot); - - //! Find descriptor by GUID. - [[nodiscard]] Standard_EXPORT static occ::handle FindKind( - const Standard_GUID& theGUID); - - //! Find descriptor by slot. - [[nodiscard]] Standard_EXPORT static occ::handle FindKind(const int theSlot); - - //! Check whether a GUID is registered. - [[nodiscard]] Standard_EXPORT static bool Contains(const Standard_GUID& theGUID); - - //! Check whether a slot is registered. - [[nodiscard]] Standard_EXPORT static bool Contains(const int theSlot); - - //! Number of registered cache kinds. - [[nodiscard]] Standard_EXPORT static int NbRegistered(); - -private: - BRepGraph_CacheKindRegistry() = delete; -}; - -//! @brief Abstract base for transient per-node cache values. -//! -//! Inherits from Standard_Transient and is stored via -//! occ::handle. This uses OCCT's embedded refcount and is -//! consistent with the Handle pattern used throughout the codebase. -class BRepGraph_CacheValue : public Standard_Transient -{ -public: - //! Mark the cached value as needing recomputation. Lock-free. - void Invalidate() { myDirty.store(true, std::memory_order_release); } - - //! True if the cached value needs recomputation. - bool IsDirty() const { return myDirty.load(std::memory_order_acquire); } - - DEFINE_STANDARD_RTTI_INLINE(BRepGraph_CacheValue, Standard_Transient) - -protected: - BRepGraph_CacheValue() - : myDirty(true) - { - } - - //! Subclass calls after successful computation to clear the dirty flag. - void MarkClean() const { myDirty.store(false, std::memory_order_release); } - - //! Mutex for thread-safe Get() in subclasses. - mutable std::shared_mutex myMutex; - -private: - mutable std::atomic myDirty; -}; - -//! @brief Concrete typed wrapper for a lazily-computed per-node value. -//! -//! @tparam T cached value type (for example double). -template -class BRepGraph_TypedCacheValue : public BRepGraph_CacheValue -{ -public: - BRepGraph_TypedCacheValue() = default; - - //! Construct with an initial value (marked clean). - explicit BRepGraph_TypedCacheValue(const T& theInitial) - : myValue(theInitial) - { - MarkClean(); - } - - //! Get the cached value, computing via theComputer if dirty. - //! Thread-safe: uses the base class shared_mutex. - T Get(const std::function& theComputer) const - { - if (!IsDirty()) - { - std::shared_lock aLock(myMutex); - if (!IsDirty()) - { - return myValue; - } - } - - std::unique_lock aLock(myMutex); - if (IsDirty()) - { - myValue = theComputer(); - MarkClean(); - } - return myValue; - } - - //! Direct write - stores the value and marks clean. - void Set(const T& theValue) - { - std::unique_lock aLock(myMutex); - myValue = theValue; - MarkClean(); - } - - //! Direct read. Caller must guarantee freshness. - const T& UncheckedValue() const { return myValue; } - -private: - mutable T myValue{}; -}; - -//! @brief Centralized transient cache for algorithm-computed per-node values. -//! -//! Stores short-lived cached data (BndBox, UVBounds, etc.) in dense per-cache-kind -//! vectors indexed by entity index. O(1) access by direct indexing - no hashing. -//! -//! ## SubtreeGen-based freshness -//! Each stored slot records SubtreeGen at write time. On read, if stored -//! SubtreeGen differs from entity's current SubtreeGen the cached value is -//! considered stale - the caller decides how to handle it. -//! -//! ## Lifecycle -//! NOT a Layer. Cleared on BRepGraph_Builder::Add() and Compact(). No OnNodeRemoved handling - -//! stale data is auto-detected by SubtreeGen mismatch. -//! -//! ## Thread safety -//! After Reserve(), Get() and Set() for in-range indices bypass the mutex -//! entirely - safe because parallel algorithms access different entity slots. -//! Out-of-range access (entities added after construction) falls back to mutex. -class BRepGraph_TransientCache -{ -public: - //! Number of Kind enum slots to cover (0..11, with gap at 9). - static constexpr int THE_KIND_COUNT = BRepGraph_NodeId::THE_KIND_COUNT; - - //! Default number of cache-kind slots reserved after BRepGraph_Builder::Add(). - static constexpr int THE_DEFAULT_RESERVED_KIND_COUNT = 16; - - //! Per-slot storage: cached value handle + SubtreeGen stamp. - struct CacheSlot - { - occ::handle Value; - uint32_t StoredSubtreeGen = 0; - }; - - //! Store a cached value for a node and cache kind. - //! @pre Reserve() must have been called for lock-free parallel access - //! on in-range entity indices; out-of-range access falls back to mutex. - Standard_EXPORT void Set(const BRepGraph_NodeId theNode, - const occ::handle& theKind, - const occ::handle& theValue, - const uint32_t theCurrentSubtreeGen); - - //! Store a cached value using a pre-resolved kind slot index. - //! Bypasses BRepGraph_CacheKindRegistry lookup - use in hot parallel paths. - //! @param[in] theKindSlot slot from BRepGraph_CacheKindRegistry::Register() - Standard_EXPORT void Set(const BRepGraph_NodeId theNode, - const int theKindSlot, - const occ::handle& theValue, - const uint32_t theCurrentSubtreeGen); - - //! Retrieve a cached value for a node and cache kind. - //! @pre Reserve() must have been called for lock-free parallel access - //! on in-range entity indices; out-of-range access falls back to mutex. - [[nodiscard]] Standard_EXPORT occ::handle Get( - const BRepGraph_NodeId theNode, - const occ::handle& theKind, - const uint32_t theCurrentSubtreeGen) const; - - //! Retrieve a cached value using a pre-resolved kind slot index. - //! Bypasses BRepGraph_CacheKindRegistry lookup - use in hot parallel paths. - //! @param[in] theKindSlot slot from BRepGraph_CacheKindRegistry::Register() - [[nodiscard]] Standard_EXPORT occ::handle Get( - const BRepGraph_NodeId theNode, - const int theKindSlot, - const uint32_t theCurrentSubtreeGen) const; - - //! Remove a cached value for a node and cache kind. - [[nodiscard]] Standard_EXPORT bool Remove(const BRepGraph_NodeId theNode, - const occ::handle& theKind); - - //! Remove a cached value using a pre-resolved cache-kind slot. - [[nodiscard]] Standard_EXPORT bool Remove(const BRepGraph_NodeId theNode, const int theKindSlot); - - //! Collect fresh cache-kind slot indices for a node (zero heap allocation). - //! Used internally by CacheView::CacheKindIterator. - //! @param[in] theNode node to query - //! @param[in] theCurrentSubtreeGen freshness stamp to match - //! @param[out] theSlots output array (caller-allocated, must hold - //! THE_DEFAULT_RESERVED_KIND_COUNT) - //! @return number of populated slots written to theSlots - Standard_EXPORT int CollectCacheKindSlots(const BRepGraph_NodeId theNode, - const uint32_t theCurrentSubtreeGen, - int theSlots[]) const; - - //! Pre-allocate storage for lock-free parallel access. - Standard_EXPORT void Reserve(const int theKindCount, const int theCounts[THE_KIND_COUNT]); - - //! True if Reserve() has been called and storage is pre-allocated. - [[nodiscard]] bool IsReserved() const noexcept - { - return myIsReserved.load(std::memory_order_acquire); - } - - //! Clear all cached data. Called on BRepGraph_Builder::Add() and Compact(). - Standard_EXPORT void Clear() noexcept; - - //! Move constructor: transfers data, creates fresh mutex. - BRepGraph_TransientCache(BRepGraph_TransientCache&& theOther) noexcept - : myKinds(std::move(theOther.myKinds)), - myIsReserved(theOther.myIsReserved.load(std::memory_order_relaxed)) - { - theOther.myIsReserved.store(false, std::memory_order_relaxed); - } - - //! Move assignment: transfers data, mutex stays local. - BRepGraph_TransientCache& operator=(BRepGraph_TransientCache&& theOther) noexcept - { - if (this != &theOther) - { - myKinds = std::move(theOther.myKinds); - myIsReserved.store(theOther.myIsReserved.load(std::memory_order_relaxed), - std::memory_order_relaxed); - theOther.myIsReserved.store(false, std::memory_order_relaxed); - } - return *this; - } - - BRepGraph_TransientCache() = default; - BRepGraph_TransientCache(const BRepGraph_TransientCache&) = delete; - BRepGraph_TransientCache& operator=(const BRepGraph_TransientCache&) = delete; - -private: - //! Per-node-kind dense vector of cache slots. - struct NodeKindStore - { - NCollection_DynamicArray mySlots; - }; - - //! Per-cache-kind storage: one node-kind store per entity kind. - struct CacheKindSlot - { - NodeKindStore myNodeKinds[THE_KIND_COUNT]; - }; - - //! Ensure myKinds has capacity for the given cache-kind slot. - void ensureKind(const int theKindSlot); - - //! Access slot (mutable) - grows vector if needed. - CacheSlot& changeSlot(const BRepGraph_NodeId theNode, const int theKindSlot); - - //! Access slot (const) - returns nullptr if out of range. - const CacheSlot* seekSlot(const BRepGraph_NodeId theNode, const int theKindSlot) const; - - //! Outer vector indexed by cache-kind slot. - NCollection_DynamicArray myKinds; - - //! True after Reserve() - enables lock-free access for in-range slots. - std::atomic myIsReserved{false}; - - //! Protects structural modifications (vector growth) during concurrent access. - mutable std::shared_mutex myMutex; -}; - -#endif // _BRepGraph_TransientCache_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UID.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UID.hxx index 0958b4cf38..431759a3c6 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UID.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UID.hxx @@ -15,104 +15,93 @@ #define _BRepGraph_UID_HeaderFile #include +#include +#include #include #include #include +#include -//! Unique node identifier within a BRepGraph. +//! Unique definition-node identifier within a BRepGraph. //! -//! Identity = (Kind, Counter). Two nodes of different kinds may share a +//! Identity = (Kind, Counter). Two nodes of different kinds may share a //! counter value but their UIDs are distinct. Within one kind, counter //! values never repeat (monotonic, never resets). //! -//! Generation is NOT part of identity; it indicates which BRepGraph::Clear() cycle -//! produced this UID (for stale-reference detection). -//! //! Trivially copyable, cheap to pass by value. -//! -//! ## Serialization Contract -//! -//! Entity UIDs (BRepGraph_UID) and reference UIDs (BRepGraph_RefUID) share -//! a single monotonic counter (BRepGraph_Data::myNextUIDCounter). -//! To persist a BRepGraph across sessions: -//! 1. Write: for each entity, serialize (Kind, Counter, OwnGen). -//! 2. Read: reconstruct entities, populate UID vectors with deserialized -//! (Kind, Counter) values, set myNextUIDCounter to -//! max(all_entity_counters, all_ref_counters) + 1. -//! 3. myGeneration resets to 0 on load (session-scoped). -//! 4. VersionStamps from a previous session will correctly detect staleness -//! via Generation mismatch. struct BRepGraph_UID { - //! Default: invalid UID (counter = 0 is the invalid sentinel). - BRepGraph_UID() - : myCounter(0), - myKind(BRepGraph_NodeId::Kind::Solid), - myGeneration(0) - { - } + BRepGraph_NodeId::Kind Kind = BRepGraph_NodeId::Kind::Solid; + uint32_t Counter = 0; - //! Construct a valid UID. Called internally by BRepGraph::allocateUID(). + //! Default: invalid UID (counter = 0 is the invalid sentinel). + BRepGraph_UID() = default; + + //! Construct a valid UID. Called internally by BRepGraphInc_Storage::AllocateNodeUID(). //! @pre theCounter > 0 (counter = 0 is reserved as the invalid sentinel) - BRepGraph_UID(const BRepGraph_NodeId::Kind theKind, - const size_t theCounter, - const uint32_t theGeneration) - : myCounter(theCounter), - myKind(theKind), - myGeneration(theGeneration) + BRepGraph_UID(const BRepGraph_NodeId::Kind theKind, const uint32_t theCounter) + : Kind(theKind), + Counter(theCounter) { - Standard_ASSERT_VOID(theCounter > 0, "BRepGraph_UID: counter must be > 0 for valid UIDs"); } //! Factory: returns an explicitly invalid UID. static BRepGraph_UID Invalid() { return BRepGraph_UID(); } - [[nodiscard]] bool IsValid() const { return myCounter > 0; } + //! True if this UID has a valid kind and a non-zero counter. + [[nodiscard]] bool IsValid() const { return Counter > 0 && BRepGraph_NodeId::IsValidKind(Kind); } - [[nodiscard]] BRepGraph_NodeId::Kind Kind() const { return myKind; } - - [[nodiscard]] size_t Counter() const { return myCounter; } - - [[nodiscard]] uint32_t Generation() const { return myGeneration; } - - [[nodiscard]] bool IsTopology() const { return BRepGraph_NodeId::IsTopologyKind(myKind); } - - [[nodiscard]] bool IsAssembly() const { return BRepGraph_NodeId::IsAssemblyKind(myKind); } - - //! Equality: Identity = (Kind, Counter). Generation excluded. - //! Two invalid UIDs are equal. - bool operator==(const BRepGraph_UID& theOther) const + [[nodiscard]] bool IsTopology() const { - if (myCounter == 0 || theOther.myCounter == 0) - return (myCounter == 0) == (theOther.myCounter == 0); - return myKind == theOther.myKind && myCounter == theOther.myCounter; + return IsValid() && BRepGraph_NodeId::IsTopologyKind(Kind); } - bool operator!=(const BRepGraph_UID& theOther) const { return !(*this == theOther); } - - bool operator<(const BRepGraph_UID& theOther) const + [[nodiscard]] bool IsAssembly() const { - if (myKind != theOther.myKind) - return static_cast(myKind) < static_cast(theOther.myKind); - return myCounter < theOther.myCounter; + return IsValid() && BRepGraph_NodeId::IsAssemblyKind(Kind); } - //! Hash value: f(Kind, Counter). - [[nodiscard]] size_t HashValue() const + //! Equality: Identity = (Kind, Counter). Two invalid UIDs are equal. + friend bool operator==(const BRepGraph_UID& theLeft, const BRepGraph_UID& theRight) noexcept { + if (theLeft.Counter == 0 || theRight.Counter == 0) + { + return (theLeft.Counter == 0) == (theRight.Counter == 0); + } + return theLeft.Kind == theRight.Kind && theLeft.Counter == theRight.Counter; + } + + friend bool operator!=(const BRepGraph_UID& theLeft, const BRepGraph_UID& theRight) noexcept + { + return !(theLeft == theRight); + } + + friend bool operator<(const BRepGraph_UID& theLeft, const BRepGraph_UID& theRight) noexcept + { + if (theLeft.Kind != theRight.Kind) + { + return static_cast(theLeft.Kind) < static_cast(theRight.Kind); + } + return theLeft.Counter < theRight.Counter; + } + + //! Hash value compatible with operator==. + [[nodiscard]] size_t HashValue() const noexcept + { + if (Counter == 0) + { + return opencascade::hash(0); + } size_t aCombination[2]; - aCombination[0] = opencascade::hash(static_cast(myKind)); - aCombination[1] = opencascade::hash(myCounter); + aCombination[0] = opencascade::hash(static_cast(Kind)); + aCombination[1] = opencascade::hash(Counter); return opencascade::hashBytes(aCombination, sizeof(aCombination)); } - -private: - size_t myCounter; //!< 0 = invalid sentinel; valid counters start at 1. - BRepGraph_NodeId::Kind myKind; //!< Node kind. - uint32_t myGeneration; //!< BRepGraph::Clear() cycle that produced this UID. }; +static_assert(sizeof(BRepGraph_UID) <= 8, "BRepGraph_UID must stay compact"); + //! std::hash specialization for NCollection_DefaultHasher support. template <> struct std::hash diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UIDsView.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UIDsView.cxx index 4b517460ed..5f01b205d2 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UIDsView.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UIDsView.cxx @@ -15,106 +15,78 @@ #include #include +#include namespace { //================================================================================================= -void appendRefUIDReverseIndex(BRepGraph_Data& theData, const BRepGraph_RefId::Kind theKind) +BRepGraph_NodeId resolveRefParentNodeId(const BRepGraphInc_Storage& theStorage, + const BRepGraph_RefId theRefId) { - const NCollection_DynamicArray& aUIDs = theData.myIncStorage.RefUIDs(theKind); - for (BRepGraph_RefId aRefId = BRepGraph_RefId::Start(theKind); aRefId.IsValidIn(aUIDs); ++aRefId) + switch (theRefId.RefKind) { - const BRepGraph_RefUID aUID = aUIDs.Value(static_cast(aRefId.Index)); - if (aUID.IsValid()) - { - theData.myRefUIDToRefId.Bind(aUID, aRefId); + case BRepGraph_RefId::Kind::Shell: { + const BRepGraph_ShellRefId aRefId(theRefId); + if (!aRefId.IsValid(theStorage.NbShellRefs())) + { + return BRepGraph_NodeId(); + } + return theStorage.ShellRef(aRefId).ParentSolidId; } - } -} - -//================================================================================================= - -void appendUIDReverseIndex(BRepGraph_Data& theData, const BRepGraph_NodeId::Kind theKind) -{ - const NCollection_DynamicArray& aUIDs = theData.myIncStorage.UIDs(theKind); - for (BRepGraph_NodeId aNodeId = BRepGraph_NodeId::Start(theKind); aNodeId.IsValidIn(aUIDs); - ++aNodeId) - { - const BRepGraph_UID aUID = aUIDs.Value(static_cast(aNodeId.Index)); - if (aUID.IsValid()) - { - theData.myUIDToNodeId.Bind(aUID, aNodeId); + case BRepGraph_RefId::Kind::Face: { + const BRepGraph_FaceRefId aRefId(theRefId); + if (!aRefId.IsValid(theStorage.NbFaceRefs())) + { + return BRepGraph_NodeId(); + } + return theStorage.FaceRef(aRefId).ParentShellId; } - } -} - -//================================================================================================= - -void ensureUIDReverseIndex(BRepGraph_Data& theData) -{ - const uint32_t aGeneration = theData.myGeneration.load(); - { - std::shared_lock aReadLock(theData.myUIDToNodeIdMutex); - if (!theData.myUIDToNodeIdDirty && theData.myUIDToNodeIdGeneration == aGeneration) - { - return; + case BRepGraph_RefId::Kind::Wire: { + const BRepGraph_WireRefId aRefId(theRefId); + if (!aRefId.IsValid(theStorage.NbWireRefs())) + { + return BRepGraph_NodeId(); + } + return theStorage.WireRef(aRefId).ParentFaceId; } - } - - std::unique_lock aWriteLock(theData.myUIDToNodeIdMutex); - if (!theData.myUIDToNodeIdDirty && theData.myUIDToNodeIdGeneration == aGeneration) - { - return; - } - - theData.myUIDToNodeId.Clear(); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Vertex); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Edge); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::CoEdge); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Wire); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Face); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Shell); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Solid); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Compound); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::CompSolid); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Product); - appendUIDReverseIndex(theData, BRepGraph_NodeId::Kind::Occurrence); - theData.myUIDToNodeIdGeneration = aGeneration; - theData.myUIDToNodeIdDirty = false; -} - -//================================================================================================= - -void ensureRefUIDReverseIndex(BRepGraph_Data& theData) -{ - const uint32_t aGeneration = theData.myGeneration.load(); - { - std::shared_lock aReadLock(theData.myRefUIDToRefIdMutex); - if (!theData.myRefUIDToRefIdDirty && theData.myRefUIDToRefIdGeneration == aGeneration) - { - return; + case BRepGraph_RefId::Kind::Solid: { + const BRepGraph_SolidRefId aRefId(theRefId); + if (!aRefId.IsValid(theStorage.NbSolidRefs())) + { + return BRepGraph_NodeId(); + } + return theStorage.SolidRef(aRefId).ParentCompSolidId; } + case BRepGraph_RefId::Kind::Child: { + const BRepGraph_ChildRefId aRefId(theRefId); + if (!aRefId.IsValid(theStorage.NbChildRefs())) + { + return BRepGraph_NodeId(); + } + return theStorage.ChildRef(aRefId).ParentCompoundId; + } + case BRepGraph_RefId::Kind::Occurrence: { + const BRepGraph_OccurrenceRefId aRefId(theRefId); + if (!aRefId.IsValid(theStorage.NbOccurrenceRefs())) + { + return BRepGraph_NodeId(); + } + return theStorage.OccurrenceRef(aRefId).ParentProductId; + } + case BRepGraph_RefId::Kind::Vertex: { + const BRepGraph_VertexRefId aRefId(theRefId); + if (!aRefId.IsValid(theStorage.NbVertexRefs())) + { + return BRepGraph_NodeId(); + } + return theStorage.VertexRef(aRefId).ParentEdgeId; + } + default: + break; } - - std::unique_lock aWriteLock(theData.myRefUIDToRefIdMutex); - if (!theData.myRefUIDToRefIdDirty && theData.myRefUIDToRefIdGeneration == aGeneration) - { - return; - } - - theData.myRefUIDToRefId.Clear(); - appendRefUIDReverseIndex(theData, BRepGraph_RefId::Kind::Shell); - appendRefUIDReverseIndex(theData, BRepGraph_RefId::Kind::Face); - appendRefUIDReverseIndex(theData, BRepGraph_RefId::Kind::Wire); - appendRefUIDReverseIndex(theData, BRepGraph_RefId::Kind::CoEdge); - appendRefUIDReverseIndex(theData, BRepGraph_RefId::Kind::Vertex); - appendRefUIDReverseIndex(theData, BRepGraph_RefId::Kind::Solid); - appendRefUIDReverseIndex(theData, BRepGraph_RefId::Kind::Child); - appendRefUIDReverseIndex(theData, BRepGraph_RefId::Kind::Occurrence); - theData.myRefUIDToRefIdGeneration = aGeneration; - theData.myRefUIDToRefIdDirty = false; + return BRepGraph_NodeId(); } } // namespace @@ -129,18 +101,12 @@ BRepGraph_UID BRepGraph::UIDsView::Of(const BRepGraph_NodeId theNode) const } const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr || aDef->IsRemoved) + if (aDef == nullptr || theNode.IsRemoved(*myGraph)) { return BRepGraph_UID(); } - const NCollection_DynamicArray& aVec = - myGraph->myData->myIncStorage.UIDs(theNode.NodeKind); - if (!theNode.IsValidIn(aVec)) - { - return BRepGraph_UID(); - } - return aVec.Value(static_cast(theNode.Index)); + return BRepGraph_UID(theNode.NodeKind, aDef->UID); } //================================================================================================= @@ -152,20 +118,45 @@ BRepGraph_RefUID BRepGraph::UIDsView::Of(const BRepGraph_RefId theRefId) const return BRepGraph_RefUID(); } - const NCollection_DynamicArray& aVec = - myGraph->myData->myIncStorage.RefUIDs(theRefId.RefKind); - if (!theRefId.IsValidIn(aVec)) + if (theRefId.IsRemoved(*myGraph)) { return BRepGraph_RefUID(); } - const BRepGraphInc::BaseRef& aBase = myGraph->myData->myIncStorage.BaseRef(theRefId); - if (aBase.IsRemoved) + const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRefId); + if (aRef == nullptr) { return BRepGraph_RefUID(); } - return aVec.Value(static_cast(theRefId.Index)); + return BRepGraph_RefUID(theRefId.RefKind, aRef->UID); +} + +//================================================================================================= + +BRepGraph_ItemUID BRepGraph::UIDsView::Of(const BRepGraph_ItemId theItem) const +{ + if (!theItem.IsValid()) + { + return BRepGraph_ItemUID(); + } + + switch (theItem.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: { + const BRepGraph_UID aUID = Of(theItem.NodeId()); + return aUID.IsValid() ? BRepGraph_ItemUID::Node(theItem.NodeKind(), aUID.Counter) + : BRepGraph_ItemUID(); + } + case BRepGraph_ItemId::Domain::Reference: { + const BRepGraph_RefUID aUID = Of(theItem.RefId()); + return aUID.IsValid() ? BRepGraph_ItemUID::Reference(theItem.RefKind(), aUID.Counter) + : BRepGraph_ItemUID(); + } + case BRepGraph_ItemId::Domain::None: + return BRepGraph_ItemUID(); + } + return BRepGraph_ItemUID(); } //================================================================================================= @@ -176,23 +167,18 @@ BRepGraph_NodeId BRepGraph::UIDsView::NodeIdFrom(const BRepGraph_UID& theUID) co { return BRepGraph_NodeId(); } - if (theUID.Generation() != myGraph->myData->myGeneration.load()) - { - return BRepGraph_NodeId(); - } - BRepGraph_Data& aData = *myGraph->myData; - ensureUIDReverseIndex(aData); + myGraph->myData->myIncStorage.EnsureUIDReverseIndex(); - std::shared_lock aReadLock(aData.myUIDToNodeIdMutex); - const BRepGraph_NodeId* aNodeId = aData.myUIDToNodeId.Seek(theUID); + std::shared_lock aReadLock(myGraph->myData->myIncStorage.myUIDToNodeIdMutex); + const BRepGraph_NodeId* aNodeId = myGraph->myData->myIncStorage.myUIDToNodeId.Seek(theUID); if (aNodeId == nullptr) { return BRepGraph_NodeId(); } const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(*aNodeId); - if (aDef == nullptr || aDef->IsRemoved) + if (aDef == nullptr || aNodeId->IsRemoved(*myGraph)) { return BRepGraph_NodeId(); } @@ -208,23 +194,17 @@ BRepGraph_RefId BRepGraph::UIDsView::RefIdFrom(const BRepGraph_RefUID& theUID) c { return BRepGraph_RefId(); } - if (theUID.Generation() != myGraph->myData->myGeneration.load()) - { - return BRepGraph_RefId(); - } - BRepGraph_Data& aData = *myGraph->myData; - ensureRefUIDReverseIndex(aData); + myGraph->myData->myIncStorage.EnsureRefUIDReverseIndex(); - std::shared_lock aReadLock(aData.myRefUIDToRefIdMutex); - const BRepGraph_RefId* aRefId = aData.myRefUIDToRefId.Seek(theUID); + std::shared_lock aReadLock(myGraph->myData->myIncStorage.myRefUIDToRefIdMutex); + const BRepGraph_RefId* aRefId = myGraph->myData->myIncStorage.myRefUIDToRefId.Seek(theUID); if (aRefId == nullptr) { return BRepGraph_RefId(); } - const BRepGraphInc::BaseRef& aBase = myGraph->myData->myIncStorage.BaseRef(*aRefId); - if (aBase.IsRemoved) + if (aRefId->IsRemoved(*myGraph)) { return BRepGraph_RefId(); } @@ -234,6 +214,29 @@ BRepGraph_RefId BRepGraph::UIDsView::RefIdFrom(const BRepGraph_RefUID& theUID) c //================================================================================================= +BRepGraph_ItemId BRepGraph::UIDsView::ItemIdFrom(const BRepGraph_ItemUID& theUID) const +{ + if (!theUID.IsValid()) + { + return BRepGraph_ItemId(); + } + + switch (theUID.ItemDomain()) + { + case BRepGraph_ItemUID::Domain::Node: + return BRepGraph_ItemId( + NodeIdFrom(BRepGraph_UID(theUID.NodeKind(), static_cast(theUID.Counter())))); + case BRepGraph_ItemUID::Domain::Reference: + return BRepGraph_ItemId( + RefIdFrom(BRepGraph_RefUID(theUID.RefKind(), static_cast(theUID.Counter())))); + case BRepGraph_ItemUID::Domain::None: + return BRepGraph_ItemId(); + } + return BRepGraph_ItemId(); +} + +//================================================================================================= + bool BRepGraph::UIDsView::Has(const BRepGraph_UID& theUID) const { return NodeIdFrom(theUID).IsValid(); @@ -248,16 +251,23 @@ bool BRepGraph::UIDsView::Has(const BRepGraph_RefUID& theUID) const //================================================================================================= +bool BRepGraph::UIDsView::Has(const BRepGraph_ItemUID& theUID) const +{ + return ItemIdFrom(theUID).IsValid(); +} + +//================================================================================================= + uint32_t BRepGraph::UIDsView::Generation() const { - return myGraph->myData->myGeneration.load(); + return myGraph->myData->myIncStorage.Generation(); } //================================================================================================= const Standard_GUID& BRepGraph::UIDsView::GraphGUID() const { - return myGraph->myData->myGraphGUID; + return myGraph->myData->myIncStorage.GraphGUID(); } //================================================================================================= @@ -269,21 +279,19 @@ BRepGraph_VersionStamp BRepGraph::UIDsView::StampOf(const BRepGraph_NodeId theNo return BRepGraph_VersionStamp(); } - const NCollection_DynamicArray& aVec = - myGraph->myData->myIncStorage.UIDs(theNode.NodeKind); - if (!theNode.IsValidIn(aVec)) - { - return BRepGraph_VersionStamp(); - } - - const BRepGraph_UID aUID = aVec.Value(static_cast(theNode.Index)); const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(theNode); - if (aDef == nullptr || aDef->IsRemoved) + if (aDef == nullptr || theNode.IsRemoved(*myGraph)) { return BRepGraph_VersionStamp(); } - return BRepGraph_VersionStamp(aUID, aDef->OwnGen, myGraph->myData->myGeneration.load()); + const BRepGraph_UID aUID(theNode.NodeKind, aDef->UID); + if (!aUID.IsValid()) + { + return BRepGraph_VersionStamp(); + } + + return BRepGraph_VersionStamp(aUID, aDef->OwnGen, myGraph->myData->myIncStorage.Generation()); } //================================================================================================= @@ -295,22 +303,145 @@ BRepGraph_VersionStamp BRepGraph::UIDsView::StampOf(const BRepGraph_RefId theRef return BRepGraph_VersionStamp(); } - const NCollection_DynamicArray& aUIDs = - myGraph->myData->myIncStorage.RefUIDs(theRefId.RefKind); - if (!theRefId.IsValidIn(aUIDs)) + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + const BRepGraph_NodeId aParentNodeId = resolveRefParentNodeId(aStorage, theRefId); + if (!aParentNodeId.IsValid() || theRefId.IsRemoved(*myGraph)) { return BRepGraph_VersionStamp(); } - const BRepGraphInc::BaseRef& aBase = myGraph->myData->myIncStorage.BaseRef(theRefId); - if (aBase.IsRemoved) + const BRepGraphInc::BaseDef* aParentDef = myGraph->topoEntity(aParentNodeId); + if (aParentDef == nullptr || aParentNodeId.IsRemoved(*myGraph)) { return BRepGraph_VersionStamp(); } - return BRepGraph_VersionStamp(aUIDs.Value(static_cast(theRefId.Index)), - aBase.OwnGen, - myGraph->myData->myGeneration.load()); + const BRepGraphInc::BaseRef* aRef = myGraph->refEntity(theRefId); + if (aRef == nullptr) + { + return BRepGraph_VersionStamp(); + } + + const BRepGraph_RefUID aRefUID(theRefId.RefKind, aRef->UID); + if (!aRefUID.IsValid()) + { + return BRepGraph_VersionStamp(); + } + + return BRepGraph_VersionStamp(aRefUID, aParentDef->OwnGen, aStorage.Generation()); +} + +//================================================================================================= + +BRepGraph_VersionStamp BRepGraph::UIDsView::StampOf(const BRepGraph_RepId theRepId) const +{ + if (!theRepId.IsValid()) + { + return BRepGraph_VersionStamp(); + } + + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + BRepGraph_NodeId aParentId; + switch (theRepId.RepKind) + { + case BRepGraph_RepId::Kind::EdgeCurve3D: { + const BRepGraph_EdgeCurve3DRepId aId(theRepId.Index); + if (!aId.IsValid(aStorage.NbEdgeCurves3D()) || aStorage.IsRemoved(aId)) + { + return BRepGraph_VersionStamp(); + } + aParentId = BRepGraph_NodeId(aStorage.EdgeCurve3DRep(aId).ParentEdgeId); + break; + } + case BRepGraph_RepId::Kind::EdgePolygon3D: { + const BRepGraph_EdgePolygon3DRepId aId(theRepId.Index); + if (!aId.IsValid(aStorage.NbEdgePolygons3D()) || aStorage.IsRemoved(aId)) + { + return BRepGraph_VersionStamp(); + } + aParentId = BRepGraph_NodeId(aStorage.EdgePolygon3DRep(aId).ParentEdgeId); + break; + } + case BRepGraph_RepId::Kind::CoEdgeCurve2D: { + const BRepGraph_CoEdgeCurve2DRepId aId(theRepId.Index); + if (!aId.IsValid(aStorage.NbCoEdgeCurves2D()) || aStorage.IsRemoved(aId)) + { + return BRepGraph_VersionStamp(); + } + aParentId = BRepGraph_NodeId(aStorage.CoEdgeCurve2DRep(aId).ParentCoEdgeId); + break; + } + case BRepGraph_RepId::Kind::CoEdgePolygon2D: { + const BRepGraph_CoEdgePolygon2DRepId aId(theRepId.Index); + if (!aId.IsValid(aStorage.NbCoEdgePolygons2D()) || aStorage.IsRemoved(aId)) + { + return BRepGraph_VersionStamp(); + } + aParentId = BRepGraph_NodeId(aStorage.CoEdgePolygon2DRep(aId).ParentCoEdgeId); + break; + } + case BRepGraph_RepId::Kind::CoEdgePolygonOnTri: { + const BRepGraph_CoEdgePolygonOnTriRepId aId(theRepId.Index); + if (!aId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) || aStorage.IsRemoved(aId)) + { + return BRepGraph_VersionStamp(); + } + aParentId = BRepGraph_NodeId(aStorage.CoEdgePolygonOnTriRep(aId).ParentCoEdgeId); + break; + } + case BRepGraph_RepId::Kind::FaceSurface: { + const BRepGraph_FaceSurfaceRepId aId(theRepId.Index); + if (!aId.IsValid(aStorage.NbFaceSurfaces()) || aStorage.IsRemoved(aId)) + { + return BRepGraph_VersionStamp(); + } + aParentId = BRepGraph_NodeId(aStorage.FaceSurfaceRep(aId).ParentFaceId); + break; + } + case BRepGraph_RepId::Kind::FaceTriangulation: { + const BRepGraph_FaceTriangulationRepId aId(theRepId.Index); + if (!aId.IsValid(aStorage.NbFaceTriangulations()) || aStorage.IsRemoved(aId)) + { + return BRepGraph_VersionStamp(); + } + aParentId = BRepGraph_NodeId(aStorage.FaceTriangulationRep(aId).ParentFaceId); + break; + } + default: + return BRepGraph_VersionStamp(); + } + + const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(aParentId); + if (aDef == nullptr || aParentId.IsRemoved(*myGraph)) + { + return BRepGraph_VersionStamp(); + } + + return BRepGraph_VersionStamp(myGraph->UIDs().Of(aParentId), + aDef->OwnGen, + myGraph->myData->myIncStorage.Generation()); +} + +//================================================================================================= + +BRepGraph_VersionStamp BRepGraph::UIDsView::StampOf(const BRepGraph_ItemId theItem) const +{ + if (!theItem.IsValid()) + { + return BRepGraph_VersionStamp(); + } + + switch (theItem.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: + return StampOf(theItem.NodeId()); + case BRepGraph_ItemId::Domain::Reference: + return StampOf(theItem.RefId()); + case BRepGraph_ItemId::Domain::None: + return BRepGraph_VersionStamp(); + } + return BRepGraph_VersionStamp(); } //================================================================================================= @@ -322,21 +453,21 @@ bool BRepGraph::UIDsView::IsStale(const BRepGraph_VersionStamp& theStamp) const return true; } - if (theStamp.myGeneration != myGraph->myData->myGeneration.load()) + if (theStamp.myGeneration != myGraph->myData->myIncStorage.Generation()) { return true; } - if (theStamp.IsEntityStamp()) + if (theStamp.IsNodeStamp()) { - const BRepGraph_NodeId aNodeId = NodeIdFrom(theStamp.myUID); + const BRepGraph_NodeId aNodeId = NodeIdFrom(theStamp.myNodeUID); if (!aNodeId.IsValid()) { return true; } const BRepGraphInc::BaseDef* aDef = myGraph->topoEntity(aNodeId); - if (aDef == nullptr || aDef->IsRemoved) + if (aDef == nullptr || aNodeId.IsRemoved(*myGraph)) { return true; } @@ -352,13 +483,21 @@ bool BRepGraph::UIDsView::IsStale(const BRepGraph_VersionStamp& theStamp) const return true; } - const BRepGraphInc::BaseRef& aRef = myGraph->myData->myIncStorage.BaseRef(aRefId); - if (aRef.IsRemoved) + const BRepGraphInc_Storage& aStorage = myGraph->myData->myIncStorage; + + const BRepGraph_NodeId aParentNodeId = resolveRefParentNodeId(aStorage, aRefId); + if (!aParentNodeId.IsValid() || aRefId.IsRemoved(*myGraph)) { return true; } - return aRef.OwnGen != theStamp.myMutationGen; + const BRepGraphInc::BaseDef* aParentDef = myGraph->topoEntity(aParentNodeId); + if (aParentDef == nullptr || aParentNodeId.IsRemoved(*myGraph)) + { + return true; + } + + return aParentDef->OwnGen != theStamp.myMutationGen; } return true; diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UIDsView.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UIDsView.hxx index 161a183675..0c98960e51 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UIDsView.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UIDsView.hxx @@ -19,13 +19,17 @@ class Standard_GUID; -//! @brief Read-only view for persistent unique identifiers. +//! @brief Read-only view for persistent node and reference identifiers. //! //! UIDs are (Kind, Counter) pairs that persist across graph mutations //! (Compact, node removal). Counters are monotonic and independent of vector //! indices. Clear() starts a new graph generation and refreshes the graph //! GUID, enabling stale-reference detection when a graph is rebuilt. -//! Provides bidirectional NodeId/UID resolution. Obtained via BRepGraph::UIDs(). +//! Provides bidirectional NodeId/UID and RefId/RefUID resolution. +//! +//! Version stamps are exposed here for graph-owned cache and layer freshness +//! checks. They reuse node/reference UID identity and do not introduce a +//! persistent representation identity. class BRepGraph::UIDsView { public: @@ -40,6 +44,11 @@ public: //! removed [[nodiscard]] Standard_EXPORT BRepGraph_RefUID Of(const BRepGraph_RefId theRefId) const; + //! Return the persistent UID assigned to a generic graph item. + //! @param[in] theItem definition-node or reference-entry item id + //! @return durable item UID, or invalid UID if the item is out of bounds or removed + [[nodiscard]] Standard_EXPORT BRepGraph_ItemUID Of(const BRepGraph_ItemId theItem) const; + //! Resolve a UID back to a NodeId using the internal reverse index. //! @param[in] theUID unique identifier to resolve //! @return corresponding active NodeId, or invalid NodeId if not found/removed @@ -50,6 +59,11 @@ public: //! @return corresponding active RefId, or invalid RefId if not found/removed [[nodiscard]] Standard_EXPORT BRepGraph_RefId RefIdFrom(const BRepGraph_RefUID& theUID) const; + //! Resolve a generic item UID back to a transient item id. + //! @param[in] theUID durable node/reference item identity + //! @return active item id, or invalid item id if the UID cannot be resolved + [[nodiscard]] Standard_EXPORT BRepGraph_ItemId ItemIdFrom(const BRepGraph_ItemUID& theUID) const; + //! Check if a UID is valid and exists in this graph generation. //! @param[in] theUID unique identifier to check //! @return true if the UID resolves to an active node in this graph generation @@ -60,6 +74,9 @@ public: //! @return true if the RefUID resolves to an active reference in this graph generation [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_RefUID& theUID) const; + //! Check if a generic item UID exists in this graph generation. + [[nodiscard]] Standard_EXPORT bool Has(const BRepGraph_ItemUID& theUID) const; + //! Return the current generation counter (incremented on each BRepGraph::Clear()). //! @return graph generation number [[nodiscard]] Standard_EXPORT uint32_t Generation() const; @@ -83,8 +100,20 @@ public: [[nodiscard]] Standard_EXPORT BRepGraph_VersionStamp StampOf(const BRepGraph_RefId theRefId) const; + //! Produce a version stamp for an owner-scoped use record. + //! Use records have no durable UID or mutation generation; the stamp uses the owning + //! definition-node UID, OwnGen, and graph Generation. + //! @param[in] theRepId use-record identifier + //! @return version stamp, or invalid stamp if theRepId is invalid, removed, or out of bounds + [[nodiscard]] Standard_EXPORT BRepGraph_VersionStamp + StampOf(const BRepGraph_RepId theRepId) const; + + //! Produce a version stamp for the given definition-node or reference-entry item. + [[nodiscard]] Standard_EXPORT BRepGraph_VersionStamp + StampOf(const BRepGraph_ItemId theItem) const; + //! Check if a previously-taken stamp is stale. - //! A stamp is stale when the stamped node or reference has been mutated, + //! A stamp is stale when the stamped item has been mutated, //! removed, or the graph was rebuilt since the stamp was taken. //! @param[in] theStamp version stamp to check //! @return true if the stamp no longer matches the current graph state @@ -94,12 +123,12 @@ private: friend class BRepGraph; friend struct BRepGraph_Data; - explicit UIDsView(const BRepGraph* theGraph) + explicit UIDsView(BRepGraph* theGraph) : myGraph(theGraph) { } - const BRepGraph* myGraph; + BRepGraph* myGraph; }; #endif // _BRepGraph_UIDsView_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UsagePath.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UsagePath.cxx new file mode 100644 index 0000000000..5602647d9d --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UsagePath.cxx @@ -0,0 +1,61 @@ +// 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 + +//================================================================================================= + +bool BRepGraph_UsagePath::IsEqual(const BRepGraph_UsagePath& theOther) const +{ + if (Size() != theOther.Size()) + { + return false; + } + for (size_t anIdx = 0; anIdx < Size(); ++anIdx) + { + if (!(Value(anIdx) == theOther.Value(anIdx))) + { + return false; + } + } + return true; +} + +//================================================================================================= + +size_t BRepGraph_UsagePath::HashCode() const +{ + const size_t aSize = Size(); + if (aSize == 0) + { + return opencascade::hash(aSize); + } + const Step& aFirst = First(); + if (aSize == 1) + { + size_t aCombination[] = {opencascade::hash(aSize), + opencascade::hash(aFirst.Node), + opencascade::hash(aFirst.Ref), + opencascade::hash(aFirst.StepIndex)}; + return opencascade::hashBytes(aCombination, sizeof(aCombination)); + } + const Step& aLast = Last(); + size_t aCombination[] = {opencascade::hash(aSize), + opencascade::hash(aFirst.Node), + opencascade::hash(aFirst.Ref), + opencascade::hash(aFirst.StepIndex), + opencascade::hash(aLast.Node), + opencascade::hash(aLast.Ref), + opencascade::hash(aLast.StepIndex)}; + return opencascade::hashBytes(aCombination, sizeof(aCombination)); +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UsagePath.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UsagePath.hxx new file mode 100644 index 0000000000..e2728a9107 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_UsagePath.hxx @@ -0,0 +1,122 @@ +// 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. + +#ifndef _BRepGraph_UsagePath_HeaderFile +#define _BRepGraph_UsagePath_HeaderFile + +#include +#include +#include + +#include +#include + +//! Explicit identity of a concrete usage from traversal root to selected node. +//! +//! A usage path is an ordered sequence of steps that records the exact +//! traversal from a root node down to a specific graph entity. Each step +//! captures the node reached, the reference through which it was reached, +//! and the sibling order (step index) at that level. +//! +//! Paths are used to disambiguate multiple occurrences of the same +//! definition reachable through different references or sibling positions. +class BRepGraph_UsagePath +{ +public: + //! One concrete traversal step in a usage path. + //! + //! Ref is valid for reference-owned links and invalid for structural links + //! such as CoEdge -> Edge or Occurrence -> Product/topology-root. Step keeps + //! sibling order explicit, so coincident or structurally-linked usages remain + //! distinguishable without relying on location or hashes. + struct Step + { + BRepGraph_NodeId Node; + BRepGraph_RefId Ref; + int StepIndex = -1; + + bool operator==(const Step& theOther) const + { + return Node == theOther.Node && Ref == theOther.Ref && StepIndex == theOther.StepIndex; + } + }; + +public: + //! Creates an empty usage path. + BRepGraph_UsagePath() = default; + + //! Creates a usage path with pre-allocated capacity. + //! @param[in] theCapacity number of steps to pre-allocate + explicit BRepGraph_UsagePath(const size_t theCapacity) + : mySteps(theCapacity) + { + } + + //! Returns the number of steps in the path. + size_t Size() const { return mySteps.Size(); } + + //! Returns true if the path has no steps. + bool IsEmpty() const { return mySteps.IsEmpty(); } + + //! Returns the step at the given index. + //! @param[in] theIdx zero-based index + const Step& Value(const size_t theIdx) const { return mySteps.Value(theIdx); } + + //! Returns the first step in the path. + const Step& First() const { return mySteps.First(); } + + //! Returns the last step in the path. + const Step& Last() const { return mySteps.Last(); } + + //! Appends a step to the end of the path. + //! @param[in] theStep step to append + void Append(Step theStep) { mySteps.Append(std::move(theStep)); } + + //! Inserts a step before the given index. + //! @param[in] theIdx zero-based index to insert before + //! @param[in] theStep step to insert + void InsertBefore(const size_t theIdx, Step theStep) + { + mySteps.InsertBefore(theIdx, std::move(theStep)); + } + + //! Removes all steps from the path. + void Clear() { mySteps.Clear(); } + + //! Returns true if this path is equal to the other path. + //! @param[in] theOther path to compare with + bool IsEqual(const BRepGraph_UsagePath& theOther) const; + + //! Returns true if this path is equal to the other path. + //! @param[in] theOther path to compare with + bool operator==(const BRepGraph_UsagePath& theOther) const { return IsEqual(theOther); } + + //! Returns a hash code for this path. + //! Uses first step, last step, and size for O(1) computation. + size_t HashCode() const; + +private: + NCollection_LinearVector mySteps; +}; + +//! std::hash specialization for BRepGraph_UsagePath. +template <> +struct std::hash +{ + size_t operator()(const BRepGraph_UsagePath& thePath) const noexcept + { + return thePath.HashCode(); + } +}; + +#endif // _BRepGraph_UsagePath_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.cxx index 2d658a1b4a..83b4106829 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.cxx @@ -12,24 +12,27 @@ // 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 namespace { @@ -74,15 +77,15 @@ bool isValidNodeId(const BRepGraph& theGraph, const BRepGraph_NodeId theId) bool isEntityRemoved(const BRepGraph& theGraph, BRepGraph_NodeId theId) { const BRepGraphInc::BaseDef* aDef = theGraph.Topo().Gen().TopoEntity(theId); - return aDef != nullptr && aDef->IsRemoved; + return aDef != nullptr && theId.IsRemoved(theGraph); } //! Convert mutator boundary issues to validator issues. //! @param[in] theBoundaryIssues boundary issues reported by mutator //! @param[in,out] theIssues destination validator issue vector void appendMutationBoundaryIssues( - const NCollection_DynamicArray& theBoundaryIssues, - NCollection_DynamicArray& theIssues) + const NCollection_LinearVector& theBoundaryIssues, + NCollection_LinearVector& theIssues) { using Issue = BRepGraph_Validate::Issue; using Severity = BRepGraph_Validate::Severity; @@ -97,7 +100,7 @@ void appendMutationBoundaryIssues( //! @param[in] theGraph source graph //! @param[in,out] theIssues collection to append diagnostic issues void checkCrossReferenceBounds(const BRepGraph& theGraph, - NCollection_DynamicArray& theIssues) + NCollection_LinearVector& theIssues) { using Issue = BRepGraph_Validate::Issue; using Severity = BRepGraph_Validate::Severity; @@ -112,23 +115,23 @@ void checkCrossReferenceBounds(const BRepGraph& if (anEdge.StartVertexRefId.IsValid()) { const BRepGraph_NodeId aStartVtxId = - theGraph.Refs().Vertices().Entry(anEdge.StartVertexRefId).VertexDefId; + theGraph.Refs().Vertices().Entry(anEdge.StartVertexRefId).ChildVertexId; if (aStartVtxId.IsValid() && !isValidNodeId(theGraph, aStartVtxId)) { theIssues.Append(Issue{Severity::Error, anEdgeIt.CurrentId(), - "EdgeDef.StartVertexRefId resolves to out-of-bounds VertexDefId"}); + "EdgeDef.StartVertexRefId resolves to out-of-bounds ChildVertexId"}); } } if (anEdge.EndVertexRefId.IsValid()) { const BRepGraph_NodeId anEndVtxId = - theGraph.Refs().Vertices().Entry(anEdge.EndVertexRefId).VertexDefId; + theGraph.Refs().Vertices().Entry(anEdge.EndVertexRefId).ChildVertexId; if (anEndVtxId.IsValid() && !isValidNodeId(theGraph, anEndVtxId)) { theIssues.Append(Issue{Severity::Error, anEdgeIt.CurrentId(), - "EdgeDef.EndVertexRefId resolves to out-of-bounds VertexDefId"}); + "EdgeDef.EndVertexRefId resolves to out-of-bounds ChildVertexId"}); } } } @@ -140,30 +143,32 @@ void checkCrossReferenceBounds(const BRepGraph& const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Current(); const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); - if (aCoEdge.FaceDefId.IsValid() && !isValidNodeId(theGraph, aCoEdge.FaceDefId)) + if (aCoEdge.FaceId.IsValid() && !isValidNodeId(theGraph, aCoEdge.FaceId)) { - theIssues.Append(Issue{Severity::Error, aCoEdgeId, "CoEdgeDef.FaceDefId out of bounds"}); + theIssues.Append(Issue{Severity::Error, aCoEdgeId, "CoEdgeDef.FaceId out of bounds"}); } - const BRepGraph_NodeId anEdgeId = aCoEdge.EdgeDefId; + const BRepGraph_NodeId anEdgeId = aCoEdge.ChildEdgeId; if (anEdgeId.IsValid() && !isValidNodeId(theGraph, anEdgeId)) { - theIssues.Append(Issue{Severity::Error, aCoEdgeId, "CoEdgeDef.EdgeDefId out of bounds"}); + theIssues.Append(Issue{Severity::Error, aCoEdgeId, "CoEdgeDef.ChildEdgeId out of bounds"}); } - // Seam consistency: for every (EdgeDefId, FaceDefId) pair there are at most + // Seam consistency: for every (ChildEdgeId, FaceId) pair there are at most // 2 CoEdges; if 2, they must have opposite Orientation. - if (aCoEdge.EdgeDefId.IsValid() && aCoEdge.FaceDefId.IsValid()) + if (aCoEdge.ChildEdgeId.IsValid() && aCoEdge.FaceId.IsValid()) { uint32_t aSameFaceCount = 0; bool aHasOppositeOnSameFace = false; for (BRepGraph_CoEdgesOfEdge anIt(theGraph, - theGraph.Topo().Edges().CoEdges(aCoEdge.EdgeDefId)); + theGraph.Topo().Edges().CoEdges(aCoEdge.ChildEdgeId)); anIt.More(); anIt.Next()) { const BRepGraph_CoEdgeId aOtherId = anIt.CurrentId(); const BRepGraphInc::CoEdgeDef& aOther = anIt.Definition(); - if (aOther.FaceDefId != aCoEdge.FaceDefId) + if (aOther.FaceId != aCoEdge.FaceId) + { continue; + } ++aSameFaceCount; if (aOtherId != aCoEdgeId && aOther.Orientation != aCoEdge.Orientation) { @@ -174,7 +179,7 @@ void checkCrossReferenceBounds(const BRepGraph& { theIssues.Append(Issue{Severity::Error, aCoEdgeId, - "More than 2 CoEdges share the same (EdgeDefId, FaceDefId)"}); + "More than 2 CoEdges share the same (ChildEdgeId, FaceId)"}); } else if (aSameFaceCount == 2 && !aHasOppositeOnSameFace) { @@ -184,7 +189,7 @@ void checkCrossReferenceBounds(const BRepGraph& } // Rep index bounds. if (aCoEdge.Curve2DRepId.IsValid() - && !aCoEdge.Curve2DRepId.IsValid(theGraph.Topo().Geometry().NbCurves2D())) + && !aCoEdge.Curve2DRepId.IsValid(theGraph.Topo().Geometry().NbCoEdgeCurves2D())) { theIssues.Append(Issue{Severity::Error, aCoEdgeId, "CoEdgeDef.Curve2DRepId out of bounds"}); } @@ -192,24 +197,23 @@ void checkCrossReferenceBounds(const BRepGraph& // Surface handles are stored directly on FaceDef; no cross-reference to validate. - // Check WireDef CoEdgeRef references. + // Check WireDef coedge sequence. for (BRepGraph_Iterator aWireIt(theGraph); aWireIt.More(); aWireIt.Next()) { const BRepGraph_WireId aWireId = aWireIt.CurrentId(); - for (BRepGraph_RefsCoEdgeOfWire anIt(theGraph, aWireId); anIt.More(); anIt.Next()) + for (BRepGraph_CoEdgesOfWire anIt(theGraph, aWireId); anIt.More(); anIt.Next()) { - const BRepGraphInc::CoEdgeRef& aCR = theGraph.Refs().CoEdges().Entry(anIt.CurrentId()); - const BRepGraph_NodeId aCoEdgeDefId = aCR.CoEdgeDefId; - if (aCoEdgeDefId.IsValid() && !isValidNodeId(theGraph, aCoEdgeDefId)) + const BRepGraph_NodeId aCoChildEdgeId = anIt.CurrentId(); + if (aCoChildEdgeId.IsValid() && !isValidNodeId(theGraph, aCoChildEdgeId)) { theIssues.Append( Issue{Severity::Error, aWireId, "WireDef.CoEdgeUsage CoEdgeIdx out of bounds"}); } const BRepGraphInc::CoEdgeDef& aCoEdge = - theGraph.Topo().CoEdges().Definition(aCR.CoEdgeDefId); - const BRepGraph_NodeId anEdgeDefId = aCoEdge.EdgeDefId; - if (anEdgeDefId.IsValid() && !isValidNodeId(theGraph, anEdgeDefId)) + theGraph.Topo().CoEdges().Definition(anIt.CurrentId()); + const BRepGraph_NodeId anChildEdgeId = aCoEdge.ChildEdgeId; + if (anChildEdgeId.IsValid() && !isValidNodeId(theGraph, anChildEdgeId)) { theIssues.Append( Issue{Severity::Error, aWireId, "WireDef.CoEdgeUsage EdgeIdx out of bounds"}); @@ -226,11 +230,11 @@ void checkCrossReferenceBounds(const BRepGraph& for (BRepGraph_RefsChildOfCompound anIt(theGraph, aCompoundId); anIt.More(); anIt.Next()) { const BRepGraphInc::ChildRef& aCR = theGraph.Refs().Children().Entry(anIt.CurrentId()); - const BRepGraph_NodeId aChildId = aCR.ChildDefId; + const BRepGraph_NodeId aChildId = aCR.ChildNodeId; if (aChildId.IsValid() && !isValidNodeId(theGraph, aChildId)) { theIssues.Append( - Issue{Severity::Error, aCompoundId, "CompoundDef.ChildDefId out of bounds"}); + Issue{Severity::Error, aCompoundId, "CompoundDef.ChildNodeId out of bounds"}); } } } @@ -243,11 +247,11 @@ void checkCrossReferenceBounds(const BRepGraph& for (BRepGraph_RefsSolidOfCompSolid anIt(theGraph, aCompSolidId); anIt.More(); anIt.Next()) { const BRepGraphInc::SolidRef& aSR = theGraph.Refs().Solids().Entry(anIt.CurrentId()); - const BRepGraph_NodeId aSolidId = aSR.SolidDefId; + const BRepGraph_NodeId aSolidId = aSR.ChildSolidId; if (aSolidId.IsValid() && !isValidNodeId(theGraph, aSolidId)) { theIssues.Append( - Issue{Severity::Error, aCompSolidId, "CompSolidDef.SolidDefId out of bounds"}); + Issue{Severity::Error, aCompSolidId, "CompSolidDef.ChildSolidId out of bounds"}); } } } @@ -260,9 +264,9 @@ void checkCrossReferenceBounds(const BRepGraph& for (BRepGraph_RefsFaceOfShell anIt(theGraph, aShellId); anIt.More(); anIt.Next()) { - const BRepGraphInc::FaceRef& aFR = theGraph.Refs().Faces().Entry(anIt.CurrentId()); - const BRepGraph_NodeId aFaceDefId = aFR.FaceDefId; - if (aFaceDefId.IsValid() && !isValidNodeId(theGraph, aFaceDefId)) + const BRepGraphInc::FaceRef& aFR = theGraph.Refs().Faces().Entry(anIt.CurrentId()); + const BRepGraph_NodeId aFaceId = aFR.ChildFaceId; + if (aFaceId.IsValid() && !isValidNodeId(theGraph, aFaceId)) { theIssues.Append( Issue{Severity::Error, aShellId, "ShellDef.FaceUsage FaceIdx out of bounds"}); @@ -278,9 +282,9 @@ void checkCrossReferenceBounds(const BRepGraph& for (BRepGraph_RefsShellOfSolid anIt(theGraph, aSolidId); anIt.More(); anIt.Next()) { - const BRepGraphInc::ShellRef& aSR = theGraph.Refs().Shells().Entry(anIt.CurrentId()); - const BRepGraph_NodeId aShellDefId = aSR.ShellDefId; - if (aShellDefId.IsValid() && !isValidNodeId(theGraph, aShellDefId)) + const BRepGraphInc::ShellRef& aSR = theGraph.Refs().Shells().Entry(anIt.CurrentId()); + const BRepGraph_NodeId aChildShellId = aSR.ChildShellId; + if (aChildShellId.IsValid() && !isValidNodeId(theGraph, aChildShellId)) { theIssues.Append( Issue{Severity::Error, aSolidId, "SolidDef.ShellUsage ShellIdx out of bounds"}); @@ -298,11 +302,11 @@ void checkCrossReferenceBounds(const BRepGraph& anOccIt.Next()) { const BRepGraph_OccurrenceId anOccId = - theGraph.Refs().Occurrences().Entry(anOccIt.CurrentId()).OccurrenceDefId; + theGraph.Refs().Occurrences().Entry(anOccIt.CurrentId()).ChildOccurrenceId; if (anOccId.IsValid() && !isValidNodeId(theGraph, anOccId)) { theIssues.Append( - Issue{Severity::Error, aProdId, "ProductDef.OccurrenceUsage out of bounds"}); + Issue{Severity::Error, aProdId, "ProductDef.OccurrenceRefId out of bounds"}); } } } @@ -313,12 +317,12 @@ void checkCrossReferenceBounds(const BRepGraph& { const BRepGraphInc::OccurrenceDef& anOcc = anOccIt.Current(); const BRepGraph_OccurrenceId anOccId = anOccIt.CurrentId(); - const BRepGraph_NodeId aChildId = anOcc.ChildDefId; + const BRepGraph_NodeId aChildId = anOcc.ChildNodeId; const BRepGraph_NodeId::Kind aKind = aChildId.NodeKind; if (!aChildId.IsValid()) { - theIssues.Append(Issue{Severity::Error, anOccId, "OccurrenceDef.ChildDefId invalid"}); + theIssues.Append(Issue{Severity::Error, anOccId, "OccurrenceDef.ChildNodeId invalid"}); continue; } @@ -330,49 +334,48 @@ void checkCrossReferenceBounds(const BRepGraph& Issue{Severity::Error, anOccId, aKind == BRepGraph_NodeId::Kind::Occurrence - ? "OccurrenceDef.ChildDefId cannot reference an Occurrence" - : "OccurrenceDef.ChildDefId kind is not Product or a topology kind"}); + ? "OccurrenceDef.ChildNodeId cannot reference an Occurrence" + : "OccurrenceDef.ChildNodeId kind is not Product or a topology kind"}); continue; } if (!isValidNodeId(theGraph, aChildId) || isEntityRemoved(theGraph, aChildId)) { - theIssues.Append(Issue{Severity::Error, anOccId, "OccurrenceDef.ChildDefId invalid"}); + theIssues.Append(Issue{Severity::Error, anOccId, "OccurrenceDef.ChildNodeId invalid"}); } } } -//! Verify that every forward incidence ref has a matching reverse-index entry +//! Verify that every forward incidence ref has a matching relation entry //! (edge->wires, edge->faces, vertex->edges, wire->faces, face->shells, shell->solids). //! @param[in] theGraph source graph //! @param[in,out] theIssues collection to append diagnostic issues -void checkReverseIndexConsistency(const BRepGraph& theGraph, - NCollection_DynamicArray& theIssues) +void checkRelationConsistency(const BRepGraph& theGraph, + NCollection_LinearVector& theIssues) { using Issue = BRepGraph_Validate::Issue; using Severity = BRepGraph_Validate::Severity; - // Build expected edge->wires mapping from CoEdgeRef scans. - NCollection_DataMap> anExpected; + // Build expected edge->wires mapping from wire-owned coedge scans. + NCollection_DataMap> anExpected; for (BRepGraph_Iterator aWireIt(theGraph); aWireIt.More(); aWireIt.Next()) { const BRepGraph_WireId aWireId = aWireIt.CurrentId(); - for (BRepGraph_RefsCoEdgeOfWire anIt(theGraph, aWireId); anIt.More(); anIt.Next()) + for (BRepGraph_CoEdgesOfWire anIt(theGraph, aWireId); anIt.More(); anIt.Next()) { - const BRepGraphInc::CoEdgeRef& aCR = theGraph.Refs().CoEdges().Entry(anIt.CurrentId()); const BRepGraphInc::CoEdgeDef& aCoEdge = - theGraph.Topo().CoEdges().Definition(aCR.CoEdgeDefId); - if (!aCoEdge.EdgeDefId.IsValid()) + theGraph.Topo().CoEdges().Definition(anIt.CurrentId()); + if (!aCoEdge.ChildEdgeId.IsValid()) { continue; } - if (!anExpected.IsBound(aCoEdge.EdgeDefId)) + if (!anExpected.IsBound(aCoEdge.ChildEdgeId)) { - anExpected.Bind(aCoEdge.EdgeDefId, NCollection_Map()); + anExpected.Bind(aCoEdge.ChildEdgeId, NCollection_FlatMap()); } - anExpected.ChangeFind(aCoEdge.EdgeDefId).Add(aWireId); + anExpected.ChangeFind(aCoEdge.ChildEdgeId).Add(aWireId); } } @@ -382,36 +385,62 @@ void checkReverseIndexConsistency(const BRepGraph& { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aActualWires = - theGraph.Topo().Edges().Wires(anEdgeId); - const NCollection_Map* anExpectedWires = anExpected.Seek(anEdgeId); + const NCollection_FlatMap* anExpectedWires = anExpected.Seek(anEdgeId); - const int anExpectedCount = (anExpectedWires != nullptr) ? anExpectedWires->Extent() : 0; + const uint32_t anExpectedCount = + (anExpectedWires != nullptr) ? static_cast(anExpectedWires->Size()) : 0u; // Build a set from actual wires for comparison. - NCollection_Map anActualSet; - for (const BRepGraph_WireId& aWireId : aActualWires) + NCollection_FlatMap anActualSet; + for (BRepGraph_WiresOfEdge aWireIt = theGraph.Topo().Edges().WiresOf(anEdgeId); aWireIt.More(); + aWireIt.Next()) { - anActualSet.Add(aWireId); + anActualSet.Add(aWireIt.CurrentId()); } - if (anActualSet.Extent() != anExpectedCount) + if (static_cast(anActualSet.Size()) != anExpectedCount) { - theIssues.Append(Issue{Severity::Error, - anEdgeIt.CurrentId(), - "Reverse index ReverseIdx.WiresOfEdge size mismatch"}); + TCollection_AsciiString aDesc("Relation Edges.Wires size mismatch: expected="); + aDesc += TCollection_AsciiString(static_cast(anExpectedCount)); + aDesc += ", actual="; + aDesc += TCollection_AsciiString(static_cast(anActualSet.Size())); + aDesc += ", edge="; + aDesc += TCollection_AsciiString(static_cast(anEdgeId.Index)); + aDesc += ", actualWires=["; + bool isFirstActual = true; + for (BRepGraph_WiresOfEdge aWireIt = theGraph.Topo().Edges().WiresOf(anEdgeId); + aWireIt.More(); + aWireIt.Next()) + { + if (!isFirstActual) + { + aDesc += ","; + } + isFirstActual = false; + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); + aDesc += TCollection_AsciiString(static_cast(aWireId.Index)); + if (!aWireId.IsValid(theGraph.Topo().Wires().Nb()) || aWireId.IsRemoved(theGraph)) + { + aDesc += " removed"; + } + } + aDesc += "]"; + theIssues.Append(Issue{Severity::Error, anEdgeIt.CurrentId(), aDesc}); continue; } if (anExpectedWires != nullptr) { - for (const BRepGraph_WireId& aWireId : *anExpectedWires) + for (NCollection_FlatMap::Iterator aWireIt(*anExpectedWires); + aWireIt.More(); + aWireIt.Next()) { + const BRepGraph_WireId aWireId = aWireIt.Value(); if (!anActualSet.Contains(aWireId)) { theIssues.Append(Issue{Severity::Error, anEdgeIt.CurrentId(), - "Reverse index ReverseIdx.WiresOfEdge missing wire entry"}); + "Relation Edges.Wires missing wire entry"}); break; } } @@ -419,11 +448,11 @@ void checkReverseIndexConsistency(const BRepGraph& } } -//! Validate consistency of cached edge-face counts in reverse index. +//! Validate consistency of cached edge-face counts in relation storage. //! @param[in] theGraph source graph //! @param[in,out] theIssues collection to append diagnostic issues -void checkReverseIndexFaceCountCache(const BRepGraph& theGraph, - NCollection_DynamicArray& theIssues) +void checkRelationFaceCountCache(const BRepGraph& theGraph, + NCollection_LinearVector& theIssues) { using Issue = BRepGraph_Validate::Issue; using Severity = BRepGraph_Validate::Severity; @@ -434,26 +463,43 @@ void checkReverseIndexFaceCountCache(const BRepGraph& { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - NCollection_Map aUniqueFaces; - const NCollection_DynamicArray& aCoEdges = aDefs.Edges().CoEdges(anEdgeId); + NCollection_FlatMap aUniqueFaces; + const NCollection_LinearVector& aCoEdges = aDefs.Edges().CoEdges(anEdgeId); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { const BRepGraphInc::CoEdgeDef& aCoEdge = aDefs.CoEdges().Definition(aCoEdgeId); - if (aCoEdge.IsRemoved || !aCoEdge.FaceDefId.IsValid()) + if (BRepGraph_NodeId(aCoEdgeId).IsRemoved(theGraph) || !aCoEdge.FaceId.IsValid()) { continue; } - aUniqueFaces.Add(aCoEdge.FaceDefId); + aUniqueFaces.Add(aCoEdge.FaceId); } const uint32_t aCachedCount = aDefs.Edges().NbFaces(anEdgeId); - const int anActualCount = aUniqueFaces.Extent(); - if (static_cast(aCachedCount) != anActualCount) + const uint32_t anActualCount = static_cast(aUniqueFaces.Size()); + if (aCachedCount != anActualCount) { - TCollection_AsciiString aDesc("Reverse index face-count cache mismatch: cached="); + TCollection_AsciiString aDesc("Relation face-count cache mismatch: cached="); aDesc += TCollection_AsciiString(static_cast(aCachedCount)); aDesc += " actual="; - aDesc += TCollection_AsciiString(anActualCount); + aDesc += TCollection_AsciiString(static_cast(anActualCount)); + aDesc += " edge="; + aDesc += TCollection_AsciiString(static_cast(anEdgeId.Index)); + aDesc += " faces=["; + bool isFirstFace = true; + for (BRepGraph_FacesOfEdge aFaceIt = theGraph.Topo().Edges().FacesOf(anEdgeId); + aFaceIt.More(); + aFaceIt.Next()) + { + if (!isFirstFace) + { + aDesc += ","; + } + isFirstFace = false; + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + aDesc += TCollection_AsciiString(static_cast(aFaceId.Index)); + } + aDesc += "]"; theIssues.Append(Issue{Severity::Error, anEdgeId, aDesc}); } } @@ -465,7 +511,7 @@ void checkReverseIndexFaceCountCache(const BRepGraph& //! @param[in] theGraph source graph //! @param[in,out] theIssues collection to append diagnostic issues void checkIncidenceRefConsistency(const BRepGraph& theGraph, - NCollection_DynamicArray& theIssues) + NCollection_LinearVector& theIssues) { using Issue = BRepGraph_Validate::Issue; using Severity = BRepGraph_Validate::Severity; @@ -478,7 +524,7 @@ void checkIncidenceRefConsistency(const BRepGraph& for (BRepGraph_RefsWireOfFace anIt(theGraph, aFaceId); anIt.More(); anIt.Next()) { const BRepGraphInc::WireRef& aWR = theGraph.Refs().Wires().Entry(anIt.CurrentId()); - const BRepGraph_NodeId aWireId = aWR.WireDefId; + const BRepGraph_NodeId aWireId = aWR.ChildWireId; if (aWireId.IsValid() && !isValidNodeId(theGraph, aWireId)) { theIssues.Append( @@ -496,7 +542,7 @@ void checkIncidenceRefConsistency(const BRepGraph& for (BRepGraph_RefsFaceOfShell anIt(theGraph, aShellId); anIt.More(); anIt.Next()) { const BRepGraphInc::FaceRef& aFR = theGraph.Refs().Faces().Entry(anIt.CurrentId()); - const BRepGraph_NodeId aFaceId = aFR.FaceDefId; + const BRepGraph_NodeId aFaceId = aFR.ChildFaceId; if (aFaceId.IsValid() && isEntityRemoved(theGraph, aFaceId)) { theIssues.Append(Issue{Severity::Error, aShellId, "ShellDef references removed FaceDef"}); @@ -513,7 +559,7 @@ void checkIncidenceRefConsistency(const BRepGraph& for (BRepGraph_RefsShellOfSolid anIt(theGraph, aSolidId); anIt.More(); anIt.Next()) { const BRepGraphInc::ShellRef& aSR = theGraph.Refs().Shells().Entry(anIt.CurrentId()); - const BRepGraph_NodeId aShellId = aSR.ShellDefId; + const BRepGraph_NodeId aShellId = aSR.ChildShellId; if (aShellId.IsValid() && isEntityRemoved(theGraph, aShellId)) { theIssues.Append(Issue{Severity::Error, aSolidId, "SolidDef references removed ShellDef"}); @@ -522,17 +568,28 @@ void checkIncidenceRefConsistency(const BRepGraph& } } -//! Validate that geometry representation ids (SurfaceRepId, Curve3DRepId, -//! Curve2DRepId, TriangulationRepIds, Polygon3DRepId) are in bounds and +//! Validate that geometry use ids (SurfaceRepId, Curve3DRepId, +//! Curve2DRepId, TriangulationRepId, Polygon3DRepId) are in bounds and //! reference non-null geometry handles. //! @param[in] theGraph source graph //! @param[in,out] theIssues collection to append diagnostic issues void checkGeometryReferences(const BRepGraph& theGraph, - NCollection_DynamicArray& theIssues) + NCollection_LinearVector& theIssues) { using Issue = BRepGraph_Validate::Issue; using Severity = BRepGraph_Validate::Severity; + const auto hasActiveSurface = [&](const BRepGraph_FaceId theFaceId) -> bool { + if (!isValidNodeId(theGraph, theFaceId) || isEntityRemoved(theGraph, theFaceId)) + { + return false; + } + const BRepGraph_FaceSurfaceRepId aSurfaceRepId = + theGraph.Topo().Faces().Definition(theFaceId).SurfaceRepId; + return aSurfaceRepId.IsValid(theGraph.Topo().Geometry().NbFaceSurfaces()) + && !aSurfaceRepId.IsRemoved(theGraph); + }; + // Check edge->curve references (handles stored directly on EdgeDef). for (BRepGraph_Iterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) @@ -540,21 +597,45 @@ void checkGeometryReferences(const BRepGraph& const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); const BRepGraphInc::EdgeDef& anEdge = anEdgeIt.Current(); - if (!BRepGraph_Tool::Edge::Degenerated(theGraph, anEdgeId) + bool needsCurve3D = true; + BRepGraph_FacesOfEdge aFaceIt = theGraph.Topo().Edges().FacesOf(anEdgeId); + if (aFaceIt.More()) + { + needsCurve3D = false; + for (; aFaceIt.More(); aFaceIt.Next()) + { + if (hasActiveSurface(aFaceIt.CurrentId())) + { + needsCurve3D = true; + break; + } + } + } + + if (needsCurve3D && !BRepGraph_Tool::Edge::Degenerated(theGraph, anEdgeId) && !BRepGraph_Tool::Edge::HasCurve(theGraph, anEdgeId)) { - theIssues.Append(Issue{Severity::Error, - anEdgeIt.CurrentId(), - "Non-degenerate EdgeDef has no Curve3D representation"}); + if (anEdge.Curve3DRepId.IsValid() && anEdge.Curve3DRepId.IsRemoved(theGraph)) + { + theIssues.Append(Issue{Severity::Error, + anEdgeIt.CurrentId(), + "Non-degenerate EdgeDef references a removed Curve3DRep"}); + } + else + { + theIssues.Append(Issue{Severity::Error, + anEdgeIt.CurrentId(), + "Non-degenerate EdgeDef has no Curve3D representation"}); + } } if (anEdge.Curve3DRepId.IsValid() - && !anEdge.Curve3DRepId.IsValid(theGraph.Topo().Geometry().NbCurves3D())) + && !anEdge.Curve3DRepId.IsValid(theGraph.Topo().Geometry().NbEdgeCurves3D())) { theIssues.Append( Issue{Severity::Error, anEdgeIt.CurrentId(), "EdgeDef.Curve3DRepId out of bounds"}); } if (anEdge.Polygon3DRepId.IsValid() - && !anEdge.Polygon3DRepId.IsValid(theGraph.Mesh().Poly().NbPolygons3D())) + && !anEdge.Polygon3DRepId.IsValid(theGraph.Mesh().Poly().NbEdgePolygons3D())) { theIssues.Append( Issue{Severity::Error, anEdgeIt.CurrentId(), "EdgeDef.Polygon3DRepId out of bounds"}); @@ -566,31 +647,43 @@ void checkGeometryReferences(const BRepGraph& { const BRepGraphInc::FaceDef& aFace = aFaceIt.Current(); if (aFace.SurfaceRepId.IsValid() - && !aFace.SurfaceRepId.IsValid(theGraph.Topo().Geometry().NbSurfaces())) + && !aFace.SurfaceRepId.IsValid(theGraph.Topo().Geometry().NbFaceSurfaces())) { theIssues.Append( Issue{Severity::Error, aFaceIt.CurrentId(), "FaceDef.SurfaceRepId out of bounds"}); } if (aFace.TriangulationRepId.IsValid() - && !aFace.TriangulationRepId.IsValid(theGraph.Mesh().Poly().NbTriangulations())) + && !aFace.TriangulationRepId.IsValid(theGraph.Mesh().Poly().NbFaceTriangulations())) { theIssues.Append( Issue{Severity::Error, aFaceIt.CurrentId(), "FaceDef.TriangulationRepId out of bounds"}); } // Validate cached mesh entry bounds. - const BRepGraph_MeshCache::FaceMeshEntry* aCachedFace = - theGraph.Mesh().Faces().CachedMesh(aFaceIt.CurrentId()); + const BRepGraph_CacheMesh::FaceMeshEntry* aCachedFace = + theGraph.Mesh().Cache().Faces().Entry(aFaceIt.CurrentId()); if (aCachedFace != nullptr) { - for (const BRepGraph_TriangulationRepId& aCTriRepId : aCachedFace->TriangulationRepIds) + if (aCachedFace->Triangulation.IsNull()) { - if (!aCTriRepId.IsValid(theGraph.Mesh().Poly().NbTriangulations())) - { - theIssues.Append(Issue{Severity::Error, - aFaceIt.CurrentId(), - "MeshCache.FaceMesh.TriangulationRepId out of bounds"}); - break; - } + theIssues.Append( + Issue{Severity::Error, aFaceIt.CurrentId(), "CacheMesh.FaceMesh.Triangulation is null"}); + } + } + } + + // A surfaced face without wire refs is allowed for intentionally unbounded + // natural faces. Keep it diagnostic so callers can decide whether to accept it. + for (BRepGraph_Iterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraphInc::FaceDef& aFace = aFaceIt.Current(); + if (aFace.SurfaceRepId.IsValid(theGraph.Topo().Geometry().NbFaceSurfaces()) + && !aFace.SurfaceRepId.IsRemoved(theGraph)) + { + const bool hasWire = BRepGraph_RefsWireOfFace(theGraph, aFaceIt.CurrentId()).More(); + if (!hasWire) + { + theIssues.Append( + Issue{Severity::Warning, aFaceIt.CurrentId(), "Surfaced face has no wire refs"}); } } } @@ -602,77 +695,326 @@ void checkGeometryReferences(const BRepGraph& const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Current(); const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); - if (aCoEdge.FaceDefId.IsValid() && !BRepGraph_Tool::CoEdge::HasPCurve(theGraph, aCoEdgeId)) + const bool needsPCurve = hasActiveSurface(aCoEdge.FaceId); + if (needsPCurve && !BRepGraph_Tool::CoEdge::HasPCurve(theGraph, aCoEdgeId)) { theIssues.Append( Issue{Severity::Error, aCoEdgeId, "CoEdgeDef has no Curve2D representation"}); } } +} + +} // namespace + +//================================================================================================= + +bool BRepGraph_Validate::Result::IsValid() const +{ + for (const Issue& anIssue : Issues) + { + if (anIssue.Sev == Severity::Error) + { + return false; + } + } + return true; +} + +//================================================================================================= + +int BRepGraph_Validate::Result::NbIssues(const Severity theSev) const +{ + int aCount = 0; + for (const Issue& anIssue : Issues) + { + if (anIssue.Sev == theSev) + { + ++aCount; + } + } + return aCount; +} + +//================================================================================================= + +//! Validate owned representation-use parent, removal, representation, and uniqueness invariants. +//! @param[in] theGraph source graph +//! @param[in,out] theIssues collection to append diagnostic issues +void BRepGraph_Validate::CheckOwnedUseReferences( + const BRepGraph& theGraph, + NCollection_LinearVector& theIssues) +{ + using Issue = BRepGraph_Validate::Issue; + using Severity = BRepGraph_Validate::Severity; + + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + + NCollection_LinearVector aSurfaceOwners(aStorage.NbFaceSurfaces(), 0); + NCollection_LinearVector aSurfaceOwner(aStorage.NbFaceSurfaces(), + BRepGraph_NodeId()); + NCollection_LinearVector aTriangulationOwners(aStorage.NbFaceTriangulations(), 0); + NCollection_LinearVector aTriangulationOwner(aStorage.NbFaceTriangulations(), + BRepGraph_NodeId()); + NCollection_LinearVector aCurve3DOwners(aStorage.NbEdgeCurves3D(), 0); + NCollection_LinearVector aCurve3DOwner(aStorage.NbEdgeCurves3D(), + BRepGraph_NodeId()); + NCollection_LinearVector aPolygon3DOwners(aStorage.NbEdgePolygons3D(), 0); + NCollection_LinearVector aPolygon3DOwner(aStorage.NbEdgePolygons3D(), + BRepGraph_NodeId()); + NCollection_LinearVector aCurve2DOwners(aStorage.NbCoEdgeCurves2D(), 0); + NCollection_LinearVector aCurve2DOwner(aStorage.NbCoEdgeCurves2D(), + BRepGraph_NodeId()); + NCollection_LinearVector aPolygon2DOwners(aStorage.NbCoEdgePolygons2D(), 0); + NCollection_LinearVector aPolygon2DOwner(aStorage.NbCoEdgePolygons2D(), + BRepGraph_NodeId()); + NCollection_LinearVector aPolygonOnTriOwners(aStorage.NbCoEdgePolygonsOnTri(), 0); + NCollection_LinearVector aPolygonOnTriOwner(aStorage.NbCoEdgePolygonsOnTri(), + BRepGraph_NodeId()); + + const auto aRememberOwner = [](NCollection_LinearVector& theCounts, + NCollection_LinearVector& theOwners, + const uint32_t theIndex, + const BRepGraph_NodeId theOwner) { + ++theCounts[theIndex]; + if (!theOwners[theIndex].IsValid()) + { + theOwners[theIndex] = theOwner; + } + }; - // Orphan-rep detection: an active def that forward-references a rep which - // has been soft-removed leaves the rep id pointing at dead geometry. The - // rep should have been cleared (or the def removed) as part of the same - // mutation; surface the asymmetry. - const BRepGraph::TopoView::GeometryOps& aGeom = theGraph.Topo().Geometry(); - const BRepGraph::MeshView::PolyOps& aPoly = theGraph.Mesh().Poly(); for (BRepGraph_Iterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraphInc::EdgeDef& anEdge = anEdgeIt.Current(); - if (anEdge.Curve3DRepId.IsValid() && anEdge.Curve3DRepId.IsValid(aGeom.NbCurves3D()) - && aGeom.Curve3DRep(anEdge.Curve3DRepId).IsRemoved) + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + const BRepGraphInc::EdgeDef& anEdge = anEdgeIt.Current(); + if (anEdge.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D())) { - theIssues.Append(Issue{Severity::Error, - anEdgeIt.CurrentId(), - "EdgeDef.Curve3DRepId points to a removed Curve3DRep"}); + if (!aStorage.IsRemoved(anEdge.Curve3DRepId)) + { + const BRepGraphInc::EdgeCurve3DRep& aUse = aStorage.EdgeCurve3DRep(anEdge.Curve3DRepId); + aRememberOwner(aCurve3DOwners, aCurve3DOwner, anEdge.Curve3DRepId.Index, anEdgeId); + if (aUse.ParentEdgeId != anEdgeId) + { + theIssues.Append( + Issue{Severity::Error, anEdgeId, "EdgeCurve3DRep.ParentEdgeId mismatch"}); + } + if (aUse.Curve.IsNull()) + { + theIssues.Append(Issue{Severity::Error, anEdgeId, "EdgeCurve3DRep.Curve is null"}); + } + } } - if (anEdge.Polygon3DRepId.IsValid() && anEdge.Polygon3DRepId.IsValid(aPoly.NbPolygons3D()) - && aPoly.Polygon3DRep(anEdge.Polygon3DRepId).IsRemoved) + if (anEdge.Polygon3DRepId.IsValid(aStorage.NbEdgePolygons3D())) { - theIssues.Append(Issue{Severity::Error, - anEdgeIt.CurrentId(), - "EdgeDef.Polygon3DRepId points to a removed Polygon3DRep"}); - } - } - for (BRepGraph_Iterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) - { - const BRepGraphInc::FaceDef& aFace = aFaceIt.Current(); - if (aFace.SurfaceRepId.IsValid() && aFace.SurfaceRepId.IsValid(aGeom.NbSurfaces()) - && aGeom.SurfaceRep(aFace.SurfaceRepId).IsRemoved) - { - theIssues.Append(Issue{Severity::Error, - aFaceIt.CurrentId(), - "FaceDef.SurfaceRepId points to a removed SurfaceRep"}); - } - if (aFace.TriangulationRepId.IsValid() - && aFace.TriangulationRepId.IsValid(aPoly.NbTriangulations()) - && aPoly.TriangulationRep(aFace.TriangulationRepId).IsRemoved) - { - theIssues.Append(Issue{Severity::Error, - aFaceIt.CurrentId(), - "FaceDef.TriangulationRepId points to a removed TriangulationRep"}); + if (!aStorage.IsRemoved(anEdge.Polygon3DRepId)) + { + const BRepGraphInc::EdgePolygon3DRep& aUse = + aStorage.EdgePolygon3DRep(anEdge.Polygon3DRepId); + aRememberOwner(aPolygon3DOwners, aPolygon3DOwner, anEdge.Polygon3DRepId.Index, anEdgeId); + if (aUse.ParentEdgeId != anEdgeId) + { + theIssues.Append( + Issue{Severity::Error, anEdgeId, "EdgePolygon3DRep.ParentEdgeId mismatch"}); + } + if (aUse.Polygon.IsNull()) + { + theIssues.Append(Issue{Severity::Error, anEdgeId, "EdgePolygon3DRep.Polygon is null"}); + } + } } } + for (BRepGraph_Iterator aCoEdgeIt(theGraph); aCoEdgeIt.More(); aCoEdgeIt.Next()) { - const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Current(); - if (aCoEdge.Curve2DRepId.IsValid() && aCoEdge.Curve2DRepId.IsValid(aGeom.NbCurves2D()) - && aGeom.Curve2DRep(aCoEdge.Curve2DRepId).IsRemoved) + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Current(); + if (aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D())) { - theIssues.Append(Issue{Severity::Error, - aCoEdgeIt.CurrentId(), - "CoEdgeDef.Curve2DRepId points to a removed Curve2DRep"}); + if (!aStorage.IsRemoved(aCoEdge.Curve2DRepId)) + { + const BRepGraphInc::CoEdgeCurve2DRep& aUse = + aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + aRememberOwner(aCurve2DOwners, aCurve2DOwner, aCoEdge.Curve2DRepId.Index, aCoEdgeId); + if (aUse.ParentCoEdgeId != aCoEdgeId) + { + theIssues.Append( + Issue{Severity::Error, aCoEdgeId, "CoEdgeCurve2DRep.ParentCoEdgeId mismatch"}); + } + if (aUse.Curve.IsNull()) + { + theIssues.Append(Issue{Severity::Error, aCoEdgeId, "CoEdgeCurve2DRep.Curve is null"}); + } + } + } + if (aCoEdge.Polygon2DRepId.IsValid(aStorage.NbCoEdgePolygons2D())) + { + if (!aStorage.IsRemoved(aCoEdge.Polygon2DRepId)) + { + const BRepGraphInc::CoEdgePolygon2DRep& aUse = + aStorage.CoEdgePolygon2DRep(aCoEdge.Polygon2DRepId); + aRememberOwner(aPolygon2DOwners, aPolygon2DOwner, aCoEdge.Polygon2DRepId.Index, aCoEdgeId); + if (aUse.ParentCoEdgeId != aCoEdgeId) + { + theIssues.Append( + Issue{Severity::Error, aCoEdgeId, "CoEdgePolygon2DRep.ParentCoEdgeId mismatch"}); + } + if (aUse.Polygon.IsNull()) + { + theIssues.Append(Issue{Severity::Error, aCoEdgeId, "CoEdgePolygon2DRep.Polygon is null"}); + } + } + } + if (aCoEdge.PolygonOnTriRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri())) + { + if (!aStorage.IsRemoved(aCoEdge.PolygonOnTriRepId)) + { + const BRepGraphInc::CoEdgePolygonOnTriRep& aUse = + aStorage.CoEdgePolygonOnTriRep(aCoEdge.PolygonOnTriRepId); + aRememberOwner(aPolygonOnTriOwners, + aPolygonOnTriOwner, + aCoEdge.PolygonOnTriRepId.Index, + aCoEdgeId); + if (aUse.ParentCoEdgeId != aCoEdgeId) + { + theIssues.Append( + Issue{Severity::Error, aCoEdgeId, "CoEdgePolygonOnTriRep.ParentCoEdgeId mismatch"}); + } + if (aUse.Polygon.IsNull()) + { + theIssues.Append( + Issue{Severity::Error, aCoEdgeId, "CoEdgePolygonOnTriRep.Polygon is null"}); + } + } } } + + for (BRepGraph_Iterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + const BRepGraphInc::FaceDef& aFace = aFaceIt.Current(); + if (aFace.SurfaceRepId.IsValid(aStorage.NbFaceSurfaces())) + { + if (!aStorage.IsRemoved(aFace.SurfaceRepId)) + { + const BRepGraphInc::FaceSurfaceRep& aUse = aStorage.FaceSurfaceRep(aFace.SurfaceRepId); + aRememberOwner(aSurfaceOwners, aSurfaceOwner, aFace.SurfaceRepId.Index, aFaceId); + if (aUse.ParentFaceId != aFaceId) + { + theIssues.Append(Issue{Severity::Error, aFaceId, "FaceSurfaceRep.ParentFaceId mismatch"}); + } + if (aUse.Surface.IsNull()) + { + theIssues.Append(Issue{Severity::Error, aFaceId, "FaceSurfaceRep.Surface is null"}); + } + } + } + if (aFace.TriangulationRepId.IsValid(aStorage.NbFaceTriangulations())) + { + if (!aStorage.IsRemoved(aFace.TriangulationRepId)) + { + const BRepGraphInc::FaceTriangulationRep& aUse = + aStorage.FaceTriangulationRep(aFace.TriangulationRepId); + aRememberOwner(aTriangulationOwners, + aTriangulationOwner, + aFace.TriangulationRepId.Index, + aFaceId); + if (aUse.ParentFaceId != aFaceId) + { + theIssues.Append( + Issue{Severity::Error, aFaceId, "FaceTriangulationRep.ParentFaceId mismatch"}); + } + if (aUse.Triangulation.IsNull()) + { + theIssues.Append( + Issue{Severity::Error, aFaceId, "FaceTriangulationRep.Triangulation is null"}); + } + } + } + } + + const auto aCheckStore = [&](const char* theName, + const uint32_t theCount, + const auto theMakeId, + const NCollection_LinearVector& theOwnerCounts, + const NCollection_LinearVector& theOwners) { + for (uint32_t anIdx = 0; anIdx < theCount; ++anIdx) + { + const auto anId = theMakeId(anIdx); + if (aStorage.IsRemoved(anId)) + { + continue; + } + if (theOwnerCounts[anIdx] == 0) + { + TCollection_AsciiString aDesc("Active "); + aDesc += theName; + aDesc += " has no owner"; + theIssues.Append(Issue{Severity::Error, BRepGraph_NodeId(), aDesc}); + } + else if (theOwnerCounts[anIdx] > 1) + { + TCollection_AsciiString aDesc("Active "); + aDesc += theName; + aDesc += " has multiple owners"; + theIssues.Append(Issue{Severity::Error, theOwners[anIdx], aDesc}); + } + } + }; + + aCheckStore( + "FaceSurfaceRep", + aStorage.NbFaceSurfaces(), + [](uint32_t theIdx) { return BRepGraph_FaceSurfaceRepId(theIdx); }, + aSurfaceOwners, + aSurfaceOwner); + aCheckStore( + "FaceTriangulationRep", + aStorage.NbFaceTriangulations(), + [](uint32_t theIdx) { return BRepGraph_FaceTriangulationRepId(theIdx); }, + aTriangulationOwners, + aTriangulationOwner); + aCheckStore( + "EdgeCurve3DRep", + aStorage.NbEdgeCurves3D(), + [](uint32_t theIdx) { return BRepGraph_EdgeCurve3DRepId(theIdx); }, + aCurve3DOwners, + aCurve3DOwner); + aCheckStore( + "EdgePolygon3DRep", + aStorage.NbEdgePolygons3D(), + [](uint32_t theIdx) { return BRepGraph_EdgePolygon3DRepId(theIdx); }, + aPolygon3DOwners, + aPolygon3DOwner); + aCheckStore( + "CoEdgeCurve2DRep", + aStorage.NbCoEdgeCurves2D(), + [](uint32_t theIdx) { return BRepGraph_CoEdgeCurve2DRepId(theIdx); }, + aCurve2DOwners, + aCurve2DOwner); + aCheckStore( + "CoEdgePolygon2DRep", + aStorage.NbCoEdgePolygons2D(), + [](uint32_t theIdx) { return BRepGraph_CoEdgePolygon2DRepId(theIdx); }, + aPolygon2DOwners, + aPolygon2DOwner); + aCheckStore( + "CoEdgePolygonOnTriRep", + aStorage.NbCoEdgePolygonsOnTri(), + [](uint32_t theIdx) { return BRepGraph_CoEdgePolygonOnTriRepId(theIdx); }, + aPolygonOnTriOwners, + aPolygonOnTriOwner); } +namespace +{ + //! Verify that removed entities are not referenced by any active (non-removed) //! parent entity through forward incidence refs. //! @param[in] theGraph source graph //! @param[in,out] theIssues collection to append diagnostic issues void checkRemovedNodeIsolation(const BRepGraph& theGraph, - NCollection_DynamicArray& theIssues) + NCollection_LinearVector& theIssues) { using Issue = BRepGraph_Validate::Issue; using Severity = BRepGraph_Validate::Severity; @@ -686,7 +1028,7 @@ void checkRemovedNodeIsolation(const BRepGraph& if (anEdge.StartVertexRefId.IsValid()) { const BRepGraph_NodeId aStartVtxId = - theGraph.Refs().Vertices().Entry(anEdge.StartVertexRefId).VertexDefId; + theGraph.Refs().Vertices().Entry(anEdge.StartVertexRefId).ChildVertexId; if (aStartVtxId.IsValid() && isEntityRemoved(theGraph, aStartVtxId)) { theIssues.Append(Issue{Severity::Error, @@ -697,7 +1039,7 @@ void checkRemovedNodeIsolation(const BRepGraph& if (anEdge.EndVertexRefId.IsValid()) { const BRepGraph_NodeId anEndVtxId = - theGraph.Refs().Vertices().Entry(anEdge.EndVertexRefId).VertexDefId; + theGraph.Refs().Vertices().Entry(anEdge.EndVertexRefId).ChildVertexId; if (anEndVtxId.IsValid() && isEntityRemoved(theGraph, anEndVtxId)) { theIssues.Append(Issue{Severity::Error, @@ -713,16 +1055,15 @@ void checkRemovedNodeIsolation(const BRepGraph& const BRepGraph_WireId aWireId = aWireIt.CurrentId(); bool hasRemovedEdge = false; - for (BRepGraph_RefsCoEdgeOfWire anIt(theGraph, aWireId); anIt.More(); anIt.Next()) + for (BRepGraph_CoEdgesOfWire anIt(theGraph, aWireId); anIt.More(); anIt.Next()) { - const BRepGraphInc::CoEdgeRef& aCR = theGraph.Refs().CoEdges().Entry(anIt.CurrentId()); if (hasRemovedEdge) { break; } const BRepGraphInc::CoEdgeDef& aCoEdge = - theGraph.Topo().CoEdges().Definition(aCR.CoEdgeDefId); - const BRepGraph_NodeId anEdgeId = aCoEdge.EdgeDefId; + theGraph.Topo().CoEdges().Definition(anIt.CurrentId()); + const BRepGraph_NodeId anEdgeId = aCoEdge.ChildEdgeId; if (anEdgeId.IsValid() && isEntityRemoved(theGraph, anEdgeId)) { hasRemovedEdge = true; @@ -740,71 +1081,59 @@ void checkRemovedNodeIsolation(const BRepGraph& aCoEdgeIt.Next()) { const BRepGraphInc::CoEdgeDef& aCoEdge = aCoEdgeIt.Current(); - const BRepGraph_NodeId aFaceId = aCoEdge.FaceDefId; + const BRepGraph_NodeId aFaceId = aCoEdge.FaceId; if (aFaceId.IsValid() && isEntityRemoved(theGraph, aFaceId)) { theIssues.Append(Issue{Severity::Error, aCoEdgeIt.CurrentId(), - "Non-removed CoEdgeDef.FaceDefId references removed FaceDef"}); + "Non-removed CoEdgeDef.FaceId references removed FaceDef"}); } } } -//! Check wire edge connectivity: each coedge's end vertex must match the -//! next coedge's start vertex. Uses BRepGraph_WireExplorer for -//! order-independent traversal. +//! Check wire edge connectivity: each stored coedge's end vertex must match +//! the next stored coedge's start vertex. //! @param[in] theGraph source graph //! @param[in,out] theIssues collection to append diagnostic issues void checkWireConnectivity(const BRepGraph& theGraph, - NCollection_DynamicArray& theIssues) + NCollection_LinearVector& theIssues) { using Issue = BRepGraph_Validate::Issue; using Severity = BRepGraph_Validate::Severity; for (BRepGraph_Iterator aWireIt(theGraph); aWireIt.More(); aWireIt.Next()) { - const BRepGraph_WireId aWireId = aWireIt.CurrentId(); - - BRepGraph_WireExplorer anExp(theGraph, aWireId); - if (anExp.NbEdges() < 2) + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); + const BRepGraphInc::WireRelations& aWireRel = theGraph.Topo().Wires().Relations(aWireId); + if (aWireRel.CoEdgeIds.Size() < 2) { continue; } - // Validate all edge indices first. - bool aAllValid = true; - for (; anExp.More(); anExp.Next()) + BRepGraph_CoEdgeId aPrevId = aWireRel.CoEdgeIds.Value(0); + for (size_t anIdx = 1; anIdx < aWireRel.CoEdgeIds.Size(); ++anIdx) { - const BRepGraphInc::CoEdgeDef& aCoEdge = - theGraph.Topo().CoEdges().Definition(anExp.CurrentCoEdgeId()); - const BRepGraph_NodeId anEdgeId = aCoEdge.EdgeDefId; - if (!anEdgeId.IsValid() || !isValidNodeId(theGraph, anEdgeId)) + const BRepGraph_CoEdgeId aCurrId = aWireRel.CoEdgeIds.Value(anIdx); + if (!aPrevId.IsValid(theGraph.Topo().CoEdges().Nb()) + || !aCurrId.IsValid(theGraph.Topo().CoEdges().Nb())) { - aAllValid = false; - break; + aPrevId = aCurrId; + continue; } - } - if (!aAllValid) - { - continue; - } - - // Check that all consecutive pairs in the ordered sequence are connected. - anExp.Reset(); - BRepGraph_CoEdgeId aPrevId = anExp.CurrentCoEdgeId(); - anExp.Next(); - int anIdx = 0; - for (; anExp.More(); anExp.Next(), ++anIdx) - { - const BRepGraph_CoEdgeId aCurrId = anExp.CurrentCoEdgeId(); const BRepGraphInc::CoEdgeDef& aPrevCoEdge = theGraph.Topo().CoEdges().Definition(aPrevId); const BRepGraphInc::CoEdgeDef& aCurrCoEdge = theGraph.Topo().CoEdges().Definition(aCurrId); + if (!aPrevCoEdge.ChildEdgeId.IsValid(theGraph.Topo().Edges().Nb()) + || !aCurrCoEdge.ChildEdgeId.IsValid(theGraph.Topo().Edges().Nb())) + { + aPrevId = aCurrId; + continue; + } const BRepGraphInc::EdgeDef& aPrevEdge = - theGraph.Topo().Edges().Definition(aPrevCoEdge.EdgeDefId); + theGraph.Topo().Edges().Definition(aPrevCoEdge.ChildEdgeId); const BRepGraphInc::EdgeDef& aCurrEdge = - theGraph.Topo().Edges().Definition(aCurrCoEdge.EdgeDefId); + theGraph.Topo().Edges().Definition(aCurrCoEdge.ChildEdgeId); // Resolve oriented end vertex of previous edge. const BRepGraph_VertexRefId aPrevEndRefId = (aPrevCoEdge.Orientation == TopAbs_FORWARD) @@ -812,7 +1141,7 @@ void checkWireConnectivity(const BRepGraph& : aPrevEdge.StartVertexRefId; const BRepGraph_NodeId aPrevEnd = aPrevEndRefId.IsValid() - ? BRepGraph_VertexId(theGraph.Refs().Vertices().Entry(aPrevEndRefId).VertexDefId) + ? BRepGraph_VertexId(theGraph.Refs().Vertices().Entry(aPrevEndRefId).ChildVertexId) : BRepGraph_NodeId(); // Resolve oriented start vertex of current edge. @@ -821,17 +1150,17 @@ void checkWireConnectivity(const BRepGraph& : aCurrEdge.EndVertexRefId; const BRepGraph_NodeId aCurrStart = aCurrStartRefId.IsValid() - ? BRepGraph_VertexId(theGraph.Refs().Vertices().Entry(aCurrStartRefId).VertexDefId) + ? BRepGraph_VertexId(theGraph.Refs().Vertices().Entry(aCurrStartRefId).ChildVertexId) : BRepGraph_NodeId(); if (aPrevEnd.IsValid() && aCurrStart.IsValid() && aPrevEnd != aCurrStart) { TCollection_AsciiString aDesc("Wire edges not connected: edge["); - aDesc += TCollection_AsciiString(anIdx); + aDesc += TCollection_AsciiString(static_cast(anIdx - 1)); aDesc += "] end != edge["; - aDesc += TCollection_AsciiString(anIdx + 1); + aDesc += TCollection_AsciiString(static_cast(anIdx)); aDesc += "] start"; - theIssues.Append(Issue{Severity::Error, aWireId, aDesc}); + theIssues.Append(Issue{Severity::Warning, aWireId, aDesc}); } aPrevId = aCurrId; } @@ -843,7 +1172,7 @@ void checkWireConnectivity(const BRepGraph& //! @param[in] theGraph source graph //! @param[in,out] theIssues collection to append diagnostic issues void checkActiveCounts(const BRepGraph& theGraph, - NCollection_DynamicArray& theIssues) + NCollection_LinearVector& theIssues) { using Severity = BRepGraph_Validate::Severity; using Issue = BRepGraph_Validate::Issue; @@ -908,6 +1237,101 @@ void checkActiveCounts(const BRepGraph& theG verify("Occurrences", aDefs.Occurrences().NbActive(), countActive(BRepGraph_NodeId::Kind::Occurrence, aDefs.Occurrences().Nb())); + + const auto countActiveUses = [&](const uint32_t theNb, const auto theMakeId) -> int { + int aCount = 0; + for (uint32_t anIdx = 0; anIdx < theNb; ++anIdx) + { + if (!theMakeId(anIdx).IsRemoved(theGraph)) + { + ++aCount; + } + } + return aCount; + }; + + verify("FaceSurfaces", + aDefs.Geometry().NbActiveFaceSurfaces(), + countActiveUses(aDefs.Geometry().NbFaceSurfaces(), + [](uint32_t theIdx) { return BRepGraph_FaceSurfaceRepId(theIdx); })); + verify("FaceTriangulations", + theGraph.Mesh().Poly().NbActiveTriangulations(), + countActiveUses(theGraph.Mesh().Poly().NbFaceTriangulations(), + [](uint32_t theIdx) { return BRepGraph_FaceTriangulationRepId(theIdx); })); + verify("EdgeCurves3D", + aDefs.Geometry().NbActiveEdgeCurves3D(), + countActiveUses(aDefs.Geometry().NbEdgeCurves3D(), + [](uint32_t theIdx) { return BRepGraph_EdgeCurve3DRepId(theIdx); })); + verify("EdgePolygons3D", + theGraph.Mesh().Poly().NbActivePolygons3D(), + countActiveUses(theGraph.Mesh().Poly().NbEdgePolygons3D(), + [](uint32_t theIdx) { return BRepGraph_EdgePolygon3DRepId(theIdx); })); + verify("CoEdgeCurves2D", + aDefs.Geometry().NbActiveCoEdgeCurves2D(), + countActiveUses(aDefs.Geometry().NbCoEdgeCurves2D(), + [](uint32_t theIdx) { return BRepGraph_CoEdgeCurve2DRepId(theIdx); })); + verify("CoEdgePolygons2D", + theGraph.Mesh().Poly().NbActivePolygons2D(), + countActiveUses(theGraph.Mesh().Poly().NbCoEdgePolygons2D(), + [](uint32_t theIdx) { return BRepGraph_CoEdgePolygon2DRepId(theIdx); })); + verify("CoEdgePolygonsOnTri", + theGraph.Mesh().Poly().NbActivePolygonsOnTri(), + countActiveUses(theGraph.Mesh().Poly().NbCoEdgePolygonsOnTri(), [](uint32_t theIdx) { + return BRepGraph_CoEdgePolygonOnTriRepId(theIdx); + })); +} + +//! Validate document roots. A root Product is an entry point into the assembly +//! forest; it must be active, unique, and not referenced by any live Occurrence. +//! @param[in] theGraph source graph +//! @param[in,out] theIssues collection to append diagnostic issues +void checkDocumentRootProducts(const BRepGraph& theGraph, + NCollection_LinearVector& theIssues) +{ + using Issue = BRepGraph_Validate::Issue; + using Severity = BRepGraph_Validate::Severity; + + const BRepGraph::TopoView& aDefs = theGraph.Topo(); + NCollection_FlatMap aSeenRoots; + for (const BRepGraph_ProductId& aRootProductId : theGraph.RootProductIds()) + { + if (!aRootProductId.IsValidIn(aDefs.Products())) + { + theIssues.Append( + Issue{Severity::Error, aRootProductId, "Document root Product is out of bounds"}); + continue; + } + + if (aRootProductId.IsRemoved(theGraph)) + { + theIssues.Append(Issue{Severity::Error, aRootProductId, "Document root Product is removed"}); + continue; + } + + if (!aSeenRoots.Add(aRootProductId)) + { + theIssues.Append( + Issue{Severity::Error, aRootProductId, "Document root Product is listed more than once"}); + } + + for (const BRepGraph_OccurrenceRefId& anOccRefId : + theGraph.Refs().Occurrences().IdsReferencing(BRepGraph_NodeId(aRootProductId))) + { + if (!anOccRefId.IsValid(theGraph.Refs().Occurrences().Nb()) || anOccRefId.IsRemoved(theGraph)) + { + continue; + } + const BRepGraph_OccurrenceId anOccId = + theGraph.Refs().Occurrences().Entry(anOccRefId).ChildOccurrenceId; + if (anOccId.IsValidIn(aDefs.Occurrences()) && !anOccId.IsRemoved(theGraph)) + { + theIssues.Append(Issue{Severity::Error, + aRootProductId, + "Document root Product is referenced by an active Occurrence"}); + break; + } + } + } } } // namespace @@ -935,36 +1359,57 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph const Options& theOptions) { Result aResult; - if (!theGraph.IsDone()) + if (theGraph.IsEmpty()) { return aResult; } if (theOptions.ValidationMode == Mode::Lightweight) { - NCollection_DynamicArray aBoundaryIssues; + NCollection_LinearVector aBoundaryIssues; if (!theGraph.Editor().ValidateMutationBoundary(&aBoundaryIssues)) { appendMutationBoundaryIssues(aBoundaryIssues, aResult.Issues); } + checkRemovedNodeIsolation(theGraph, aResult.Issues); + checkDocumentRootProducts(theGraph, aResult.Issues); return aResult; } - NCollection_DynamicArray aBoundaryIssues; + NCollection_LinearVector aBoundaryIssues; if (!theGraph.Editor().ValidateMutationBoundary(&aBoundaryIssues)) { appendMutationBoundaryIssues(aBoundaryIssues, aResult.Issues); } checkCrossReferenceBounds(theGraph, aResult.Issues); - checkReverseIndexConsistency(theGraph, aResult.Issues); - checkReverseIndexFaceCountCache(theGraph, aResult.Issues); + checkRelationConsistency(theGraph, aResult.Issues); + checkRelationFaceCountCache(theGraph, aResult.Issues); checkIncidenceRefConsistency(theGraph, aResult.Issues); checkGeometryReferences(theGraph, aResult.Issues); + CheckOwnedUseReferences(theGraph, aResult.Issues); checkRemovedNodeIsolation(theGraph, aResult.Issues); checkWireConnectivity(theGraph, aResult.Issues); checkActiveCounts(theGraph, aResult.Issues); + checkDocumentRootProducts(theGraph, aResult.Issues); + + if (const occ::handle aSupplementLayer = + theGraph.LayerRegistry().FindLayer(); + !aSupplementLayer.IsNull()) + { + try + { + aSupplementLayer->Validate(); + } + catch (const Standard_Failure& theFailure) + { + aResult.Issues.Append( + Issue{Severity::Error, + BRepGraph_NodeId(), + TCollection_AsciiString("LayerTopoSupplement invalid: ") + theFailure.what()}); + } + } // UID integrity checks: all active nodes must have a valid UID that round-trips. const BRepGraph::TopoView& aDefs = theGraph.Topo(); @@ -974,7 +1419,7 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph { const BRepGraph_NodeId aNode(theKind, anIdx); const BRepGraphInc::BaseDef* aDef = aDefs.Gen().TopoEntity(aNode); - if (aDef == nullptr || aDef->IsRemoved) + if (aDef == nullptr || aNode.IsRemoved(theGraph)) { continue; } @@ -1026,8 +1471,8 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph // BFS from this product's children; skip already-visited to avoid // exponential blowup on DAGs. A cycle exists if we re-encounter aProdIdx. - NCollection_Map aVisited; - NCollection_DynamicArray aQueue; + NCollection_FlatMap aVisited; + NCollection_LinearVector aQueue; size_t aHead = 0; // Seed with direct children. @@ -1037,12 +1482,13 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph const BRepGraphInc::OccurrenceRef& anOccRef = theGraph.Refs().Occurrences().Entry(anOccIt.CurrentId()); const BRepGraphInc::OccurrenceDef& anOcc = - aDefs.Occurrences().Definition(anOccRef.OccurrenceDefId); - if (anOcc.IsRemoved || anOcc.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Product) + aDefs.Occurrences().Definition(anOccRef.ChildOccurrenceId); + if (anOccIt.CurrentId().IsRemoved(theGraph) + || anOcc.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Product) { continue; } - const BRepGraph_ProductId aChildProdId = BRepGraph_ProductId::FromNodeId(anOcc.ChildDefId); + const BRepGraph_ProductId aChildProdId = BRepGraph_ProductId::FromNodeId(anOcc.ChildNodeId); if (!aChildProdId.IsValid(aDefs.Products().Nb())) { continue; @@ -1066,8 +1512,7 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph { const BRepGraph_ProductId aChildProdId = aQueue.Value(aHead); ++aHead; - const BRepGraphInc::ProductDef& aChildProd = aDefs.Products().Definition(aChildProdId); - if (aChildProd.IsRemoved) + if (aChildProdId.IsRemoved(theGraph)) { continue; } @@ -1077,12 +1522,13 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph const BRepGraphInc::OccurrenceRef& aRef = theGraph.Refs().Occurrences().Entry(aRefIt.CurrentId()); const BRepGraphInc::OccurrenceDef& aOcc = - aDefs.Occurrences().Definition(aRef.OccurrenceDefId); - if (aOcc.IsRemoved || aOcc.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Product) + aDefs.Occurrences().Definition(aRef.ChildOccurrenceId); + if (aRefIt.CurrentId().IsRemoved(theGraph) + || aOcc.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Product) { continue; } - const BRepGraph_ProductId aDescProdId = BRepGraph_ProductId::FromNodeId(aOcc.ChildDefId); + const BRepGraph_ProductId aDescProdId = BRepGraph_ProductId::FromNodeId(aOcc.ChildNodeId); if (!aDescProdId.IsValid(aDefs.Products().Nb())) { continue; @@ -1105,26 +1551,27 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph } // Compound structural cycle detection: a compound must not reach itself - // directly or transitively through ChildRefs whose ChildDefId.NodeKind is + // directly or transitively through ChildRefs whose ChildNodeId.NodeKind is // Compound. Distinct from Product/Occurrence cycles (those are assembly-level). for (BRepGraph_Iterator aCompIt(theGraph); aCompIt.More(); aCompIt.Next()) { const BRepGraph_CompoundId aRootCompoundId = aCompIt.CurrentId(); - NCollection_Map aVisited; - NCollection_DynamicArray aQueue; + NCollection_FlatMap aVisited; + NCollection_LinearVector aQueue; size_t aHead = 0; for (BRepGraph_RefsChildOfCompound anIt(theGraph, aRootCompoundId); anIt.More(); anIt.Next()) { const BRepGraphInc::ChildRef& aCR = theGraph.Refs().Children().Entry(anIt.CurrentId()); - if (aCR.IsRemoved || aCR.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Compound - || !aCR.ChildDefId.IsValid()) + if (theGraph.Refs().Gen().IsRemoved(anIt.CurrentId()) + || aCR.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Compound + || !aCR.ChildNodeId.IsValid()) { continue; } const BRepGraph_CompoundId aChildCompoundId = - BRepGraph_CompoundId::FromNodeId(aCR.ChildDefId); + BRepGraph_CompoundId::FromNodeId(aCR.ChildNodeId); if (!aChildCompoundId.IsValidIn(aDefs.Compounds())) { continue; @@ -1148,7 +1595,7 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph { const BRepGraph_CompoundId aChildCompoundId = aQueue.Value(aHead); ++aHead; - if (aDefs.Compounds().Definition(aChildCompoundId).IsRemoved) + if (aChildCompoundId.IsRemoved(theGraph)) { continue; } @@ -1156,13 +1603,14 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph aRefIt.Next()) { const BRepGraphInc::ChildRef& aCR = theGraph.Refs().Children().Entry(aRefIt.CurrentId()); - if (aCR.IsRemoved || aCR.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Compound - || !aCR.ChildDefId.IsValid()) + if (theGraph.Refs().Gen().IsRemoved(aRefIt.CurrentId()) + || aCR.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Compound + || !aCR.ChildNodeId.IsValid()) { continue; } const BRepGraph_CompoundId aDescCompoundId = - BRepGraph_CompoundId::FromNodeId(aCR.ChildDefId); + BRepGraph_CompoundId::FromNodeId(aCR.ChildNodeId); if (!aDescCompoundId.IsValidIn(aDefs.Compounds())) { continue; @@ -1184,30 +1632,58 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph } } - // Orphan ref detection: ChildRef/SolidRef entries whose ParentId points - // to a removed / out-of-range container. These survive node removal and - // can cause reverse-index queries to return wrong answers. + // Ref ownership: every active ref is a parent-owned usage record and must be + // reachable from exactly one live parent slot. Sharing a ref id across two + // parents, or using it twice in one parent, makes branch context ambiguous. { - const BRepGraph::RefsView& aRefs = theGraph.Refs(); + const BRepGraph::RefsView& aRefs = theGraph.Refs(); + auto appendOwnerIssue = [&](const BRepGraph_NodeId theNode, + const int theOwnerCount, + const TCollection_AsciiString& theKind) { + if (theOwnerCount == 0) + { + TCollection_AsciiString aDesc("Orphan "); + aDesc += theKind; + aDesc += ": no live parent owns it"; + aResult.Issues.Append(Issue{Severity::Error, theNode, aDesc}); + } + else if (theOwnerCount > 1) + { + TCollection_AsciiString aDesc("Shared "); + aDesc += theKind; + aDesc += ": more than one live parent slot owns it"; + aResult.Issues.Append(Issue{Severity::Error, theNode, aDesc}); + } + }; + for (BRepGraph_FullChildRefIterator aChildRefIt(theGraph); aChildRefIt.More(); aChildRefIt.Next()) { const BRepGraph_ChildRefId aChildRefId = aChildRefIt.CurrentId(); const BRepGraphInc::ChildRef& aCR = aRefs.Children().Entry(aChildRefId); - if (aCR.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aChildRefId)) { continue; } - const BRepGraph_NodeId aParentId = aCR.ParentId; - if (!aParentId.IsValid() || aParentId.NodeKind != BRepGraph_NodeId::Kind::Compound - || !BRepGraph_CompoundId(aParentId).IsValidIn(aDefs.Compounds()) - || aDefs.Compounds().Definition(BRepGraph_CompoundId(aParentId)).IsRemoved) + uint32_t anOwnerCount = 0; + for (BRepGraph_CompoundId aCompoundId = BRepGraph_CompoundId::Start(); + aCompoundId < BRepGraph_CompoundId(aDefs.Compounds().Nb()); + ++aCompoundId) { - aResult.Issues.Append( - Issue{Severity::Error, - aParentId, - "Orphan ChildRef: ParentId points to removed or out-of-range Compound"}); + if (aCompoundId.IsRemoved(theGraph)) + { + continue; + } + for (const BRepGraph_ChildRefId& aRefId : + aDefs.Compounds().Relations(aCompoundId).ChildRefIds) + { + if (aRefId == aChildRefId) + { + ++anOwnerCount; + } + } } + appendOwnerIssue(aCR.ChildNodeId, anOwnerCount, "ChildRef"); } for (BRepGraph_FullSolidRefIterator aSolidRefIt(theGraph); aSolidRefIt.More(); @@ -1215,167 +1691,230 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph { const BRepGraph_SolidRefId aSolidRefId = aSolidRefIt.CurrentId(); const BRepGraphInc::SolidRef& aSR = aRefs.Solids().Entry(aSolidRefId); - if (aSR.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aSolidRefId)) { continue; } - if (!aSR.ParentId.IsValid()) + uint32_t anOwnerCount = 0; + for (BRepGraph_CompSolidId aCompSolidId = BRepGraph_CompSolidId::Start(); + aCompSolidId < BRepGraph_CompSolidId(aDefs.CompSolids().Nb()); + ++aCompSolidId) { - aResult.Issues.Append( - Issue{Severity::Error, aSR.ParentId, "Orphan SolidRef: ParentId invalid"}); - continue; - } - if (aSR.ParentId.NodeKind == BRepGraph_NodeId::Kind::CompSolid) - { - if (!BRepGraph_CompSolidId(aSR.ParentId).IsValidIn(aDefs.CompSolids()) - || aDefs.CompSolids().Definition(BRepGraph_CompSolidId(aSR.ParentId)).IsRemoved) + if (aCompSolidId.IsRemoved(theGraph)) { - aResult.Issues.Append( - Issue{Severity::Error, - aSR.ParentId, - "Orphan SolidRef: ParentId points to removed or out-of-range CompSolid"}); + continue; + } + for (const BRepGraph_SolidRefId& aRefId : + aDefs.CompSolids().Relations(aCompSolidId).SolidRefIds) + { + if (aRefId == aSolidRefId) + { + ++anOwnerCount; + } } } + appendOwnerIssue(aSR.ChildSolidId, anOwnerCount, "SolidRef"); } - // Shell refs: parent must be a live Solid. for (BRepGraph_FullShellRefIterator aShellRefIt(theGraph); aShellRefIt.More(); aShellRefIt.Next()) { const BRepGraph_ShellRefId aShellRefId = aShellRefIt.CurrentId(); const BRepGraphInc::ShellRef& aRef = aRefs.Shells().Entry(aShellRefId); - if (aRef.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aShellRefId)) { continue; } - const BRepGraph_NodeId aP = aRef.ParentId; - if (!aP.IsValid() || aP.NodeKind != BRepGraph_NodeId::Kind::Solid - || !BRepGraph_SolidId(aP).IsValidIn(aDefs.Solids()) - || aDefs.Solids().Definition(BRepGraph_SolidId(aP)).IsRemoved) + uint32_t anOwnerCount = 0; + for (BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + aSolidId < BRepGraph_SolidId(aDefs.Solids().Nb()); + ++aSolidId) { - aResult.Issues.Append( - Issue{Severity::Error, aP, "Orphan ShellRef: ParentId is not a live Solid"}); + if (aSolidId.IsRemoved(theGraph)) + { + continue; + } + for (const BRepGraph_ShellRefId& aRefId : aDefs.Solids().Relations(aSolidId).ShellRefIds) + { + if (aRefId == aShellRefId) + { + ++anOwnerCount; + } + } } + appendOwnerIssue(aRef.ChildShellId, anOwnerCount, "ShellRef"); } - // Face refs: parent must be a live Shell. for (BRepGraph_FullFaceRefIterator aFaceRefIt(theGraph); aFaceRefIt.More(); aFaceRefIt.Next()) { const BRepGraph_FaceRefId aFaceRefId = aFaceRefIt.CurrentId(); const BRepGraphInc::FaceRef& aRef = aRefs.Faces().Entry(aFaceRefId); - if (aRef.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aFaceRefId)) { continue; } - const BRepGraph_NodeId aP = aRef.ParentId; - if (!aP.IsValid() || aP.NodeKind != BRepGraph_NodeId::Kind::Shell - || !BRepGraph_ShellId(aP).IsValidIn(aDefs.Shells()) - || aDefs.Shells().Definition(BRepGraph_ShellId(aP)).IsRemoved) + uint32_t anOwnerCount = 0; + for (BRepGraph_ShellId aShellId = BRepGraph_ShellId::Start(); + aShellId < BRepGraph_ShellId(aDefs.Shells().Nb()); + ++aShellId) { - aResult.Issues.Append( - Issue{Severity::Error, aP, "Orphan FaceRef: ParentId is not a live Shell"}); + if (aShellId.IsRemoved(theGraph)) + { + continue; + } + for (const BRepGraph_FaceRefId& aRefId : aDefs.Shells().Relations(aShellId).FaceRefIds) + { + if (aRefId == aFaceRefId) + { + ++anOwnerCount; + } + } } + appendOwnerIssue(aRef.ChildFaceId, anOwnerCount, "FaceRef"); } - // Wire refs: parent must be a live Face. for (BRepGraph_FullWireRefIterator aWireRefIt(theGraph); aWireRefIt.More(); aWireRefIt.Next()) { const BRepGraph_WireRefId aWireRefId = aWireRefIt.CurrentId(); const BRepGraphInc::WireRef& aRef = aRefs.Wires().Entry(aWireRefId); - if (aRef.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aWireRefId)) { continue; } - const BRepGraph_NodeId aP = aRef.ParentId; - if (!aP.IsValid() || aP.NodeKind != BRepGraph_NodeId::Kind::Face - || !BRepGraph_FaceId(aP).IsValidIn(aDefs.Faces()) - || aDefs.Faces().Definition(BRepGraph_FaceId(aP)).IsRemoved) + uint32_t anOwnerCount = 0; + for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + aFaceId < BRepGraph_FaceId(aDefs.Faces().Nb()); + ++aFaceId) { - aResult.Issues.Append( - Issue{Severity::Error, aP, "Orphan WireRef: ParentId is not a live Face"}); + if (aFaceId.IsRemoved(theGraph)) + { + continue; + } + for (const BRepGraph_WireRefId& aRefId : aDefs.Faces().Relations(aFaceId).WireRefIds) + { + if (aRefId == aWireRefId) + { + ++anOwnerCount; + } + } } + appendOwnerIssue(aRef.ChildWireId, anOwnerCount, "WireRef"); } - // CoEdge refs: parent must be a live Wire. - for (BRepGraph_FullCoEdgeRefIterator aCoEdgeRefIt(theGraph); aCoEdgeRefIt.More(); - aCoEdgeRefIt.Next()) + // CoEdges: every active coedge must be owned by at least one live wire. + for (BRepGraph_Iterator aCoEdgeIt(theGraph); aCoEdgeIt.More(); + aCoEdgeIt.Next()) { - const BRepGraph_CoEdgeRefId aCoEdgeRefId = aCoEdgeRefIt.CurrentId(); - const BRepGraphInc::CoEdgeRef& aRef = aRefs.CoEdges().Entry(aCoEdgeRefId); - if (aRef.IsRemoved) + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + if (BRepGraph_NodeId(aCoEdgeId).IsRemoved(theGraph)) { continue; } - const BRepGraph_NodeId aP = aRef.ParentId; - if (!aP.IsValid() || aP.NodeKind != BRepGraph_NodeId::Kind::Wire - || !BRepGraph_WireId(aP).IsValidIn(aDefs.Wires()) - || aDefs.Wires().Definition(BRepGraph_WireId(aP)).IsRemoved) + + const BRepGraph_WireId aWireId = aDefs.CoEdges().Wire(aCoEdgeId); + const bool hasLiveWire = aWireId.IsValidIn(aDefs.Wires()) && !aWireId.IsRemoved(theGraph); + + if (!hasLiveWire) { aResult.Issues.Append( - Issue{Severity::Error, aP, "Orphan CoEdgeRef: ParentId is not a live Wire"}); + Issue{Severity::Error, aCoEdgeId, "Orphan CoEdge: not owned by any live Wire"}); } } - // Vertex refs: parent must be a live Edge. for (BRepGraph_FullVertexRefIterator aVertexRefIt(theGraph); aVertexRefIt.More(); aVertexRefIt.Next()) { const BRepGraph_VertexRefId aVertexRefId = aVertexRefIt.CurrentId(); const BRepGraphInc::VertexRef& aRef = aRefs.Vertices().Entry(aVertexRefId); - if (aRef.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aVertexRefId)) { continue; } - const BRepGraph_NodeId aP = aRef.ParentId; - if (!aP.IsValid() || aP.NodeKind != BRepGraph_NodeId::Kind::Edge - || !BRepGraph_EdgeId(aP).IsValidIn(aDefs.Edges()) - || aDefs.Edges().Definition(BRepGraph_EdgeId(aP)).IsRemoved) + uint32_t anOwnerCount = 0; + for (BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + anEdgeId < BRepGraph_EdgeId(aDefs.Edges().Nb()); + ++anEdgeId) { - aResult.Issues.Append( - Issue{Severity::Error, aP, "Orphan VertexRef: ParentId is not a live Edge"}); + const BRepGraphInc::EdgeDef& anEdge = aDefs.Edges().Definition(anEdgeId); + if (anEdgeId.IsRemoved(theGraph)) + { + continue; + } + if (anEdge.StartVertexRefId == aVertexRefId) + { + ++anOwnerCount; + } + if (anEdge.EndVertexRefId == aVertexRefId) + { + ++anOwnerCount; + } } + appendOwnerIssue(aRef.ChildVertexId, anOwnerCount, "VertexRef"); } - // Occurrence refs: parent must be a live Product. for (BRepGraph_FullOccurrenceRefIterator anOccurrenceRefIt(theGraph); anOccurrenceRefIt.More(); anOccurrenceRefIt.Next()) { const BRepGraph_OccurrenceRefId aOccurrenceRefId = anOccurrenceRefIt.CurrentId(); const BRepGraphInc::OccurrenceRef& aRef = aRefs.Occurrences().Entry(aOccurrenceRefId); - if (aRef.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aOccurrenceRefId)) { continue; } - const BRepGraph_NodeId aP = aRef.ParentId; - if (!aP.IsValid() || aP.NodeKind != BRepGraph_NodeId::Kind::Product - || !BRepGraph_ProductId(aP).IsValidIn(aDefs.Products()) - || aDefs.Products().Definition(BRepGraph_ProductId(aP)).IsRemoved) + uint32_t anOwnerCount = 0; + for (BRepGraph_ProductIterator aProductIt(theGraph); aProductIt.More(); aProductIt.Next()) { - aResult.Issues.Append( - Issue{Severity::Error, aP, "Orphan OccurrenceRef: ParentId is not a live Product"}); + for (const BRepGraph_OccurrenceRefId& aRefId : + aDefs.Products().Relations(aProductIt.CurrentId()).OccurrenceRefIds) + { + if (aRefId == aOccurrenceRefId) + { + ++anOwnerCount; + } + } } + appendOwnerIssue(aRef.ChildOccurrenceId, anOwnerCount, "OccurrenceRef"); + } + + for (BRepGraph_Iterator anOccIt(theGraph); anOccIt.More(); + anOccIt.Next()) + { + if (anOccIt.CurrentId().IsRemoved(theGraph)) + { + continue; + } + int anOwnerCount = 0; + for (BRepGraph_FullOccurrenceRefIterator aRefIt(theGraph); aRefIt.More(); aRefIt.Next()) + { + const BRepGraphInc::OccurrenceRef& aRef = aRefs.Occurrences().Entry(aRefIt.CurrentId()); + if (!theGraph.Refs().Gen().IsRemoved(aRefIt.CurrentId()) + && aRef.ChildOccurrenceId == anOccIt.CurrentId()) + { + ++anOwnerCount; + } + } + appendOwnerIssue(anOccIt.CurrentId(), anOwnerCount, "OccurrenceDef"); } } - // Occurrence-to-Product liveness: every active OccurrenceDef whose ChildDefId + // Occurrence-to-Product liveness: every active OccurrenceDef whose ChildNodeId // points to a Product must target a live Product. This catches the case where // a Product is soft-removed but dependent Occurrences survived. for (BRepGraph_Iterator anOccIt(theGraph); anOccIt.More(); anOccIt.Next()) { const BRepGraphInc::OccurrenceDef& anOcc = anOccIt.Current(); - if (anOcc.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Product) + if (anOcc.ChildNodeId.NodeKind != BRepGraph_NodeId::Kind::Product) { continue; } - const BRepGraph_ProductId aChildProductId(anOcc.ChildDefId); - if (!aChildProductId.IsValidIn(aDefs.Products()) - || aDefs.Products().Definition(aChildProductId).IsRemoved) + const BRepGraph_ProductId aChildProductId(anOcc.ChildNodeId); + if (!aChildProductId.IsValidIn(aDefs.Products()) || aChildProductId.IsRemoved(theGraph)) { aResult.Issues.Append( Issue{Severity::Error, anOccIt.CurrentId(), - "Active OccurrenceDef.ChildDefId points to a removed or out-of-range Product"}); + "Active OccurrenceDef.ChildNodeId points to a removed or out-of-range Product"}); } } @@ -1383,20 +1922,20 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph // an active Compound ChildRef AND an active CompSolid SolidRef. OCCT models // a solid as belonging to either a Compound or a CompSolid, not both. { - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - NCollection_Map aCompoundOwnedSolids; + const BRepGraph::RefsView& aRefs = theGraph.Refs(); + NCollection_FlatMap aCompoundOwnedSolids; for (BRepGraph_FullChildRefIterator aChildRefIt(theGraph); aChildRefIt.More(); aChildRefIt.Next()) { const BRepGraph_ChildRefId aChildRefId = aChildRefIt.CurrentId(); const BRepGraphInc::ChildRef& aCR = aRefs.Children().Entry(aChildRefId); - if (aCR.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aChildRefId)) { continue; } - if (aCR.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Solid && aCR.ChildDefId.IsValid()) + if (aCR.ChildNodeId.NodeKind == BRepGraph_NodeId::Kind::Solid && aCR.ChildNodeId.IsValid()) { - aCompoundOwnedSolids.Add(BRepGraph_SolidId::FromNodeId(aCR.ChildDefId)); + aCompoundOwnedSolids.Add(BRepGraph_SolidId::FromNodeId(aCR.ChildNodeId)); } } for (BRepGraph_FullSolidRefIterator aSolidRefIt(theGraph); aSolidRefIt.More(); @@ -1404,15 +1943,15 @@ BRepGraph_Validate::Result BRepGraph_Validate::Perform(const BRepGraph& theGraph { const BRepGraph_SolidRefId aSolidRefId = aSolidRefIt.CurrentId(); const BRepGraphInc::SolidRef& aSR = aRefs.Solids().Entry(aSolidRefId); - if (aSR.IsRemoved) + if (theGraph.Refs().Gen().IsRemoved(aSolidRefId)) { continue; } - if (aSR.SolidDefId.IsValid() && aCompoundOwnedSolids.Contains(aSR.SolidDefId)) + if (aSR.ChildSolidId.IsValid() && aCompoundOwnedSolids.Contains(aSR.ChildSolidId)) { aResult.Issues.Append( Issue{Severity::Error, - aSR.SolidDefId, + aSR.ChildSolidId, "Cross-container ownership: Solid is referenced by both a Compound (ChildRef) " "and a CompSolid (SolidRef)"}); } diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.hxx index d588b668f0..97bad30f3d 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_Validate.hxx @@ -15,16 +15,15 @@ #define _BRepGraph_Validate_HeaderFile #include - #include -#include +#include #include #include //! @brief Structural invariant checker for BRepGraph. //! //! Read-only algorithm that verifies the graph's internal consistency: -//! cross-reference bounds, reverse index symmetry, incidence ref consistency, +//! cross-reference bounds, relation symmetry, incidence ref consistency, //! geometry reference validity, removed-node isolation, and wire connectivity. //! //! Distinct from BRepGraphCheck (geometric shape validity). This class @@ -35,12 +34,13 @@ //! | Check | Lightweight | Audit | //! |--------------------------------|:-----------:|:-----:| //! | Active entity count boundary | YES | YES | +//! | Document root product sanity | YES | YES | //! | Cross-reference bounds | - | YES | //! | Reverse-index consistency | - | YES | //! | Face-count cache consistency | - | YES | //! | Incidence ref consistency | - | YES | //! | Geometry representation refs | - | YES | -//! | Removed-node isolation | - | YES | +//! | Removed-node isolation | YES | YES | //! | Wire edge connectivity | - | YES | //! | Entity ID positional integrity | - | YES | //! | UID round-trip integrity | - | YES | @@ -50,10 +50,10 @@ //! //! | Mode | What it checks | Cost | Recommended use | //! |------|----------------|------|-----------------| -//! | `Lightweight` | Active entity count boundary only | Low | Hot-path release builds when the -//! graph structure is already trusted | | `Audit` | Full structural audit from cross-reference -//! bounds through assembly DAG cycle detection | Higher | Default validation mode for production -//! pipelines, test gates, and API-boundary verification | +//! | `Lightweight` | Active entity count boundary plus removed-node isolation | Low | Hot-path +//! release builds when the graph structure is already trusted | | `Audit` | Full structural audit +//! from cross-reference bounds through assembly DAG cycle detection | Higher | Default validation +//! mode for production pipelines, test gates, and API-boundary verification | //! //! For production pipelines, prefer `Mode::Audit`; `Mode::Lightweight` is intended //! for hot-path release builds where the graph structure is already trusted. @@ -89,30 +89,13 @@ public: //! Aggregated validation result. struct Result { - NCollection_DynamicArray Issues; + NCollection_LinearVector Issues; //! True if no Error-level issues were found. - [[nodiscard]] bool IsValid() const - { - for (const Issue& anIssue : Issues) - { - if (anIssue.Sev == Severity::Error) - return false; - } - return true; - } + [[nodiscard]] Standard_EXPORT bool IsValid() const; //! Count issues of a given severity. - [[nodiscard]] int NbIssues(const Severity theSev) const - { - int aCount = 0; - for (const Issue& anIssue : Issues) - { - if (anIssue.Sev == theSev) - ++aCount; - } - return aCount; - } + [[nodiscard]] Standard_EXPORT int NbIssues(const Severity theSev) const; }; //! Validation options. @@ -158,8 +141,12 @@ public: [[nodiscard]] Standard_EXPORT static Result Perform(const BRepGraph& theGraph, const Options& theOptions); -private: BRepGraph_Validate() = delete; + +private: + static void CheckOwnedUseReferences( + const BRepGraph& theGraph, + NCollection_LinearVector& theIssues); }; #endif // _BRepGraph_Validate_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_VersionStamp.cxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_VersionStamp.cxx index a000ece0db..1c5afd805e 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_VersionStamp.cxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_VersionStamp.cxx @@ -14,6 +14,7 @@ #include #include +#include #include @@ -27,15 +28,15 @@ Standard_GUID BRepGraph_VersionStamp::ToGUID(const Standard_GUID& theGraphGUID) size_t aCounter = 0; int aKind = 0; - if (myDomain == Domain::Entity) + if (myDomain == Domain::Node) { - aCounter = myUID.Counter(); - aKind = static_cast(myUID.Kind()); + aCounter = myNodeUID.Counter; + aKind = static_cast(myNodeUID.Kind); } - else if (myDomain == Domain::Ref) + else if (myDomain == Domain::Reference) { - aCounter = myRefUID.Counter(); - aKind = static_cast(myRefUID.Kind()); + aCounter = myRefUID.Counter; + aKind = static_cast(myRefUID.Kind); } uint8_t aBuffer[sizeof(aGraphUUID) + sizeof(aDomain) + sizeof(aCounter) + sizeof(aKind) @@ -82,3 +83,72 @@ Standard_GUID BRepGraph_VersionStamp::ToGUID(const Standard_GUID& theGraphGUID) std::memcpy(aDst + THE_QUARTER * 3, &aH4, THE_QUARTER); return Standard_GUID(aResultUUID); } + +//================================================================================================= + +bool BRepGraph_VersionStamp::operator==(const BRepGraph_VersionStamp& theOther) const +{ + if (!IsValid() && !theOther.IsValid()) + { + return true; + } + if (myDomain != theOther.myDomain) + { + return false; + } + if (myMutationGen != theOther.myMutationGen || myGeneration != theOther.myGeneration) + { + return false; + } + if (myDomain == Domain::Node) + { + return myNodeUID == theOther.myNodeUID; + } + if (myDomain == Domain::Reference) + { + return myRefUID == theOther.myRefUID; + } + return myNodeUID == theOther.myNodeUID && myRefUID == theOther.myRefUID; +} + +//================================================================================================= + +bool BRepGraph_VersionStamp::IsSameItem(const BRepGraph_VersionStamp& theOther) const +{ + if (myDomain != theOther.myDomain) + { + return false; + } + if (myDomain == Domain::Node) + { + return myNodeUID == theOther.myNodeUID; + } + if (myDomain == Domain::Reference) + { + return myRefUID == theOther.myRefUID; + } + return myNodeUID == theOther.myNodeUID && myRefUID == theOther.myRefUID; +} + +//================================================================================================= + +size_t BRepGraph_VersionStamp::HashValue() const +{ + size_t aCombination[4]; + aCombination[0] = opencascade::hash(static_cast(myDomain)); + if (myDomain == Domain::Node) + { + aCombination[1] = myNodeUID.HashValue(); + } + else if (myDomain == Domain::Reference) + { + aCombination[1] = myRefUID.HashValue(); + } + else + { + aCombination[1] = opencascade::hash(0); + } + aCombination[2] = opencascade::hash(myMutationGen); + aCombination[3] = opencascade::hash(myGeneration); + return opencascade::hashBytes(aCombination, sizeof(aCombination)); +} diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_VersionStamp.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_VersionStamp.hxx index 963e450f58..fcc3d3b567 100644 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_VersionStamp.hxx +++ b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_VersionStamp.hxx @@ -14,17 +14,18 @@ #ifndef _BRepGraph_VersionStamp_HeaderFile #define _BRepGraph_VersionStamp_HeaderFile +#include #include #include #include #include -//! @brief Snapshot of an entity/ref identity and version at a point in time. +//! @brief Snapshot of a graph item identity and its freshness generation. //! -//! Combines a persistent UID (entity or reference entry) with -//! OwnGen (own-data version counter) and graph Generation (BRepGraph::Clear() cycle). -//! Computed on demand via BRepGraph::UIDs().StampOf(). +//! Combines a persistent node or reference UID with OwnGen (own-data mutation counter) +//! and graph Generation (BRepGraph::Clear() cycle). It is intended for custom cache and +//! layer freshness checks, not as a separate topology identity model. //! //! Usage pattern: //! @code @@ -39,17 +40,16 @@ struct BRepGraph_VersionStamp //! Identity domain encoded in this stamp. enum class Domain : uint8_t { - None = 0, - Entity = 1, - Ref = 2 + None = 0, + Node = 1, + Reference = 2 }; - BRepGraph_UID myUID; //!< Entity identity for entity-domain stamps. - BRepGraph_RefUID myRefUID; //!< Reference identity for ref-domain stamps. - uint32_t - myMutationGen; //!< OwnGen counter at snapshot time (maps to BaseDef::OwnGen / BaseRef::OwnGen). - uint32_t myGeneration; //!< Graph BRepGraph::Clear() generation at snapshot time. - Domain myDomain; //!< Active identity domain. + BRepGraph_UID myNodeUID; //!< Definition-node identity for node-domain stamps. + BRepGraph_RefUID myRefUID; //!< Reference-entry identity for reference-domain stamps. + uint32_t myMutationGen; //!< OwnGen counter at snapshot time. + uint32_t myGeneration; //!< Graph BRepGraph::Clear() generation at snapshot time. + Domain myDomain; //!< Active identity domain. //! Default constructor. Creates an invalid stamp (invalid UID, zero counters). BRepGraph_VersionStamp() @@ -59,17 +59,17 @@ struct BRepGraph_VersionStamp { } - //! Construct an entity-domain stamp from components. - //! @param[in] theUID persistent entity identity + //! Construct a node-domain stamp from components. + //! @param[in] theUID persistent definition-node identity //! @param[in] theMutationGen OwnGen counter (own-data mutation counter) //! @param[in] theGeneration graph BRepGraph::Clear() generation BRepGraph_VersionStamp(const BRepGraph_UID& theUID, const uint32_t theMutationGen, const uint32_t theGeneration) - : myUID(theUID), + : myNodeUID(theUID), myMutationGen(theMutationGen), myGeneration(theGeneration), - myDomain(Domain::Entity) + myDomain(Domain::Node) { } @@ -83,69 +83,69 @@ struct BRepGraph_VersionStamp : myRefUID(theRefUID), myMutationGen(theMutationGen), myGeneration(theGeneration), - myDomain(Domain::Ref) + myDomain(Domain::Reference) { } //! Check if the stamp has a valid identity in its domain. [[nodiscard]] bool IsValid() const { - if (myDomain == Domain::Entity) - return myUID.IsValid(); - if (myDomain == Domain::Ref) + if (myDomain == Domain::Node) + { + return myNodeUID.IsValid(); + } + if (myDomain == Domain::Reference) + { return myRefUID.IsValid(); - return myUID.IsValid() || myRefUID.IsValid(); + } + return myNodeUID.IsValid() || myRefUID.IsValid(); } - //! True when this is an entity-domain stamp. - [[nodiscard]] bool IsEntityStamp() const + //! True when this is a definition-node-domain stamp. + [[nodiscard]] bool IsNodeStamp() const { - if (myDomain == Domain::Entity) - return myUID.IsValid(); - return myDomain == Domain::None && myUID.IsValid() && !myRefUID.IsValid(); + if (myDomain == Domain::Node) + { + return myNodeUID.IsValid(); + } + return myDomain == Domain::None && myNodeUID.IsValid() && !myRefUID.IsValid(); } //! True when this is a reference-domain stamp. [[nodiscard]] bool IsRefStamp() const { - if (myDomain == Domain::Ref) + if (myDomain == Domain::Reference) + { return myRefUID.IsValid(); - return myDomain == Domain::None && myRefUID.IsValid() && !myUID.IsValid(); + } + return myDomain == Domain::None && myRefUID.IsValid() && !myNodeUID.IsValid(); + } + + //! Return the active generic item identity. + [[nodiscard]] BRepGraph_ItemUID ItemUID() const + { + if (myDomain == Domain::Node) + { + return BRepGraph_ItemUID::Node(myNodeUID.Kind, myNodeUID.Counter); + } + if (myDomain == Domain::Reference) + { + return BRepGraph_ItemUID::Reference(myRefUID.Kind, myRefUID.Counter); + } + return BRepGraph_ItemUID(); } //! Full equality: same domain, UID, OwnGen, and Generation. //! Two invalid stamps are equal. - bool operator==(const BRepGraph_VersionStamp& theOther) const - { - if (!IsValid() && !theOther.IsValid()) - return true; - if (myDomain != theOther.myDomain) - return false; - if (myMutationGen != theOther.myMutationGen || myGeneration != theOther.myGeneration) - return false; - if (myDomain == Domain::Entity) - return myUID == theOther.myUID; - if (myDomain == Domain::Ref) - return myRefUID == theOther.myRefUID; - return myUID == theOther.myUID && myRefUID == theOther.myRefUID; - } + Standard_EXPORT bool operator==(const BRepGraph_VersionStamp& theOther) const; bool operator!=(const BRepGraph_VersionStamp& theOther) const { return !(*this == theOther); } - //! Check if two stamps refer to the same entity/reference regardless of version. + //! Check if two stamps refer to the same graph item regardless of version. //! Compares active UID only, ignoring OwnGen and Generation. //! @param[in] theOther stamp to compare with //! @return true if both stamps have the same domain and UID - [[nodiscard]] bool IsSameNode(const BRepGraph_VersionStamp& theOther) const - { - if (myDomain != theOther.myDomain) - return false; - if (myDomain == Domain::Entity) - return myUID == theOther.myUID; - if (myDomain == Domain::Ref) - return myRefUID == theOther.myRefUID; - return myUID == theOther.myUID && myRefUID == theOther.myRefUID; - } + [[nodiscard]] Standard_EXPORT bool IsSameItem(const BRepGraph_VersionStamp& theOther) const; //! Derive a deterministic Standard_GUID from this stamp. //! The graph GUID is incorporated into the hash, making per-node GUIDs @@ -157,20 +157,7 @@ struct BRepGraph_VersionStamp //! Compute hash value consistent with operator==. //! @return hash combining active UID, domain, OwnGen, and Generation - [[nodiscard]] size_t HashValue() const - { - size_t aCombination[4]; - aCombination[0] = opencascade::hash(static_cast(myDomain)); - if (myDomain == Domain::Entity) - aCombination[1] = myUID.HashValue(); - else if (myDomain == Domain::Ref) - aCombination[1] = myRefUID.HashValue(); - else - aCombination[1] = opencascade::hash(0); - aCombination[2] = opencascade::hash(myMutationGen); - aCombination[3] = opencascade::hash(myGeneration); - return opencascade::hashBytes(aCombination, sizeof(aCombination)); - } + [[nodiscard]] Standard_EXPORT size_t HashValue() const; }; //! std::hash specialization for NCollection_DefaultHasher support. diff --git a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_WireExplorer.hxx b/src/ModelingData/TKBRep/BRepGraph/BRepGraph_WireExplorer.hxx deleted file mode 100644 index c2fb49c8ac..0000000000 --- a/src/ModelingData/TKBRep/BRepGraph/BRepGraph_WireExplorer.hxx +++ /dev/null @@ -1,215 +0,0 @@ -// 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. - -#ifndef _BRepGraph_WireExplorer_HeaderFile -#define _BRepGraph_WireExplorer_HeaderFile - -#include -#include -#include -#include -#include -#include - -class BRepGraph; - -//! @brief Iterator for traversing wire edges in connection order using graph data. -//! @see BRepGraph class comment "Iterator guide" for choosing between iterator types. -//! -//! Reorders wire coedges by vertex adjacency: the end vertex of each edge -//! matches the start vertex of the next. This is the graph equivalent of -//! BRepTools_WireExplorer, operating on pre-built BRepGraph data. -//! -//! The coedges are reordered on construction (O(N^2) worst case for N coedges). -//! For most wires this is fast since N is small (4-8 edges typically). -//! -//! Internal storage uses NCollection_LocalArray with stack allocation for -//! wires with up to 16 edges (the common case), falling back to heap for larger wires. -//! -//! Usage: -//! @code -//! BRepGraph_WireExplorer anExp(aGraph, aWireId); -//! for (; anExp.More(); anExp.Next()) -//! { -//! const BRepGraph_CoEdgeId aCoEdgeId = anExp.CurrentCoEdgeId(); -//! const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aCoEdgeId); -//! // ... use aDef ... -//! } -//! @endcode -class BRepGraph_WireExplorer -{ -public: - //! Initialize the explorer from a pre-built BRepGraph and wire identifier. - //! Collects coedge IDs from graph iterators and reorders them by vertex connectivity. - //! @param[in] theGraph pre-built BRepGraph (IsDone() == true) - //! @param[in] theWire wire definition identifier - BRepGraph_WireExplorer(const BRepGraph& theGraph, const BRepGraph_WireId theWire) - : myCurrent(0), - myLength(0) - { - buildOrder(theGraph, theWire); - } - - //! Returns true if there are more edges to iterate. - bool More() const { return myCurrent < myLength; } - - //! Advance to the next edge. - void Next() { ++myCurrent; } - - //! Reset the iterator to the beginning (for re-iteration). - void Reset() { myCurrent = 0; } - - //! Current coedge definition identifier in connection order. - BRepGraph_CoEdgeId CurrentCoEdgeId() const { return myOrder[myCurrent]; } - - //! Number of coedges in the ordered sequence. - int NbEdges() const { return myLength; } - - //! Current coedge identifier (alias for CurrentCoEdgeId(), enables range-for). - BRepGraph_CoEdgeId Current() const { return CurrentCoEdgeId(); } - - //! Returns an STL-compatible iterator for range-based for loops. - //! Yields BRepGraph_CoEdgeId values. - NCollection_ForwardRangeIterator begin() - { - return NCollection_ForwardRangeIterator(this); - } - - //! Returns a sentinel marking the end of iteration. - NCollection_ForwardRangeSentinel end() const { return NCollection_ForwardRangeSentinel{}; } - -private: - //! Resolve the oriented start vertex of an edge. - static BRepGraph_NodeId orientedStartVertex(const BRepGraph& theGraph, - const BRepGraphInc::EdgeDef& theEdge, - const TopAbs_Orientation theOrientation) - { - const BRepGraph_VertexRefId aRefId = - (theOrientation == TopAbs_FORWARD) ? theEdge.StartVertexRefId : theEdge.EndVertexRefId; - if (!aRefId.IsValid()) - return BRepGraph_NodeId(); - return theGraph.Refs().Vertices().Entry(aRefId).VertexDefId; - } - - //! Resolve the oriented end vertex of an edge. - static BRepGraph_NodeId orientedEndVertex(const BRepGraph& theGraph, - const BRepGraphInc::EdgeDef& theEdge, - const TopAbs_Orientation theOrientation) - { - const BRepGraph_VertexRefId aRefId = - (theOrientation == TopAbs_FORWARD) ? theEdge.EndVertexRefId : theEdge.StartVertexRefId; - if (!aRefId.IsValid()) - return BRepGraph_NodeId(); - return theGraph.Refs().Vertices().Entry(aRefId).VertexDefId; - } - - //! Recursive backtracking chain: try to extend myOrder from theDepth onward, - //! picking each unused candidate whose oriented start matches the previous - //! oriented end. Returns true iff a full chain covering [0, theNbEdges) is built. - bool chainRecursive(const BRepGraph& theGraph, - const NCollection_LocalArray& theInput, - NCollection_LocalArray& theUsed, - const int theDepth, - const int theNbEdges) - { - if (theDepth == theNbEdges) - return true; - - const BRepGraphInc::CoEdgeDef& aPrevCoEdge = - theGraph.Topo().CoEdges().Definition(myOrder[theDepth - 1]); - const BRepGraphInc::EdgeDef& aPrevEdge = - theGraph.Topo().Edges().Definition(aPrevCoEdge.EdgeDefId); - const BRepGraph_NodeId aPrevEnd = - orientedEndVertex(theGraph, aPrevEdge, aPrevCoEdge.Orientation); - - for (int i = 0; i < theNbEdges; ++i) - { - if (theUsed[i]) - continue; - const BRepGraphInc::CoEdgeDef& aCandCoEdge = - theGraph.Topo().CoEdges().Definition(theInput[i]); - const BRepGraphInc::EdgeDef& aCandEdge = - theGraph.Topo().Edges().Definition(aCandCoEdge.EdgeDefId); - const BRepGraph_NodeId aCandStart = - orientedStartVertex(theGraph, aCandEdge, aCandCoEdge.Orientation); - - if (!aPrevEnd.IsValid() || !aCandStart.IsValid() || aPrevEnd != aCandStart) - continue; - - myOrder[theDepth] = theInput[i]; - theUsed[i] = true; - if (chainRecursive(theGraph, theInput, theUsed, theDepth + 1, theNbEdges)) - return true; - theUsed[i] = false; - } - return false; - } - - //! Build connection-ordered coedge sequence from graph data. - //! Uses greedy depth-first backtracking so that wires with ambiguous - //! continuations (e.g. cylinder lateral face with a seam pair) still produce - //! a fully connected chain whenever one exists. For pathologically disconnected - //! wires, remaining coedges are appended in input order. - void buildOrder(const BRepGraph& theGraph, const BRepGraph_WireId theWire) - { - int aNbEdges = 0; - for (BRepGraph_RefsCoEdgeOfWire aCountIt(theGraph, theWire); aCountIt.More(); aCountIt.Next()) - ++aNbEdges; - - if (aNbEdges == 0) - return; - - NCollection_LocalArray anInput(aNbEdges); - { - int anIdx = 0; - for (BRepGraph_RefsCoEdgeOfWire aCEIt(theGraph, theWire); aCEIt.More(); aCEIt.Next()) - { - const BRepGraphInc::CoEdgeRef& aCRef = theGraph.Refs().CoEdges().Entry(aCEIt.CurrentId()); - anInput[anIdx++] = aCRef.CoEdgeDefId; - } - } - - myOrder.Allocate(aNbEdges); - myLength = aNbEdges; - - NCollection_LocalArray aUsed(aNbEdges); - for (int i = 0; i < aNbEdges; ++i) - aUsed[i] = false; - - myOrder[0] = anInput[0]; - aUsed[0] = true; - - if (!chainRecursive(theGraph, anInput, aUsed, 1, aNbEdges)) - { - // Pathologically disconnected wire: append any unused coedges in input order. - for (int aPlaced = 1; aPlaced < aNbEdges; ++aPlaced) - { - for (int i = 0; i < aNbEdges; ++i) - { - if (!aUsed[i]) - { - myOrder[aPlaced] = anInput[i]; - aUsed[i] = true; - break; - } - } - } - } - } - - NCollection_LocalArray myOrder; //!< Ordered coedge IDs (stack for <=16). - int myCurrent; //!< Current iteration index. - int myLength; //!< Number of coedges. -}; - -#endif // _BRepGraph_WireExplorer_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraph/FILES.cmake b/src/ModelingData/TKBRep/BRepGraph/FILES.cmake index e2b5c50895..dd2150424b 100644 --- a/src/ModelingData/TKBRep/BRepGraph/FILES.cmake +++ b/src/ModelingData/TKBRep/BRepGraph/FILES.cmake @@ -3,11 +3,13 @@ set(OCCT_BRepGraph_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_BRepGraph_FILES BRepGraph.cxx BRepGraph.hxx - BRepGraph_CacheKindIterator.hxx - BRepGraph_CacheView.cxx - BRepGraph_CacheView.hxx - BRepGraph_Builder.cxx - BRepGraph_Builder.hxx + BRepGraph_Cache.cxx + BRepGraph_Cache.hxx + BRepGraph_CacheDerivedState.cxx + BRepGraph_CacheDerivedState.hxx + BRepGraph_CacheIterator.hxx + BRepGraph_CacheRegistry.cxx + BRepGraph_CacheRegistry.hxx BRepGraph_EditorView.cxx BRepGraph_EditorView.hxx BRepGraph_EditorView_Mut.cxx @@ -16,48 +18,55 @@ set(OCCT_BRepGraph_FILES BRepGraph_ChildExplorer.cxx BRepGraph_ChildExplorer.hxx BRepGraph_Iterator.hxx + BRepGraph_ItemId.hxx + BRepGraph_ItemUID.hxx BRepGraph_ParentExplorer.cxx BRepGraph_ParentExplorer.hxx BRepGraph_Data.hxx BRepGraph_TopoView.cxx BRepGraph_TopoView.hxx - BRepGraph_History.cxx - BRepGraph_History.hxx - BRepGraph_HistoryRecord.hxx + BRepGraph_LayerHistory.cxx + BRepGraph_LayerHistory.hxx BRepGraph_Layer.cxx BRepGraph_Layer.hxx + BRepGraph_LayerParametric.cxx + BRepGraph_LayerParametric.hxx + BRepGraph_LayerDeferred.cxx + BRepGraph_LayerDeferred.hxx BRepGraph_LayerIterator.hxx + BRepGraph_LayerLock.cxx + BRepGraph_LayerLock.hxx BRepGraph_LayerRegistry.cxx BRepGraph_LayerRegistry.hxx BRepGraph_DeferredScope.hxx - BRepGraph_MeshCache.cxx - BRepGraph_MeshCache.hxx + BRepGraph_CacheMesh.cxx + BRepGraph_CacheMesh.hxx BRepGraph_MeshView.cxx BRepGraph_MeshView.hxx BRepGraph_MutGuard.hxx BRepGraph_NodeId.hxx - BRepGraph_LayerParam.cxx - BRepGraph_LayerParam.hxx + BRepGraph_LayerTopoSupplement.cxx + BRepGraph_LayerTopoSupplement.hxx + BRepGraph_ParallelPolicy.cxx BRepGraph_ParallelPolicy.hxx BRepGraph_RefId.hxx BRepGraph_RefUID.hxx + BRepGraph_RelatedIterator.cxx BRepGraph_RelatedIterator.hxx BRepGraph_ReverseIterator.hxx BRepGraph_RefsIterator.hxx BRepGraph_RefsView.cxx BRepGraph_RefsView.hxx - BRepGraph_LayerRegularity.cxx - BRepGraph_LayerRegularity.hxx - BRepGraph_RepId.hxx BRepGraph_ShapesView.cxx BRepGraph_ShapesView.hxx + BRepGraph_SupplementIterator.cxx + BRepGraph_SupplementIterator.hxx + BRepGraph_SupplementEditor.cxx + BRepGraph_SupplementEditor.hxx BRepGraph_Tool.cxx BRepGraph_Tool.hxx - BRepGraph_RefTransientCache.cxx - BRepGraph_RefTransientCache.hxx - BRepGraph_TransientCache.cxx - BRepGraph_TransientCache.hxx - BRepGraph_WireExplorer.hxx + BRepGraph_UsagePath.cxx + BRepGraph_UsagePath.hxx BRepGraph_UID.hxx BRepGraph_UIDsView.cxx BRepGraph_VersionStamp.cxx @@ -69,6 +78,8 @@ set(OCCT_BRepGraph_FILES BRepGraph_Compact.cxx BRepGraph_Copy.hxx BRepGraph_Copy.cxx + BRepGraph_CopyRemap.hxx + BRepGraph_CopyRemap.cxx BRepGraph_Transform.hxx BRepGraph_Transform.cxx BRepGraph_Validate.hxx diff --git a/src/ModelingData/TKBRep/BRepGraph/README.md b/src/ModelingData/TKBRep/BRepGraph/README.md index ddf8efb516..7263b4a796 100644 --- a/src/ModelingData/TKBRep/BRepGraph/README.md +++ b/src/ModelingData/TKBRep/BRepGraph/README.md @@ -1,6 +1,7 @@ # BRepGraph -BRepGraph is a facade API over an incidence-table topology backend for TopoDS/BRep shapes. +BRepGraph is the high-level graph API over the `BRepGraphInc` incidence-table backend for +TopoDS/BRep shapes. ## Why It Exists @@ -30,20 +31,22 @@ The goal is to make workflows like sewing, healing, compact, and deduplicate eas kind, avoid kind, emit flag, accumulate-location/orientation, start location/orientation) for the downward walker. The new `BRepGraph_ChildExplorer(graph, root, Config{...})` constructor is the preferred long-term - idiom; existing overloads stay in place for compat. Extend `Config` with new fields + idiom; extend `Config` with new fields instead of adding new constructor overloads. -- **Concrete layers renamed** for `*Layer*` prefix grouping: - `BRepGraph_ParamLayer` -> `BRepGraph_LayerParam`, `BRepGraph_RegularityLayer` -> - `BRepGraph_LayerRegularity`. Callers must update includes and type names. +- **Derived topology and geometry state** lives in cache services. Caches are + graph-local temporary services and compute missing values from the current graph. ## Current Model (April 2026) The runtime model is incidence-first: -- Source of truth: BRepGraphInc_Storage +- Source of truth: `BRepGraphInc_Storage` - Topology defs in BRepGraph are aliases to incidence entities - Orientation/location context is stored on incidence refs - No separate runtime Usage storage layer +- `BRepGraphInc` headers are intentionally available for advanced low-level users; callers + using them directly must maintain relation tables, UID maps, active counters, generation + counters, and cache invalidation explicitly. See backend details in `src/ModelingData/TKBRep/BRepGraphInc/README.md`. @@ -55,8 +58,7 @@ flowchart TB subgraph Views V1[Topo / UIDs / Shapes] - V2[Cache / Builder / Refs] - V3[Paths] + V2[Editor / Mesh / Refs] end G --> Views @@ -67,25 +69,25 @@ flowchart TB S --> E[Entity tables] S --> AS[Assembly tables] - S --> X[Reverse index] + S --> X[Relation tables] S --> T[TShape to NodeId] S --> O[Original shapes] S --> U[UID vectors] ``` -## Views Reference +## Public Accessors -All queries and mutations go through lightweight view objects obtained from a `BRepGraph` instance. +Queries and mutations go through accessors obtained from a `BRepGraph` instance. -| View | Accessor | Purpose | +| Accessor | Method | Purpose | |------|----------|---------| | **TopoView** | `Topo()` | Const topology definition access, representation access, adjacency queries, and raw Product/Occurrence definition storage | | **UIDsView** | `UIDs()` | UID allocation for active entries (`Of`), active-only lookup (`NodeIdFrom`/`RefIdFrom`), and validity checking (`Has`) | -| **ShapesView** | `Shapes()` | Cached `Shape()` access (`null` for invalid/removed nodes), fresh `Reconstruct()` (`null` for invalid/removed nodes), `FindOriginal()/HasOriginal()` non-throw original lookup for active nodes, strict `OriginalOf()` (throws when absent), and FindNode/HasNode reverse lookup for active nodes | -| **CacheView** | `Cache()` | Stable public transient cache access (Set/Get/Has/Remove per-node and per-ref cached values). Supports `CacheKindIter()` for enumerating active cache kinds on a node or ref. Low-level reserve, transfer, and explicit generation-aware access remain on `TransientCache()` / `RefTransientCache()` for algorithm code. | +| **ShapesView** | `Shapes()` | Cached `Shape()` access (`null` for invalid/removed nodes), fresh `Reconstruct()` (`null` for invalid/removed nodes), `Original()/HasOriginal()` non-throw original lookup (`null` when absent), and FindNode/HasNode reverse lookup for active nodes | +| **CacheRegistry** | `CacheRegistry()` | GUID-keyed registry of typed transient cache services. Supports `Find()`, `Ensure()`, explicit registration, unregister, bulk clear, and `BRepGraph_CacheIterator` for enumerating live cache services. | | **EditorView** | `Editor()` | All mutation: creation (nested `VertexOps`/`EdgeOps`/`WireOps`/`FaceOps`/... `Add(...)` / `Split(...)`), field-level `Mut*()` RAII guards (`MutEdge`, `MutFace`, `MutShell`, `MutProduct`, `MutVertexRef`, `MutSurface`, ...), structural removal (RemoveNode, RemoveSubgraph, RemoveRef with orphan pruning), SetCoEdgePCurve, ClearFaceMesh, ClearEdgePolygon3D, AppendFlattenedShape, AppendFullShape, rep creation (CreateTriangulationRep, CreatePolygon3DRep, CreatePolygonOnTriRep), ValidateMutationBoundary. EditorView absorbs the former `AccessView`: there is a single entry point for all mutation. | | **RefsView** | `Refs()` | Reference entry access, RefUID lookup, VersionStamp for refs | -| **MeshView** | `Mesh()` | Read-only mesh cache queries with cache-first, persistent-fallback priority. For mesh-cache writes use `BRepGraph_Tool::Mesh`. | +| **MeshView** | `Mesh()` | Mesh cache and persistent mesh representation queries with cache-first, persistent-fallback priority. Use the non-const mesh/editor APIs for mesh-cache writes. | `TopoView` also exposes grouped node-oriented helpers for discoverable read queries: @@ -94,7 +96,7 @@ All queries and mutations go through lightweight view objects obtained from a `B - `Topo().Compounds()`, `CompSolids()` - `Topo().Products()`, `Occurrences()` -Keep `Refs()` as the home for APIs returning `RefId` vectors and reference-entry payloads. +Keep `Refs()` as the home for APIs returning `RefId` vectors and reference-entry representations. ### Non-View Helpers @@ -104,13 +106,18 @@ Use `BRepGraph_ChildExplorer` and `BRepGraph_ParentExplorer` directly for struct | Accessor | Purpose | |----------|---------| -| `History()` | Mutation history subsystem (lineage records) | -| `TransientCache()` | Raw transient algorithm cache for low-level algorithms needing reserve, transfer, or explicit generation-aware access; public callers should prefer `Cache()` | -| `RefTransientCache()` | Per-reference transient cache (symmetric to `TransientCache()`, keyed by `RefId`, freshness via `OwnGen`); public callers should prefer `Cache()` | | `LayerRegistry()` | Access the GUID-keyed runtime registry of registered layers | | `LayerRegistry().RegisterLayer(layer)` | Register a `BRepGraph_Layer` plugin explicitly | -| `LayerRegistry().FindLayer(guid)` / `LayerRegistry().FindLayer()` | Lookup a registered layer by GUID or layer type | +| `LayerRegistry().Find(guid)` / `LayerRegistry().Find()` / `LayerRegistry().Ensure()` | Lookup or create a registered layer by GUID or layer type | | `LayerRegistry().UnregisterLayer(guid)` | Remove a registered layer by GUID | +| `CacheRegistry()` | Access the GUID-keyed runtime registry of transient cache families | +| `CacheRegistry().RegisterCache(cache)` | Register a `BRepGraph_Cache` family explicitly | +| `CacheRegistry().FindCache(guid)` / `CacheRegistry().Find()` / `CacheRegistry().Ensure()` | Lookup or create a registered cache family by GUID or cache type | +| `CacheRegistry().UnregisterCache(guid)` | Remove a registered cache family by GUID | + +`BRepGraph_LayerHistory` is registered as a normal layer. Use +`graph.LayerRegistry().Ensure()` when an operation should create/record +history, and `graph.LayerRegistry().Find()` when history is optional input. ## Main Data Concepts @@ -121,8 +128,8 @@ Use `BRepGraph_ChildExplorer` and `BRepGraph_ParentExplorer` directly for struct - **RepId** (Kind + Index): separate geometry/mesh addressing decoupled from topology nodes - **Topology entities**: Vertex, Edge, CoEdge, Wire, Face, Shell, Solid, Compound, CompSolid - **Assembly entities**: Product (part or assembly), Occurrence (placed instance) -- **Context refs**: VertexUsage, CoEdgeUsage, WireUsage, FaceUsage, ShellUsage, SolidUsage, ChildUsage, OccurrenceUsage -- **Reverse indices**: edge->wire, edge->face, edge->coedge, vertex->edge, wire->face, face->shell, shell->solid, product->occurrences +- **Reference entries**: VertexRef, WireRef, FaceRef, ShellRef, SolidRef, ChildRef, OccurrenceRef +- **Relation tables**: ordered child refs and incoming parent/use lists for topology and product graph traversal ## Reference Identity (RefId) @@ -130,11 +137,11 @@ Reference entries are the typed edges of the incidence graph. Each ref kind has ### Ref Kinds -8 ref kinds: Shell, Face, Wire, CoEdge, Vertex, Solid, Child, Occurrence. Type-safe wrappers: `BRepGraph_ShellRefId`, `BRepGraph_FaceRefId`, `BRepGraph_WireRefId`, `BRepGraph_CoEdgeRefId`, `BRepGraph_VertexRefId`, `BRepGraph_SolidRefId`, `BRepGraph_ChildRefId`, `BRepGraph_OccurrenceRefId`. +7 ref kinds: Shell, Face, Wire, Vertex, Solid, Child, Occurrence. Type-safe wrappers: `BRepGraph_ShellRefId`, `BRepGraph_FaceRefId`, `BRepGraph_WireRefId`, `BRepGraph_VertexRefId`, `BRepGraph_SolidRefId`, `BRepGraph_ChildRefId`, `BRepGraph_OccurrenceRefId`. ### BaseRef and Ref -`BaseRef` is the common header for all reference entries: `RefId` + `ParentId` + `OwnGen` + `IsRemoved`. Concrete ref entry types (e.g. `ShellRef`, `FaceRef`) extend BaseRef with `DefId` + `Orientation` + `LocalLocation`. +`BaseRef` is the common header for all reference entries: `RefId` + `OwnGen` + `IsRemoved`. Normal topology refs (e.g. `ShellRef`, `FaceRef`) extend BaseRef with typed parent and child ids plus `Orientation`; `ChildRef` and `OccurrenceRef` additionally carry `LocalLocation`. Parent definition containers are the canonical owner lists for refs. ### RefUID @@ -157,8 +164,7 @@ Reference entries are the typed edges of the incidence graph. Each ref kind has - UID operations: `UIDOf(refId)`, `RefIdFrom(uid)` - Parent-to-ref vectors: `ShellRefIdsOf(solidId)`, `FaceRefIdsOf(shellId)`, etc. -Face outer-wire convenience is available from grouped `TopoView` helpers: -- `Topo().Faces().OuterWire(faceId)` +Face outer-wire lookup is available from `BRepGraph_Tool::Face::OuterWire(graph, faceId)`. ## Core Pipelines @@ -170,11 +176,11 @@ flowchart LR P1 --> P2[Parallel face extraction] P2 --> P3[Sequential registration and dedup] P3 --> P4[Post-passes] - P4 --> P5[Reverse index build] + P4 --> P5[Relation finalization] P5 --> D[IsDone] ``` -After topology population, `BRepGraph_Builder::Perform()` can auto-create a single root Product wrapping the top-level topology node. This graph-level policy is controlled by `BRepGraph_Builder::BuildOptions::CreateAutoProduct` (default true), because it is implemented by the builder layer rather than the backend population pipeline. When disabled (e.g. XCAF builder manages Products itself), the caller is responsible for creating Products. +After topology population, `BRepGraph::ShapesView::Add()` can auto-create a single root Product wrapping the top-level topology node. This graph-level policy is controlled by `BRepGraph::ShapesView::Options::CreateAutoProduct` (default true), because it is implemented by the shape facade rather than the backend population pipeline. When disabled (e.g. XCAF builder manages Products itself), the caller is responsible for creating Products. ### Reconstruct @@ -216,41 +222,46 @@ flowchart LR O3[Occurrence 3] end - RP -->|OccurrenceUsage| O1 - RP -->|OccurrenceUsage| O2 - O1 -->|ProductDefId| P1 - O2 -->|ProductDefId| P2 - P2 -->|OccurrenceUsage| O3 - O3 -->|ProductDefId| P1 + RP -->|OccurrenceRef| O1 + RP -->|OccurrenceRef| O2 + O1 -->|ChildNodeId| P1 + O2 -->|ChildNodeId| P2 + P2 -->|OccurrenceRef| O3 + O3 -->|ChildNodeId| P1 ``` -- **ProductDef**: `ShapeRootId` (topology root for parts; invalid for assemblies), `RootOrientation`, `RootLocation`, `OccurrenceRefIds` (child occurrences) -- **OccurrenceDef**: `ProductDefId` (referenced product), `ParentProductDefId` (parent assembly), `ParentOccurrenceDefId` (parent occurrence for tree-structured placement chains), `Placement` (TopLoc_Location) +- **ProductRelations**: ordered `OccurrenceRefIds` owned by the parent product +- **OccurrenceDef**: `ChildNodeId` (referenced Product or topology root) +- **OccurrenceRef**: `ParentProductId`, `ChildOccurrenceId`, `LocalLocation` ### Placement Composition -`Paths().OccurrenceLocation(occId)` walks `ParentOccurrenceDefId` from leaf to root, composing `Placement` transforms. DAG-safe: shared products placed at multiple locations have distinct occurrence paths. +`BRepGraph_ChildExplorer` and `BRepGraph_ParentExplorer` compose `ChildRef::LocalLocation` and `OccurrenceRef::LocalLocation` along the explicit path. DAG-safe: shared products placed at multiple locations have distinct occurrence refs and distinct traversal paths. ### API Distribution | View | Methods | |------|---------| | **TopoView** | `NbProducts`, `NbOccurrences`, grouped helpers `Products()` / `Occurrences()` | -| **PathView** | `RootProducts`, `IsAssembly`, `IsPart`, `NbComponents`, `Component`, `OccurrenceLocation(occId)` | -| **EditorView** | `AddProduct`, `AddAssemblyProduct`, `AddOccurrence` (with optional parent occurrence), `RemoveSubgraph` (cascades to child occurrences), `MutProduct(i)`, `MutOccurrence(i)` (RAII guards) | +| **TopoView** | `IsAssembly`, `IsPart`, `NbComponents`, `Component`, `OccurrenceLocation(occId)`, grouped helpers `Products()` / `Occurrences()` | +| **BRepGraph** | `RootProductIds` | +| **EditorView** | `Add(shapeRoot, placement)`, `Add()`, `Append(parent, child, ...)`, `AppendDocumentRoot`, `RemoveOccurrence`, `RemoveShapeRoot`, `MutProduct(i)`, `MutOccurrence(i)` (RAII guards) | | **Traversal** | Flat definition traversal via `BRepGraph_ProductIterator` / `BRepGraph_OccurrenceIterator` (or explicit `NbProducts()` / `NbOccurrences()` scans when storage-level access is required) | ### Single-Shape Graph -`BRepGraph_Builder::Perform(aGraph, aBox)` creates one Product with `ShapeRootId = Solid(0)`, zero occurrences. Algorithms always see a uniform model. +`aGraph.Shapes().Add(aBox)` creates one Product that owns one shape-root +Occurrence whose `ChildNodeId` is the topology root, for example `Solid(0)`. +Traversal therefore sees the same explicit `Product -> Occurrence -> topology` +model used by imported assemblies. ## Traversal BRepGraph provides a context-preserving traversal system for walking the hierarchy from any root down to entities of a target kind, producing full occurrence paths with composed locations and orientations. -### TopologyPath +### UsagePath -`BRepGraph_TopologyPath` uniquely identifies one occurrence of an entity by encoding the root and a sequence of ref-index steps through the incidence hierarchy. The step model is uniform: assembly occurrences, compound containers, and topology entities are all just steps. +`BRepGraph_UsagePath` identifies one concrete traversal branch by recording the visited nodes plus the reference entry, when present, for each step. The step model is uniform: assembly occurrences, compound containers, and topology entities are all explicit steps. ### Explorer @@ -268,21 +279,14 @@ for (BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_SolidId(0), Can also start from a Product to descend through assembly occurrences into topology. -### PathView +### Traversal Paths -`PathView` (via `Paths()`) resolves topology paths: +`BRepGraph_ChildExplorer` and `BRepGraph_ParentExplorer` resolve concrete usage paths through the explicit graph: -- `RootProductIds()` / `BRepGraph_RootIterator` / `IsAssembly()` / `IsPart()` / `NbComponents()` / `Component()` - graph-root and assembly-aware product traversal --- `GlobalLocation(path)` / `GlobalOrientation(path)` - composed transforms -- `ForEachPathTo(node, alloc, callback)` / `ForEachPathFromTo(root, leaf, alloc, callback)` - lazy reverse path enumeration without result-vector materialization -- `ForEachNodeLocation(node, alloc, callback)` - lazy occurrence enumeration with path, location, and orientation per branch --- `PathsTo(node)` - all paths from any root to a given entity (reverse lookup) --- `NodeLocations(node)` - all occurrence entries with paths, locations, orientations --- `CommonAncestor(path1, path2)` - longest common prefix --- `FilterByInclude` / `FilterByExclude` - path set filtering - -The vector-returning reverse lookup methods remain as convenience wrappers over the lazy enumeration layer. -- `IsAncestorOf`, `AllNodesOnPath`, `DepthOfKind` +- `RootProductIds()` plus `Topo().Products()` helpers provide graph-root and assembly-aware product queries. +- `CurrentUsagePath()` records the concrete node/ref steps for a downward traversal branch. +- `Current().Location` and `Current().Orientation` expose transforms composed along the emitted branch. +- Recursive target-kind traversal filters emitted nodes, but it must still walk through intermediate `Product` and `Occurrence` nodes rather than inventing direct topology shortcuts. ### RelatedIterator @@ -297,7 +301,7 @@ for (BRepGraph_RelatedIterator anIt(aGraph, BRepGraph_NodeId(aFaceId)); anIt.Mor } ``` -Relation kinds include: `ChildShell`, `ChildFace`, `FreeChild`, `BoundaryEdge`, `AdjacentFace`, `OuterWire`, `ReferencedByFace`, `IncidentVertex`, `WireCoEdge`, `IncidentEdge`, `ParentEdge`, `OwningFace`, `SeamPair`, `ChildEntity`, `ChildSolid`, `ChildOccurrence`, `ReferencedProduct`, `ParentProduct`, `ParentOccurrence`. +Relation kinds include: `BoundaryEdge`, `AdjacentFace`, `OuterWire`, `ReferencedByFace`, `IncidentVertex`, `WireCoEdge`, `OwningFace`, `IncidentEdge`, `ParentEdge`, `SeamPair`. ### Connected Components @@ -309,13 +313,14 @@ Disconnected topology is grouped on demand by walking from faces to their solid | Helper | Key Methods | |--------|-------------| -| **Vertex** | `Pnt`, `Tolerance`, `Parameter` (on edge), `Parameters` (on surface) | -| **Edge** | `Tolerance`, `Degenerated`, `SameParameter`, `SameRange`, `Range`, `StartVertex`, `EndVertex`, `Curve`, `Polygon`, `Continuity` | -| **CoEdge** | `PCurveGeometry`, `PCurvePolygon`, `PCurveIsHandle` | -| **Face** | `Surface`, `Tolerance`, `NaturalRestriction`, `Wires`, `BndLib`, `UVBounds`, `CurveOnPlane`, `EvalD0` | -| **Wire** | `Edges` (traversal order via WireExplorer) | +| **Vertex** | `Usage`, `Pnt` (definition frame, with ref location, by ref id), `Tolerance`, `NbEdges` | +| **Edge** | `Tolerance`, `Degenerated`, `SameParameter`, `SameRange`, `IsClosed`, `Range`, `StartVertexId`, `EndVertexId`, `HasCurve`, `Curve`, `CurveAdaptor`, `CurveOnSurface`, `FindByVertices`, `FindPCurveCoEdgeId`, `FindCoEdgeId`, `NbFaces`, `IsManifold`, `IsBoundary`, `IsSeamOnFace` (Degenerated, SameParameter, SameRange are derived queries, not stored fields) | +| **CoEdge** | `Orientation`, `IsReversed`, `EdgeOf`, `FaceOf`, `SeamPair`, `IsSeam`, `HasPCurve`, `PCurve`, `PCurveAdaptor`, `UVPoints`, `Range` | +| **Face** | `Usage`, `Tolerance`, `HasSurface`, `OuterWire`, `Surface`, `SurfaceAdaptor`, `NbWires`, `Bounds` | +| **Wire** | `Usage`, `IsClosed`, `NbCoEdges`, `NbDistinctEdges`, `FaceOf`, `IsOuter` | +| **Shell** | `Usage`, `IsClosed`, `NbFaces` | -## Extensibility: Layers vs TransientCache +## Extensibility: Layers vs Cache Registry `UserAttribute` naming is reserved for the future persistent metadata subsystem. @@ -327,45 +332,70 @@ layers are added explicitly via `LayerRegistry().RegisterLayer()`. - **Purpose**: persistent domain metadata (colors, materials, names, layer groups) - **Identity**: `Standard_GUID`, not display name - **Name**: display-only metadata returned by `BRepGraph_Layer::Name()` -- **Storage**: internal maps keyed by NodeId, owned by the layer -- **Lifecycle**: `OnNodeRemoved(old, replacement)` migrates data; `OnCompact(remapMap)` remaps; `OnNodeModified`/`OnNodesModified` for node mutation tracking; `OnRefRemoved`/`OnRefModified`/`OnRefsModified` for reference mutation tracking (subscribed via `SubscribedRefKinds()` bitmask) +- **Storage**: internal maps keyed by `BRepGraph_NodeId`, `BRepGraph_RefId`, `BRepGraph_RepId`, or `BRepGraph_ItemId`, owned by the layer +- **Lifecycle**: typed virtual callbacks remain the extension points: `OnNodeRemoved(node)` drops data for pure deletions; `OnNodeReplaced(old, replacement)` migrates compatible data; `OnCompact(remapMap)` remaps; `OnNodeModified`/`OnNodesModified` for node mutation tracking; `OnRefRemoved`/`OnRepRemoved` for reference/representation deletion tracking; `OnRefModified`/`OnRefsModified` for reference mutation tracking (subscribed via `SubscribedRefKinds()` bitmask). `OnItemRemoved(item)` and `OnItemModified(item)` are non-virtual dispatch helpers for callers that already hold a `BRepGraph_ItemId`. - **Survives mutations**: yes -- **Examples**: `BRepGraph_LayerParam`, `BRepGraph_LayerRegularity` +- **Examples**: `BRepGraph_LayerTopoSupplement`, `BRepGraph_LayerLock`, `BRepGraph_LayerDeferred` + +`BRepGraph_LayerLock` stores graph-item ownership for definitions, references, and representations. +The public ownership state is a small `IsOwned` flag on the item itself, while the typed owner ID +(`Standard_GUID`) lives in the layer. Mutation entry points reject owned items; owner-specific code must release or resolve its +layer ownership before writing the controlled item. + +`BRepGraph_MutGuard` provides transient reentrancy protection via an active guard set. +When a `Mut()` factory returns a guard, the item is registered as actively guarded. +Attempting to acquire a second guard on the same item throws. The guard deregisters +on destruction. This prevents double-mutation independently of the persistent ownership model. + +`BRepGraph_LayerDeferred` is the provider-neutral base for postponed loading. Format-specific +layers, such as `BRepGraphODE_LayerDeferred`, register representation records against graph items and let +the lock layer own those items until the provider loads or unregisters the deferred representation. Typical workflow: ```cpp BRepGraph aGraph; -aGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerParam()); -aGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerRegularity()); +aGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerTopoSupplement()); -const occ::handle aParamLayer = - aGraph.LayerRegistry().FindLayer(); +double aParameter = 0.0; +if (BRepGraphAlgo_Parameters::AddCachedPointOnCurve(aGraph, aVertexId, anEdgeId, aParameter)) +{ + // use aParameter +} ``` -### TransientCache (`BRepGraph_TransientCache`) and RefTransientCache (`BRepGraph_RefTransientCache`) +`BRepGraph_LayerTopoSupplement` is the runtime-only preservation layer for +supplemental `TopoDS` topology that should survive live +`TopoDS -> Graph -> TopoDS` reconstruction without becoming part of persisted +core topology. -Centralized per-node (TransientCache) and per-reference (RefTransientCache) caches for algorithm-computed attributes. Dense graph-local storage keyed by registered cache-kind descriptors with O(1) slot access. NOT a Layer - cleared on BRepGraph_Builder::Perform() and Compact(). +Supported supplement owners are currently `Vertex`, `Edge`, `Face`, and +`Solid`. Shell-owned supplement is intentionally unsupported because live +`TopoDS_Shell` reconstruction does not accept non-face children. + +### Cache Registry (`BRepGraph_CacheRegistry`) + +Graph-local registry for algorithm-computed transient services. Cache families are registered by GUID; concrete cache services own their own typed storage and validate freshness lazily against graph generation counters. - **Purpose**: ephemeral computed caches (bounding boxes, UV bounds, FClass2d results) -- **Identity**: cache families are described by `BRepGraph_CacheKind` with stable `Standard_GUID` identity -- **Storage**: dense `NCollection_DynamicArray` per cache kind, then per node kind, then per entity index -- **Granularity**: one cached value per `(node, cache kind)` -- **Freshness**: SubtreeGen-validated for nodes (each slot stores `StoredSubtreeGen`); OwnGen-validated for refs (refs have no subtree). On read, if the stored generation differs from the entity's current generation, the attribute is marked dirty and recomputed lazily. -- **Thread safety**: `shared_mutex` (concurrent reads from `OSD_Parallel::For`, exclusive writes) -- **Survives mutations**: yes (stale entries detected by SubtreeGen mismatch) +- **Identity**: cache families are described by `BRepGraph_Cache` with stable `Standard_GUID` identity +- **Storage**: owned by each concrete cache service; hot caches may use dense vectors, sparse caches may use maps +- **Granularity**: defined by the service (for example, bbox stores node-local entries while exposing ref-aware read helpers) +- **Freshness**: node entries usually store OwnGen or SubtreeGen; ref entries store OwnGen; layer-derived entries store source layer revision +- **Thread safety**: service-local policy; hot shared services use `shared_mutex` for concurrent reads and exclusive writes +- **Survives mutations**: yes when stale entries can be rejected by generation/revision checks; explicit `ClearAll()` drops representations while keeping services ### When to Use Which - Data that must persist and migrate across graph mutations -> **Layer** -- Computed values that can be recomputed from entity state -> **TransientCache** (prefer `CacheView` / `Cache()` in public code) +- Computed values that can be recomputed from entity state -> **Cache Registry** (prefer `CacheRegistry` / `Cache()` in public code) ### Persistence Boundary - Persist the graph model: topology / assembly defs, refs, reps, UID / RefUID vectors, direct mutation freshness (`OwnGen`), and explicitly persistent layer data. -- Do **not** persist runtime acceleration state: `TransientCache`, reconstructed shape cache, reverse indices, lazy UID lookup maps, or deferred-mutation bookkeeping. +- Do **not** persist runtime acceleration state: cache registry values, reconstructed shape cache, derived relation tables, lazy UID lookup maps, or deferred-mutation bookkeeping. - Use `UID` / `RefUID` as persistence anchors and `NodeId` / `RefId` as runtime addresses. -- Keep occurrence-context metadata resolution out of the core storage model; add it later through `PathView` helpers or layer-side resolvers once DE layers exist. +- Keep occurrence-context metadata resolution out of the core storage model; resolve it through explorer usage paths or layer-side resolvers. ## Mutation Tracking and Change Propagation @@ -376,7 +406,7 @@ Every entity (`BaseDef`) carries two generation counters: | Counter | Incremented when | Used for | |---------|-----------------|----------| | **OwnGen** | Entity's own definition fields change (tolerance, point, flags, edge list, surface, etc.) | VersionStamp persistent identity; PLM staleness detection | -| **SubtreeGen** | Entity's own data OR any descendant's data changes | TransientCache freshness; shape cache validation | +| **SubtreeGen** | Entity's own data OR any descendant's data changes | cache registry freshness; shape cache validation | `BaseRef` and `BaseRep` carry only `OwnGen` (no subtree). @@ -384,7 +414,7 @@ Every entity (`BaseDef`) carries two generation counters: When an entity is directly mutated via `MutGuard`: 1. `++OwnGen; ++SubtreeGen` on the mutated entity -2. `propagateSubtreeGen()` walks upward via reverse indices (Edge->Wire->Face->Shell->Solid) +2. `propagateSubtreeGen()` walks upward via relation tables (Vertex->Edge->Wire->Face->Shell->Solid) 3. Each parent gets `++SubtreeGen` only (NOT OwnGen - parent's own data didn't change) 4. Diamond guard (`LastPropWave`) prevents exponential blowup on shared parents @@ -392,7 +422,7 @@ Propagation is **mutex-free** - no locks, no shape cache clears, no layer dispat ### Deferred Mode -`BRepGraph_DeferredScope` wraps batch mutations (sewing, SameParameter, compact loops): +`BRepGraph_DeferredScope` wraps batch mutations (sewing, SameParameter enforcement, compact loops): - During scope: `markModified()` appends to deferred list, no propagation - At scope exit: BFS upward propagation of SubtreeGen, batch layer dispatch - Deferred mode batches invalidation only; concurrent `Mut*()` calls still require external synchronization @@ -400,7 +430,7 @@ Propagation is **mutex-free** - no locks, no shape cache clears, no layer dispat ### Shape Cache -Reconstructed shapes are cached in `BRepGraph_Data::myCurrentShapes` as `CachedShape{Shape, StoredSubtreeGen}`. Validated lazily on read - if `StoredSubtreeGen != entity.SubtreeGen`, the shape is stale and reconstructed. +Reconstructed shapes are cached in `BRepGraphInc_Storage::myCurrentShapes` as `CachedShape{Shape, StoredSubtreeGen}`. Validated lazily on read - if `StoredSubtreeGen != entity.SubtreeGen`, the shape is stale and reconstructed. ### Persistent Identity (VersionStamp) @@ -412,18 +442,19 @@ Primary mutation entry points are exposed via `Editor()` and scoped RAII guards Common operations: SplitEdge, ReplaceEdgeInWire, AddPCurveToEdge, relation-edge add/remove. -History records lineage for downstream attribute transfer and diagnostics. Supports allocator propagation via `SetAllocator()`. +History records lineage for downstream attribute transfer and diagnostics. Each layer owns its internal memory strategy independently. ## Memory Model -BRepGraph uses a single `NCollection_IncAllocator` (bump-pointer allocator) for all internal containers: +BRepGraph uses storage-local containers for its incidence tables, relation +tables, UID vectors, and caches: -- All DataMaps in `BRepGraph_Data` -- All `BRepGraphInc_Storage` entity tables and UID vectors -- All `BRepGraphInc_ReverseIndex` inner vectors -- `BRepGraph_History` containers and inner vectors +- `BRepGraphInc_Storage` entity tables, relation tables, and UID vectors +- UID reverse lookup maps -Benefits: O(1) allocation (bump-pointer), O(1) destruction (bulk page release). The allocator can be provided externally via `BRepGraph::SetAllocator()`. +Layers manage their own internal allocators and containers. + +Benefits: O(1) allocation (bump-pointer), O(1) destruction (bulk page release) for storage-owned graph data, without coupling layer lifetime to graph-owned allocator state. ## Threading Model @@ -435,28 +466,33 @@ Benefits: O(1) allocation (bump-pointer), O(1) destruction (bulk page release). ## Build Options -`BRepGraph_Builder::Perform(graph, theShape, theParallel)` uses default `BRepGraph_Builder::BuildOptions`. -Use the explicit overload when the caller needs to override extraction passes or graph-level import policy. +`graph.Shapes().Add(theShape)` uses default `BRepGraph::ShapesView::Options`. +Use the explicit overload when the caller needs to override graph-level import policy. -`BRepGraph_Builder::BuildOptions`: +`BRepGraph::ShapesView::Options`: -- `Populate.ExtractRegularities` (default true): edge continuity across face pairs. -- `Populate.ExtractVertexPointReps` (default true): vertex parameter representations on curves/surfaces. - `CreateAutoProduct` (default true): auto-create a root Product wrapping the top-level topology node. Set to false when a higher-level builder (e.g. XCAF) manages Products itself. ### Incremental Append -`Editor().AppendFlattenedShape(shape)` appends faces without container nodes. `Editor().AppendFullShape(shape)` preserves the full hierarchy (Solid/Shell/Compound/CompSolid). Both accept `BRepGraphInc_Populate::Options` for backend extraction passes only. `BRepGraph_Builder::AppendFull()` is the lower-level static API. +`graph.Shapes().Add(shape, options)` appends shapes into the graph. `Options::Flatten` appends faces without container nodes; the default preserves the full hierarchy (Solid/Shell/Compound/CompSolid). ## Debug Validation -`BRepGraphInc_ReverseIndex::Validate()` checks all reverse index maps against forward entity refs. Called automatically via `Standard_ASSERT_VOID` after SplitEdge and ReplaceEdgeInWire in debug builds. +`ValidateRelations()` checks relation tables, sparse incoming maps, direct +coedge endpoints, and ref parent/child endpoints against each other. -`Editor().CommitMutation()` validates reverse index + active entity counts. Called at end of Sewing, Compact, Deduplicate. +`Editor().CommitMutation()` validates relations and active entity counts. Called +at the end of Sewing, Compact, Deduplicate, and manual batch edits. ### Validation Pipeline -`BRepGraph_Validate` checks structural graph invariants, not geometric validity. Use `Mode::Lightweight` only for cheap boundary checks on graphs whose structure is created or mutated exclusively by internal algorithm code with already-tested invariants. For CI, integration tests, and production API boundaries, prefer `Mode::Audit`, which adds cross-reference, reverse-index, UID, and assembly-cycle checks. +`BRepGraph_Validate` checks structural graph invariants, not geometric validity. +Use `Mode::Lightweight` only for cheap boundary checks on graphs whose structure +is created or mutated exclusively by internal algorithm code with already-tested +invariants. For CI, integration tests, and production API boundaries, prefer +`Mode::Audit`, which adds cross-reference, relation, UID, and assembly-cycle +checks. If the caller also needs geometric/topological validity of reconstructed shapes, run `BRepGraph_Validate` first for graph integrity, then run the shape-level validation stack separately. @@ -472,8 +508,8 @@ if (!aResult.IsValid()) ## Practical Guidance 1. Treat BRepGraph as API boundary and BRepGraphInc as implementation backend. -2. Treat view APIs (`Topo()`, `Refs()`, `Paths()`) as the stable read boundary; avoid direct access to `BRepGraph_Data` / `myIncStorage` outside designated backend maintenance code. -3. Keep reverse index updates consistent with forward ref changes. +2. Treat view APIs (`Topo()`, `Refs()`, explorers) as the stable read boundary; avoid direct access to `BRepGraph_Data` / `myIncStorage` outside designated backend maintenance code. +3. Keep relation updates consistent with forward ref changes. 4. Prefer incremental updates in mutators over full rebuilds. 5. Use profiling before adding micro-optimizations. @@ -481,16 +517,21 @@ if (!aResult.IsValid()) | Category | Files | |----------|-------| -| **Core** | `BRepGraph.hxx/.cxx`, `BRepGraph_Data.hxx`, `BRepGraph_NodeId.hxx`, `BRepGraph_UID.hxx`, `BRepGraph_RefId.hxx`, `BRepGraph_RefUID.hxx`, `BRepGraph_RepId.hxx` | -| **Views** | `BRepGraph_TopoView.hxx/.cxx`, `BRepGraph_UIDsView.hxx/.cxx`, `BRepGraph_RefsView.hxx/.cxx`, `BRepGraph_ShapesView.hxx/.cxx`, `BRepGraph_CacheView.hxx/.cxx`, `BRepGraph_EditorView.hxx/.cxx`, `BRepGraph_PathView.hxx/.cxx` | +| **Core** | `BRepGraph.hxx/.cxx`, `BRepGraph_Data.hxx`, `BRepGraph_NodeId.hxx`, `BRepGraph_UID.hxx`, `BRepGraph_RefId.hxx`, `BRepGraph_RefUID.hxx`, `BRepGraph_ItemId.hxx`, `BRepGraph_ItemUID.hxx` | +| **Views** | `BRepGraph_TopoView.hxx/.cxx`, `BRepGraph_UIDsView.hxx/.cxx`, `BRepGraph_RefsView.hxx/.cxx`, `BRepGraph_ShapesView.hxx/.cxx`, `BRepGraph_EditorView.hxx/.cxx` (+ `_Mut.cxx`, `_Setters.cxx`), `BRepGraph_MeshView.hxx/.cxx` | | **Refs** | `BRepGraph_VersionStamp.hxx/.cxx` | -| **Traversal** | `BRepGraph_ChildExplorer.hxx/.cxx`, `BRepGraph_ParentExplorer.hxx/.cxx`, `BRepGraph_RelatedIterator.hxx`, `BRepGraph_TopologyPath.hxx`, `BRepGraph_PCurveContext.hxx` | +| **Traversal** | `BRepGraph_ChildExplorer.hxx/.cxx`, `BRepGraph_ParentExplorer.hxx/.cxx`, `BRepGraph_RelatedIterator.hxx/.cxx`, `BRepGraph_Iterator.hxx`, `BRepGraph_RefsIterator.hxx`, `BRepGraph_DefsIterator.hxx`, `BRepGraph_ReverseIterator.hxx`, `BRepGraph_UsagePath.hxx/.cxx` | | **Geometry** | `BRepGraph_Tool.hxx/.cxx` | | **Mutation** | `BRepGraph_MutGuard.hxx`, `BRepGraph_DeferredScope.hxx` | -| **Layers** | `BRepGraph_Layer.hxx/.cxx`, `BRepGraph_LayerIterator.hxx`, `BRepGraph_LayerRegistry.hxx/.cxx`, `BRepGraph_LayerParam.hxx/.cxx`, `BRepGraph_LayerRegularity.hxx/.cxx` | -| **Transient Cache** | `BRepGraph_TransientCache.hxx/.cxx`, `BRepGraph_RefTransientCache.hxx/.cxx`, `BRepGraph_CacheKindIterator.hxx` | -| **History** | `BRepGraph_History.hxx/.cxx`, `BRepGraph_HistoryRecord.hxx` | -| **Build** | `BRepGraph_Builder.hxx/.cxx` | +| **Layers** | `BRepGraph_Layer.hxx/.cxx`, `BRepGraph_LayerIterator.hxx`, `BRepGraph_LayerRegistry.hxx/.cxx`, `BRepGraph_LayerLock.hxx/.cxx`, `BRepGraph_LayerDeferred.hxx/.cxx`, `BRepGraph_LayerTopoSupplement.hxx/.cxx`, `BRepGraph_LayerParametric.hxx/.cxx` | +| **Cache** | `BRepGraph_Cache.hxx/.cxx`, `BRepGraph_CacheRegistry.hxx/.cxx`, `BRepGraph_CacheIterator.hxx`, `BRepGraph_CacheMesh.hxx/.cxx`, `BRepGraph_CacheDerivedState.hxx/.cxx` | +| **History** | `BRepGraph_LayerHistory.hxx/.cxx` | +| **Copy / Transform** | `BRepGraph_Copy.hxx/.cxx`, `BRepGraph_CopyRemap.hxx/.cxx`, `BRepGraph_Transform.hxx/.cxx` | +| **Supplement** | `BRepGraph_SupplementEditor.hxx/.cxx`, `BRepGraph_SupplementIterator.hxx/.cxx` | +| **Compaction** | `BRepGraph_Compact.hxx/.cxx` | +| **Deduplication** | `BRepGraph_Deduplicate.hxx/.cxx` | +| **Validation** | `BRepGraph_Validate.hxx/.cxx` | +| **Parallel** | `BRepGraph_ParallelPolicy.hxx/.cxx` | ## Documentation Map diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_BitFlags.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_BitFlags.hxx new file mode 100644 index 0000000000..1415136aa1 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_BitFlags.hxx @@ -0,0 +1,147 @@ +// 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. + +#ifndef _BRepGraphInc_BitFlags_HeaderFile +#define _BRepGraphInc_BitFlags_HeaderFile + +#include + +#include +#include + +//! @brief Contiguous bit-vector for per-entity boolean flags. +//! +//! Stores one bit per entity index in a flat array of 64-bit blocks. +//! Provides O(1) Set/Clear/Test operations and cache-friendly sequential +//! traversal (512 flags per 64-byte cache line via eight 64-bit blocks). +//! +//! Used by BRepGraphInc_Storage to store IsRemoved and IsOwned flags +//! outside the entity structs, improving cache locality during traversal +//! and reducing struct size by eliminating bool-field padding. +//! +//! Public helpers in BRepGraphInc_Storage validate indices before reaching this +//! low-level container. Set, Clear, and Test remain unchecked for hot internal +//! paths that already proved the index is in range. +//! +//! @code +//! BRepGraphInc_BitFlags aFlags; +//! aFlags.Resize(1000); +//! aFlags.Set(42); +//! if (aFlags.Test(42)) { ... } +//! aFlags.Clear(42); +//! @endcode +class BRepGraphInc_BitFlags +{ + static constexpr uint32_t THE_BITS_PER_BLOCK = 64; + using BlockType = uint64_t; + +public: + //! Construct an empty bit-vector. + BRepGraphInc_BitFlags() = default; + + //! Resize the bit-vector to hold at least theCount bits. + //! Newly added bits are initialized to false. + void Resize(const size_t theCount) + { + const size_t aBlockCount = (theCount + THE_BITS_PER_BLOCK - 1) / THE_BITS_PER_BLOCK; + myBlocks.Resize(aBlockCount, 0); + myBitCount = theCount; + maskTailBits(); + } + + //! Set the bit at theIndex to true. + void Set(const uint32_t theIndex) + { + const size_t aBlock = theIndex / THE_BITS_PER_BLOCK; + const uint32_t aBit = theIndex % THE_BITS_PER_BLOCK; + myBlocks[aBlock] |= (BlockType(1) << aBit); + } + + //! Clear the bit at theIndex to false. + void Clear(const uint32_t theIndex) + { + const size_t aBlock = theIndex / THE_BITS_PER_BLOCK; + const uint32_t aBit = theIndex % THE_BITS_PER_BLOCK; + myBlocks[aBlock] &= ~(BlockType(1) << aBit); + } + + //! Return the value of the bit at theIndex. + [[nodiscard]] bool Test(const uint32_t theIndex) const + { + const size_t aBlock = theIndex / THE_BITS_PER_BLOCK; + const uint32_t aBit = theIndex % THE_BITS_PER_BLOCK; + return (myBlocks[aBlock] & (BlockType(1) << aBit)) != 0; + } + + //! Set all bits to true. + void SetAll() + { + for (size_t i = 0; i < myBlocks.Size(); ++i) + { + myBlocks[i] = ~BlockType(0); + } + maskTailBits(); + } + + //! Clear all bits to false. + void ClearAll() + { + for (size_t i = 0; i < myBlocks.Size(); ++i) + { + myBlocks[i] = 0; + } + } + + //! Return true if any bit is set. + [[nodiscard]] bool HasAnyBitSet() const + { + for (size_t i = 0; i < myBlocks.Size(); ++i) + { + if (myBlocks[i] != 0) + { + return true; + } + } + return false; + } + + //! Return the number of blocks allocated. + [[nodiscard]] size_t NbBlocks() const { return myBlocks.Size(); } + + //! Return the number of valid bits represented by this vector. + [[nodiscard]] size_t BitCount() const { return myBitCount; } + + //! Return true if theIndex is inside the valid bit range. + [[nodiscard]] bool IsValidIndex(const uint32_t theIndex) const { return theIndex < myBitCount; } + + //! Return the raw block array for direct iteration. + [[nodiscard]] const BlockType* Blocks() const { return myBlocks.Data(); } + +private: + void maskTailBits() + { + const uint32_t aTailBits = static_cast(myBitCount % THE_BITS_PER_BLOCK); + if (aTailBits == 0u || myBlocks.Size() == 0) + { + return; + } + + const BlockType aTailMask = (BlockType(1) << aTailBits) - BlockType(1); + myBlocks[myBlocks.Size() - 1] &= aTailMask; + } + + NCollection_LinearVector myBlocks; + size_t myBitCount = 0; +}; + +#endif // _BRepGraphInc_BitFlags_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_BoundaryBuilder.pxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_BoundaryBuilder.pxx new file mode 100644 index 0000000000..81a21261dc --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_BoundaryBuilder.pxx @@ -0,0 +1,288 @@ +// 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. + +#ifndef _BRepGraphInc_BoundaryBuilder_HeaderFile +#define _BRepGraphInc_BoundaryBuilder_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace BRepGraphInc_BoundaryBuilder +{ + +struct SurfaceBoundaryVertex +{ + gp_Pnt Point; +}; + +struct SurfaceBoundaryEdge +{ + size_t StartVertex = 0; + size_t EndVertex = 0; + occ::handle Curve3D; + double First = 0.0; + double Last = 0.0; + bool IsDegenerate = false; +}; + +struct SurfaceBoundaryCoEdge +{ + size_t EdgeIndex = 0; + TopAbs_Orientation Orientation = TopAbs_FORWARD; + occ::handle PCurve; + double First = 0.0; + double Last = 0.0; +}; + +struct SurfaceBoundary +{ + NCollection_LinearVector Vertices; + NCollection_LinearVector Edges; + NCollection_LinearVector CoEdges; +}; + +inline bool isFullPeriod(const occ::handle& theSurface, + const bool theUPeriod, + const double theFirst, + const double theLast, + const double theTolerance) +{ + if (theSurface.IsNull()) + { + return false; + } + if (theUPeriod && !theSurface->IsUPeriodic()) + { + return false; + } + if (!theUPeriod && !theSurface->IsVPeriodic()) + { + return false; + } + + const double aPeriod = theUPeriod ? theSurface->UPeriod() : theSurface->VPeriod(); + return std::abs(std::abs(theLast - theFirst) - aPeriod) <= theTolerance; +} + +inline size_t addSurfaceVertex(SurfaceBoundary& theBoundary, + const gp_Pnt& thePoint, + const double theTolerance) +{ + const double aTol2 = theTolerance * theTolerance; + for (size_t anIdx = 0; anIdx < theBoundary.Vertices.Size(); ++anIdx) + { + if (theBoundary.Vertices.Value(anIdx).Point.SquareDistance(thePoint) <= aTol2) + { + return anIdx; + } + } + + SurfaceBoundaryVertex& aVertex = theBoundary.Vertices.Appended(); + aVertex.Point = thePoint; + return theBoundary.Vertices.Size() - 1; +} + +inline bool isDegenerateIsoCurve(const occ::handle& theCurve, const double theTolerance) +{ + if (theCurve.IsNull()) + { + return false; + } + + GeomAdaptor_Curve anAdaptor(theCurve); + return anAdaptor.GetType() == GeomAbs_Circle && anAdaptor.Circle().Radius() <= theTolerance; +} + +inline size_t addSurfaceEdge(SurfaceBoundary& theBoundary, + const size_t theStartVertex, + const size_t theEndVertex, + const occ::handle& theCurve, + const double theFirst, + const double theLast, + const bool theIsDegenerate) +{ + SurfaceBoundaryEdge& anEdge = theBoundary.Edges.Appended(); + anEdge.StartVertex = theStartVertex; + anEdge.EndVertex = theEndVertex; + anEdge.Curve3D = theCurve; + anEdge.First = theFirst; + anEdge.Last = theLast; + anEdge.IsDegenerate = theIsDegenerate; + return theBoundary.Edges.Size() - 1; +} + +inline void addSurfaceCoEdge(SurfaceBoundary& theBoundary, + const size_t theEdgeIndex, + const TopAbs_Orientation theOrientation, + const occ::handle& thePCurve, + const double theFirst, + const double theLast) +{ + SurfaceBoundaryCoEdge& aCoEdge = theBoundary.CoEdges.Appended(); + aCoEdge.EdgeIndex = theEdgeIndex; + aCoEdge.Orientation = theOrientation; + aCoEdge.PCurve = thePCurve; + aCoEdge.First = theFirst; + aCoEdge.Last = theLast; +} + +inline bool BuildSurfaceBoundary(SurfaceBoundary& theBoundary, + const occ::handle& theSurface, + const double theUMin, + const double theUMax, + const double theVMin, + const double theVMax, + const double theTolerance) +{ + if (theSurface.IsNull()) + { + return false; + } + + theBoundary.Vertices.Clear(); + theBoundary.Edges.Clear(); + theBoundary.CoEdges.Clear(); + + const double aTol = std::max(theTolerance, Precision::Confusion()); + const double aPeriodTol = Precision::PConfusion(); + + const bool isUClosed = isFullPeriod(theSurface, true, theUMin, theUMax, aPeriodTol); + const bool isVClosed = isFullPeriod(theSurface, false, theVMin, theVMax, aPeriodTol); + + size_t aV00 = addSurfaceVertex(theBoundary, theSurface->Value(theUMin, theVMin), aTol); + size_t aV10 = addSurfaceVertex(theBoundary, theSurface->Value(theUMax, theVMin), aTol); + size_t aV11 = addSurfaceVertex(theBoundary, theSurface->Value(theUMax, theVMax), aTol); + size_t aV01 = addSurfaceVertex(theBoundary, theSurface->Value(theUMin, theVMax), aTol); + + if (isUClosed) + { + aV10 = aV00; + aV11 = aV01; + } + if (isVClosed) + { + aV01 = aV00; + aV11 = aV10; + } + + const occ::handle aBottomCurve = theSurface->VIso(theVMin); + const occ::handle aRightCurve = theSurface->UIso(theUMax); + const occ::handle aTopCurve = theSurface->VIso(theVMax); + const occ::handle aLeftCurve = theSurface->UIso(theUMin); + + const bool isBottomDegenerate = isDegenerateIsoCurve(aBottomCurve, aTol); + const bool isRightDegenerate = isDegenerateIsoCurve(aRightCurve, aTol); + const bool isTopDegenerate = isDegenerateIsoCurve(aTopCurve, aTol); + const bool isLeftDegenerate = isDegenerateIsoCurve(aLeftCurve, aTol); + + const size_t aBottomEdge = + addSurfaceEdge(theBoundary, aV00, aV10, aBottomCurve, theUMin, theUMax, isBottomDegenerate); + size_t aRightEdge = 0; + if (!isUClosed) + { + aRightEdge = + addSurfaceEdge(theBoundary, aV10, aV11, aRightCurve, theVMin, theVMax, isRightDegenerate); + } + size_t aTopEdge = 0; + if (!isVClosed) + { + aTopEdge = + addSurfaceEdge(theBoundary, aV01, aV11, aTopCurve, theUMin, theUMax, isTopDegenerate); + } + const size_t aLeftEdge = + addSurfaceEdge(theBoundary, aV00, aV01, aLeftCurve, theVMin, theVMax, isLeftDegenerate); + + addSurfaceCoEdge(theBoundary, + aBottomEdge, + TopAbs_FORWARD, + new Geom2d_Line(gp_Pnt2d(theUMin, theVMin), gp_Dir2d(1.0, 0.0)), + theUMin, + theUMax); + + if (!isUClosed) + { + addSurfaceCoEdge(theBoundary, + aRightEdge, + TopAbs_FORWARD, + new Geom2d_Line(gp_Pnt2d(theUMax, theVMin), gp_Dir2d(0.0, 1.0)), + theVMin, + theVMax); + } + else + { + addSurfaceCoEdge(theBoundary, + aLeftEdge, + TopAbs_FORWARD, + new Geom2d_Line(gp_Pnt2d(theUMin, theVMin), gp_Dir2d(0.0, 1.0)), + theVMin, + theVMax); + } + + if (!isVClosed) + { + addSurfaceCoEdge(theBoundary, + aTopEdge, + TopAbs_REVERSED, + new Geom2d_Line(gp_Pnt2d(theUMin, theVMax), gp_Dir2d(1.0, 0.0)), + theUMin, + theUMax); + } + else + { + addSurfaceCoEdge(theBoundary, + aBottomEdge, + TopAbs_REVERSED, + new Geom2d_Line(gp_Pnt2d(theUMin, theVMax), gp_Dir2d(1.0, 0.0)), + theUMin, + theUMax); + } + + if (!isUClosed) + { + addSurfaceCoEdge(theBoundary, + aLeftEdge, + TopAbs_REVERSED, + new Geom2d_Line(gp_Pnt2d(theUMin, theVMin), gp_Dir2d(0.0, 1.0)), + theVMin, + theVMax); + } + else + { + addSurfaceCoEdge(theBoundary, + aLeftEdge, + TopAbs_REVERSED, + new Geom2d_Line(gp_Pnt2d(theUMax, theVMin), gp_Dir2d(0.0, 1.0)), + theVMin, + theVMax); + } + + return !theBoundary.Vertices.IsEmpty() && !theBoundary.Edges.IsEmpty() + && !theBoundary.CoEdges.IsEmpty(); +} + +} // namespace BRepGraphInc_BoundaryBuilder + +#endif // _BRepGraphInc_BoundaryBuilder_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx index cffcc67a4a..62f8156834 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Definition.hxx @@ -15,41 +15,32 @@ #define _BRepGraphInc_Definition_HeaderFile #include +#include +#include #include -#include #include - #include -#include #include #include -#include -#include - //! @brief Definition structs for the incidence-table topology model. //! -//! Each definition holds intrinsic geometry properties plus forward-direction -//! children (via RefId indices). The incidence model stores topology as flat -//! vectors of definitions (one per kind) with integer cross-references, -//! enabling cache-friendly traversal and parallel geometry extraction. +//! Each definition holds intrinsic geometry properties. Ordered child +//! incidence lives in BRepGraphInc_Relations and reusable parent/child usage +//! edges live in reference records. namespace BRepGraphInc { -//! Helper: reinitialize a vector member with the given allocator and block size. -template -inline void InitVec(NCollection_DynamicArray& theVec, - const occ::handle& theAlloc, - const int theBlockSize = 4) -{ - theVec = NCollection_DynamicArray(theBlockSize, theAlloc); -} - //! Fields shared by every entity. struct BaseDef { using TypeId = BRepGraph_NodeId; + //! Persistent per-kind UID counter value. + //! 0 = invalid sentinel (not yet allocated). Valid UIDs start at 1. + //! Kind is implicit from the concrete struct type (VertexDef, EdgeDef, etc.). + uint32_t UID = 0; + //! Own-data mutation counter, incremented ONLY when the entity's own //! definition fields change (tolerance, point, flags, etc.). //! NOT incremented by descendant changes. @@ -64,10 +55,8 @@ struct BaseDef //! Wave counter from the last propagation that visited this node. //! Used as a re-visit guard in markParentSubtreeGen() to prevent //! exponential blowup on diamond topologies. Compared against - //! BRepGraph_Data::myPropagationWave. + //! BRepGraphInc_Storage::myPropagationWave. uint32_t LastPropWave = 0; - - bool IsRemoved = false; //!< Soft-removal flag }; //! Vertex definition: 3D point + tolerance. @@ -80,55 +69,24 @@ struct VertexDef : public BaseDef //! Tolerance from BRep_TVertex. double Tolerance = 0.0; - - void InitVectors(const occ::handle&) {} }; -//! Edge entity: parameter range, boundary vertices, flags. -//! Geometry (curve, polygon) accessed via rep indices into Storage vectors. +//! Edge entity: parameter range, boundary vertices. +//! Geometry (curve, polygon) accessed via owned use records. +//! Degeneracy, closure, SameRange, and SameParameter are derived from +//! current topology and geometry via BRepGraph_CacheDerivedState. struct EdgeDef : public BaseDef { using TypeId = BRepGraph_EdgeId; - //! Typed representation id into Storage::myCurves3D (invalid for degenerate edges). - BRepGraph_Curve3DRepId Curve3DRepId; + BRepGraph_EdgeCurve3DRepId Curve3DRepId; //!< Owned 3D curve use id (invalid for degenerate edges) - //! Curve parameter range. - double ParamFirst = 0.0; - double ParamLast = 0.0; + double Tolerance = 0.0; //!< Tolerance from BRep_TEdge - //! Tolerance from BRep_TEdge. - double Tolerance = 0.0; + BRepGraph_VertexRefId StartVertexRefId; //!< Start vertex reference + BRepGraph_VertexRefId EndVertexRefId; //!< End vertex reference - //! True if this edge collapses to a point on the surface. - bool IsDegenerate = false; - - //! True if all PCurves are reparametrized to the same range as the 3D curve. - bool SameParameter = false; - - //! True if the PCurve parameter range equals the 3D curve parameter range. - bool SameRange = false; - - //! True if StartVertex == EndVertex (topological loop, e.g. circle edge). - bool IsClosed = false; - - //! Boundary vertex reference ids (indices into VertexRef table). - //! For closed edges, the start and end ref entries point to the same VertexDefId. - BRepGraph_VertexRefId StartVertexRefId; - BRepGraph_VertexRefId EndVertexRefId; - - //! Additional vertex reference ids with INTERNAL or EXTERNAL orientation. - //! Edges with only FORWARD/REVERSED boundary vertices leave this empty. - NCollection_DynamicArray InternalVertexRefIds; - - //! Typed representation id into Storage::myPolygons3D (invalid if no polygon). - BRepGraph_Polygon3DRepId Polygon3DRepId; - - //! Reinitialize inner vectors with the given allocator. - void InitVectors(const occ::handle& theAlloc) - { - InitVec(InternalVertexRefIds, theAlloc, 2); // typically 0 - } + BRepGraph_EdgePolygon3DRepId Polygon3DRepId; //!< Owned 3D polygon use id }; //! CoEdge entity: use of an edge on a specific face, owns PCurve data. @@ -141,38 +99,21 @@ struct CoEdgeDef : public BaseDef { using TypeId = BRepGraph_CoEdgeId; - BRepGraph_EdgeId EdgeDefId; //!< Parent edge definition id - BRepGraph_FaceId FaceDefId; //!< Face this coedge belongs to (invalid for free wires) - TopAbs_Orientation Orientation = TopAbs_FORWARD; //!< Orientation relative to parent edge + BRepGraph_WireId ParentWireId; //!< Ordered owner wire + BRepGraph_EdgeId ChildEdgeId; //!< Connected reusable edge definition + BRepGraph_FaceId FaceId; //!< Face this coedge belongs to (invalid for free wires) + ParityOrientation Orientation = TopAbs_FORWARD; //!< Orientation relative to parent edge - //! Typed representation id into Storage::myCurves2D (invalid for free-wire coedges). - BRepGraph_Curve2DRepId Curve2DRepId; - double ParamFirst = 0.0; - double ParamLast = 0.0; - gp_Pnt2d UV1; //!< UV at ParamFirst - gp_Pnt2d UV2; //!< UV at ParamLast - - //! Typed representation id into Storage::myPolygons2D (invalid if no polygon-on-surface). - BRepGraph_Polygon2DRepId Polygon2DRepId; - - //! Typed representation id into Storage::myPolygonsOnTri (persistent/imported). - BRepGraph_PolygonOnTriRepId PolygonOnTriRepId; - - void InitVectors(const occ::handle&) {} + BRepGraph_CoEdgeCurve2DRepId Curve2DRepId; //!< Owned 2D curve use id + BRepGraph_CoEdgePolygon2DRepId Polygon2DRepId; //!< Owned 2D polygon use id + BRepGraph_CoEdgePolygonOnTriRepId PolygonOnTriRepId; //!< Owned polygon-on-triangulation use id }; -//! Wire entity: ordered coedge references with closure flag. +//! Wire entity: ordered coedge sequence. +//! Wire closure is derived from the ordered coedge chain via BRepGraph_CacheDerivedState. struct WireDef : public BaseDef { using TypeId = BRepGraph_WireId; - - bool IsClosed = false; - NCollection_DynamicArray CoEdgeRefIds; //!< Ordered coedge ref indices - - void InitVectors(const occ::handle& theAlloc) - { - InitVec(CoEdgeRefIds, theAlloc, 8); // typically 3-8 coedges per wire - } }; //! Face entity: surface, triangulations, wires. @@ -180,119 +121,58 @@ struct FaceDef : public BaseDef { using TypeId = BRepGraph_FaceId; - BRepGraph_SurfaceRepId SurfaceRepId; //!< Typed id into mySurfaces - BRepGraph_TriangulationRepId - TriangulationRepId; //!< Typed id into myTriangulations (persistent/imported) + BRepGraph_FaceSurfaceRepId SurfaceRepId; //!< Owned surface use id + BRepGraph_FaceTriangulationRepId + TriangulationRepId; //!< Owned triangulation use id (persistent/imported) - double Tolerance = 0.0; - bool NaturalRestriction = false; - - NCollection_DynamicArray WireRefIds; //!< Wire ref indices (outer first) - - //! Direct INTERNAL/EXTERNAL vertex children (not inside wires). - //! Boundary vertices are normally reached through WireRefIds -> CoEdgeRefIds - //! -> CoEdgeDef.EdgeDefId -> EdgeDef.{StartVertexRefId, EndVertexRefId}. - //! This vector is for additional direct face-owned vertex usage. - NCollection_DynamicArray VertexRefIds; - - void InitVectors(const occ::handle& theAlloc) - { - InitVec(WireRefIds, theAlloc, 2); // typically 1-2 (outer + holes) - InitVec(VertexRefIds, theAlloc, 2); // typically 0 - } + double Tolerance = 0.0; //!< Face tolerance }; -//! Shell entity: ordered face references with local locations. +//! Shell entity. +//! Shell closure is derived from face-boundary edge incidence via BRepGraph_CacheDerivedState. struct ShellDef : public BaseDef { using TypeId = BRepGraph_ShellId; - - bool IsClosed = false; //!< True if shell forms a watertight (closed) boundary. - NCollection_DynamicArray FaceRefIds; //!< Face ref indices - NCollection_DynamicArray - AuxChildRefIds; //!< Non-face children (wires, edges) - - void InitVectors(const occ::handle& theAlloc) - { - InitVec(FaceRefIds, theAlloc, 8); // typically 4-8 faces per shell - InitVec(AuxChildRefIds, theAlloc, 2); // typically 0 - } }; -//! Solid entity: ordered shell references with local locations. +//! Solid entity. struct SolidDef : public BaseDef { using TypeId = BRepGraph_SolidId; - - NCollection_DynamicArray ShellRefIds; //!< Shell ref indices - NCollection_DynamicArray - AuxChildRefIds; //!< Non-shell children (edges, vertices) - - void InitVectors(const occ::handle& theAlloc) - { - InitVec(ShellRefIds, theAlloc, 2); // typically 1 - InitVec(AuxChildRefIds, theAlloc, 2); // typically 0 - } }; -//! Compound entity: heterogeneous child references. +//! Compound entity. struct CompoundDef : public BaseDef { using TypeId = BRepGraph_CompoundId; - - NCollection_DynamicArray ChildRefIds; //!< Child ref indices - - void InitVectors(const occ::handle& theAlloc) - { - InitVec(ChildRefIds, theAlloc, 4); - } }; -//! Comp-solid entity: ordered solid references. +//! Comp-solid entity. struct CompSolidDef : public BaseDef { using TypeId = BRepGraph_CompSolidId; - - NCollection_DynamicArray SolidRefIds; //!< Solid ref indices - - void InitVectors(const occ::handle& theAlloc) - { - InitVec(SolidRefIds, theAlloc, 2); - } }; //! Product entity: reusable shape definition (part or assembly). -//! Children are managed uniformly via OccurrenceRefIds: -//! - A part product has one occurrence whose ChildDefId is a topology root node. -//! - An assembly product has occurrences whose ChildDefId values are other products. +//! Children are managed uniformly via ProductRelations::OccurrenceRefIds: +//! - A part product has one occurrence whose ChildNodeId is a topology root node. +//! - An assembly product has occurrences whose ChildNodeId values are other products. //! Products carry no location or orientation - those live on references. struct ProductDef : public BaseDef { using TypeId = BRepGraph_ProductId; - - NCollection_DynamicArray - OccurrenceRefIds; //!< All children (shape roots and sub-products) - - void InitVectors(const occ::handle& theAlloc) - { - InitVec(OccurrenceRefIds, theAlloc, 4); - } }; //! Occurrence entity: reference to a child node (topology root or product). -//! The parent product is determined from OccurrenceRef::ParentId (BaseRef). +//! Parent products are determined from ProductRelations owner arrays. //! Placement lives on OccurrenceRef::LocalLocation (definitions never carry location). -//! Path-based traversal (PathView::ForEachPathTo) resolves DAG paths without -//! stored parent-occurrence pointers. +//! Path-based traversal (BRepGraph_UsagePath) resolves DAG paths without stored +//! parent-occurrence pointers. struct OccurrenceDef : public BaseDef { using TypeId = BRepGraph_OccurrenceId; - BRepGraph_NodeId ChildDefId; //!< Referenced child node (topology root or product) - - //! No-op: OccurrenceDef has no inner vectors to reinitialize. - //! Present for uniform DefStore::Append() logic. - void InitVectors(const occ::handle&) {} + BRepGraph_NodeId ChildNodeId; //!< Referenced child node (topology root or product) }; } // namespace BRepGraphInc diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Instance.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Instance.hxx index 05e6dcecdf..4a9485866d 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Instance.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Instance.hxx @@ -48,10 +48,14 @@ struct Instance TypedIdT DefId; TopLoc_Location Location; TopAbs_Orientation Orientation = TopAbs_FORWARD; + + //! Returns true if the instance references an existing definition id. + [[nodiscard]] bool IsValid() const { return DefId.IsValid(); } }; using VertexInstance = Instance; using CoEdgeInstance = Instance; +using WireInstance = Instance; using FaceInstance = Instance; using ShellInstance = Instance; using SolidInstance = Instance; @@ -67,12 +71,6 @@ using ProductInstance = Instance; //! implicit conversion to BRepGraph_NodeId. using NodeInstance = Instance; -//! Wire instance with an additional flag indicating whether this is the outer wire. -struct WireInstance : Instance -{ - bool IsOuter = false; -}; - } // namespace BRepGraphInc //! std::hash specialization for BRepGraphInc::Instance. diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Load.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Load.hxx new file mode 100644 index 0000000000..129942dee8 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Load.hxx @@ -0,0 +1,56 @@ +// 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. + +#ifndef _BRepGraphInc_Load_HeaderFile +#define _BRepGraphInc_Load_HeaderFile + +#include + +//! Internal storage-side types for fixed-size indexed load preparation. +namespace BRepGraphInc_Load +{ + +//! Final section counts needed to prepare `BRepGraphInc_Storage` for indexed load. +struct Counts +{ + uint32_t NbVertices = 0; //!< Number of `VertexDef` slots. + uint32_t NbEdges = 0; //!< Number of `EdgeDef` slots. + uint32_t NbCoEdges = 0; //!< Number of `CoEdgeDef` slots. + uint32_t NbWires = 0; //!< Number of `WireDef` slots. + uint32_t NbFaces = 0; //!< Number of `FaceDef` slots. + uint32_t NbShells = 0; //!< Number of `ShellDef` slots. + uint32_t NbSolids = 0; //!< Number of `SolidDef` slots. + uint32_t NbCompounds = 0; //!< Number of `CompoundDef` slots. + uint32_t NbCompSolids = 0; //!< Number of `CompSolidDef` slots. + uint32_t NbProducts = 0; //!< Number of `ProductDef` slots. + uint32_t NbOccurrences = 0; //!< Number of `OccurrenceDef` slots. + uint32_t NbShellRefs = 0; //!< Number of `ShellRef` slots. + uint32_t NbFaceRefs = 0; //!< Number of `FaceRef` slots. + uint32_t NbWireRefs = 0; //!< Number of `WireRef` slots. + uint32_t NbVertexRefs = 0; //!< Number of `VertexRef` slots. + uint32_t NbSolidRefs = 0; //!< Number of `SolidRef` slots. + uint32_t NbChildRefs = 0; //!< Number of `ChildRef` slots. + uint32_t NbOccurrenceRefs = 0; //!< Number of `OccurrenceRef` slots. + uint32_t NbFaceSurfaceReps = 0; //!< Number of `FaceSurfaceRep` slots. + uint32_t NbEdgeCurve3DReps = 0; //!< Number of `EdgeCurve3DRep` slots. + uint32_t NbCoEdgeCurve2DReps = 0; //!< Number of `CoEdgeCurve2DRep` slots. + uint32_t NbFaceTriangulationReps = 0; //!< Number of `FaceTriangulationRep` slots. + uint32_t NbEdgePolygon3DReps = 0; //!< Number of `EdgePolygon3DRep` slots. + uint32_t NbCoEdgePolygon2DReps = 0; //!< Number of `CoEdgePolygon2DRep` slots. + uint32_t NbCoEdgePolygonOnTriReps = 0; //!< Number of `CoEdgePolygonOnTriRep` slots. + uint32_t NbRootProducts = 0; //!< Number of root product ids outside storage tables. +}; + +} // namespace BRepGraphInc_Load + +#endif // _BRepGraphInc_Load_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ParityOrientation.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ParityOrientation.hxx new file mode 100644 index 0000000000..b8c4daa2ab --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ParityOrientation.hxx @@ -0,0 +1,64 @@ +// 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. + +#ifndef _BRepGraphInc_ParityOrientation_HeaderFile +#define _BRepGraphInc_ParityOrientation_HeaderFile + +#include +#include + +namespace BRepGraphInc +{ + +//! @brief Persisted core-topology orientation stored as forward/reversed parity only. +//! +//! The wrapper keeps storage compact through one bool while remaining implicitly +//! convertible to `TopAbs_Orientation` for existing orientation-facing codepaths. +struct ParityOrientation +{ + //! Stored parity bit: `false` for `TopAbs_FORWARD`, `true` for `TopAbs_REVERSED`. + bool IsReversed = false; + + //! Constructs the parity wrapper from a core forward/reversed orientation. + ParityOrientation() = default; + + //! Constructs the parity wrapper from a core forward/reversed orientation. + ParityOrientation(const TopAbs_Orientation theOrientation) + : IsReversed(toIsReversed(theOrientation)) + { + } + + //! Assigns a core forward/reversed orientation. + ParityOrientation& operator=(const TopAbs_Orientation theOrientation) + { + IsReversed = toIsReversed(theOrientation); + return *this; + } + + //! Converts stored parity back to `TopAbs_Orientation`. + operator TopAbs_Orientation() const { return IsReversed ? TopAbs_REVERSED : TopAbs_FORWARD; } + +private: + //! Converts a core forward/reversed orientation to the stored parity bit. + static bool toIsReversed(const TopAbs_Orientation theOrientation) + { + Standard_ProgramError_Raise_if(theOrientation != TopAbs_FORWARD + && theOrientation != TopAbs_REVERSED, + "BRepGraphInc::ParityOrientation stores only FORWARD/REVERSED"); + return theOrientation == TopAbs_REVERSED; + } +}; + +} // namespace BRepGraphInc + +#endif // _BRepGraphInc_ParityOrientation_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx index f4e675d01e..04b289b9c7 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.cxx @@ -12,22 +12,30 @@ // 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 #include @@ -44,245 +52,302 @@ #include #include +#include #include -// Population pipeline overview: -// -// Phase 1 (sequential): Recursively traverse the TopoDS hierarchy -// (Compound -> CompSolid -> Solid -> Shell), collecting face contexts -// into a flat FaceLocalData vector. Registers container entities -// (Compound, CompSolid, Solid, Shell) with TShape deduplication. -// -// Phase 2 (parallel): Per-face geometry extraction via OSD_Parallel. -// Extracts surface, triangulations, wires, edges (with PCurves, -// polygons, vertices) into ExtractedEdge/ExtractedWire/FaceLocalData -// structs. No storage writes - thread-safe read-only access to TopoDS. -// -// Phase 3 (sequential): Register extracted data into BRepGraphInc_Storage. -// Creates Face, Wire, Edge, CoEdge, Vertex entities with TShape dedup -// and representation dedup (getOrCreate*Rep). Also runs optional -// post-passes: edge regularities (3b) and vertex point reps (3c). -// -// Phase 4 (sequential): Build reverse indices for O(1) upward navigation. +#include "BRepGraphInc_BoundaryBuilder.pxx" namespace { +constexpr size_t THE_PENDING_FACE_BUCKET = 32; +constexpr size_t THE_LOCATED_NODE_BUCKET = 64; +constexpr size_t THE_WIRE_BUCKET = 4; +constexpr size_t THE_EDGE_BUCKET = 8; +constexpr size_t THE_ROOT_SET_BUCKET = 4; -//================================================================================================= - -void appendFaceRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_ShellId theParentShellId, - const BRepGraphInc::FaceInstance& theRef) +struct BuildCounts { - const BRepGraph_FaceRefId aRefId = theStorage.AppendFaceRef(); - BRepGraphInc::FaceRef& anEntry = theStorage.ChangeFaceRef(aRefId); - anEntry.ParentId = theParentShellId; - anEntry.FaceDefId = theRef.DefId; - anEntry.Orientation = theRef.Orientation; - anEntry.LocalLocation = theRef.Location; - theStorage.ChangeShell(theParentShellId).FaceRefIds.Append(aRefId); + uint32_t NbVertices = 0; + uint32_t NbEdges = 0; + uint32_t NbWires = 0; + uint32_t NbFaces = 0; + uint32_t NbShells = 0; + uint32_t NbSolids = 0; + uint32_t NbCompounds = 0; + uint32_t NbCompSolids = 0; + uint32_t NbCoEdges = 0; + uint32_t NbChildRefs = 0; + uint32_t NbSolidRefs = 0; + uint32_t NbFaceRefs = 0; + uint32_t NbShellRefs = 0; + uint32_t NbWireRefs = 0; +}; + +static void extractEdgeSupplementAttachments(BRepGraph_LayerTopoSupplement& theLayer, + const BRepGraph_NodeId theOwner, + const TopoDS_Shape& theOwnerShape) +{ + size_t aLastForwardIdx = 0; + size_t aLastReversedIdx = 0; + size_t aChildIdx = 0; + bool hasForward = false; + bool hasReversed = false; + for (TopoDS_Iterator aChildIt(theOwnerShape, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + if (aChild.ShapeType() == TopAbs_VERTEX && aChild.Orientation() == TopAbs_FORWARD) + { + aLastForwardIdx = aChildIdx; + hasForward = true; + } + else if (aChild.ShapeType() == TopAbs_VERTEX && aChild.Orientation() == TopAbs_REVERSED) + { + aLastReversedIdx = aChildIdx; + hasReversed = true; + } + ++aChildIdx; + } + + aChildIdx = 0; + for (TopoDS_Iterator aChildIt(theOwnerShape, false, false); aChildIt.More(); + aChildIt.Next(), ++aChildIdx) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + if (aChild.ShapeType() != TopAbs_VERTEX) + { + theLayer.AddAttachment(theOwner, + BRepGraph_LayerTopoSupplement::AttachmentKind::GenericSupplementShape, + aChild); + continue; + } + + const bool isBoundaryForward = + hasForward && aChild.Orientation() == TopAbs_FORWARD && aChildIdx == aLastForwardIdx; + const bool isBoundaryReversed = + hasReversed && aChild.Orientation() == TopAbs_REVERSED && aChildIdx == aLastReversedIdx; + if (!isBoundaryForward && !isBoundaryReversed) + { + theLayer.AddAttachment(theOwner, + BRepGraph_LayerTopoSupplement::AttachmentKind::EdgeInternalVertex, + aChild); + } + } +} + +static void extractOwnerSupplementAttachments(BRepGraph_LayerTopoSupplement& theLayer, + const BRepGraph_NodeId theOwner, + const TopoDS_Shape& theOwnerShape) +{ + switch (theOwner.NodeKind) + { + case BRepGraph_NodeId::Kind::Edge: + extractEdgeSupplementAttachments(theLayer, theOwner, theOwnerShape); + return; + case BRepGraph_NodeId::Kind::Face: + for (TopoDS_Iterator aChildIt(theOwnerShape, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + if (aChild.ShapeType() == TopAbs_WIRE) + { + continue; + } + + const BRepGraph_LayerTopoSupplement::AttachmentKind aKind = + aChild.ShapeType() == TopAbs_VERTEX + ? BRepGraph_LayerTopoSupplement::AttachmentKind::FaceDirectVertex + : BRepGraph_LayerTopoSupplement::AttachmentKind::GenericSupplementShape; + theLayer.AddAttachment(theOwner, aKind, aChild); + } + return; + default: + return; + } +} + +static void attachSupplement(const occ::handle& theLayer, + const BRepGraph_NodeId theOwner, + const TopoDS_Shape& theOwnerShape) +{ + if (theLayer.IsNull()) + { + return; + } + extractOwnerSupplementAttachments(*theLayer, theOwner, theOwnerShape); } //================================================================================================= -void appendWireRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_FaceId theParentFaceId, - const BRepGraphInc::WireInstance& theRef) +void appendFaceRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_ShellId theOwnerShellId, + const BRepGraph_FaceId theFaceId, + const TopAbs_Orientation theOrientation) { - const BRepGraph_WireRefId aRefId = theStorage.AppendWireRef(); - BRepGraphInc::WireRef& anEntry = theStorage.ChangeWireRef(aRefId); - anEntry.ParentId = theParentFaceId; - anEntry.WireDefId = theRef.DefId; - anEntry.IsOuter = theRef.IsOuter; - anEntry.Orientation = theRef.Orientation; - anEntry.LocalLocation = theRef.Location; - theStorage.ChangeFace(theParentFaceId).WireRefIds.Append(aRefId); + theStorage.AttachFaceToShell(theOwnerShellId, theFaceId, theOrientation); } //================================================================================================= -void appendCoEdgeRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_WireId theParentWireId, - const BRepGraphInc::CoEdgeInstance& theRef) +void appendWireRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_FaceId theOwnerFaceId, + const BRepGraph_WireId theWireId, + const TopAbs_Orientation theOrientation) { - const BRepGraph_CoEdgeRefId aRefId = theStorage.AppendCoEdgeRef(); - BRepGraphInc::CoEdgeRef& anEntry = theStorage.ChangeCoEdgeRef(aRefId); - anEntry.ParentId = theParentWireId; - anEntry.CoEdgeDefId = theRef.DefId; - anEntry.LocalLocation = theRef.Location; - theStorage.ChangeWire(theParentWireId).CoEdgeRefIds.Append(aRefId); + theStorage.AttachWireToFace(theOwnerFaceId, theWireId, theOrientation); } //================================================================================================= -BRepGraph_VertexRefId appendVertexRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParentId, - const BRepGraphInc::VertexInstance& theRef) +BRepGraph_VertexRefId appendVertexRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexId theVertexId, + const TopAbs_Orientation theOrientation, + const BRepGraph_EdgeId theParentEdgeId = BRepGraph_EdgeId()) { const BRepGraph_VertexRefId aRefId = theStorage.AppendVertexRef(); BRepGraphInc::VertexRef& anEntry = theStorage.ChangeVertexRef(aRefId); - anEntry.ParentId = theParentId; - anEntry.VertexDefId = theRef.DefId; - anEntry.Orientation = theRef.Orientation; - anEntry.LocalLocation = theRef.Location; + anEntry.ChildVertexId = theVertexId; + anEntry.ParentEdgeId = theParentEdgeId; + anEntry.Orientation = theOrientation; return aRefId; } //================================================================================================= -void appendShellRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_SolidId theParentSolidId, - const BRepGraphInc::ShellInstance& theRef) +void appendShellRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_SolidId theOwnerSolidId, + const BRepGraph_ShellId theShellId, + const TopAbs_Orientation theOrientation) { - const BRepGraph_ShellRefId aRefId = theStorage.AppendShellRef(); - BRepGraphInc::ShellRef& anEntry = theStorage.ChangeShellRef(aRefId); - anEntry.ParentId = theParentSolidId; - anEntry.ShellDefId = theRef.DefId; - anEntry.Orientation = theRef.Orientation; - anEntry.LocalLocation = theRef.Location; - theStorage.ChangeSolid(theParentSolidId).ShellRefIds.Append(aRefId); + theStorage.AttachShellToSolid(theOwnerSolidId, theShellId, theOrientation); } //================================================================================================= -void appendSolidRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_CompSolidId theParentCompSolidId, - const BRepGraphInc::SolidInstance& theRef) +void appendSolidRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_CompSolidId theOwnerCompSolidId, + const BRepGraph_SolidId theSolidId, + const TopAbs_Orientation theOrientation) { - const BRepGraph_SolidRefId aRefId = theStorage.AppendSolidRef(); - BRepGraphInc::SolidRef& anEntry = theStorage.ChangeSolidRef(aRefId); - anEntry.ParentId = theParentCompSolidId; - anEntry.SolidDefId = theRef.DefId; - anEntry.Orientation = theRef.Orientation; - anEntry.LocalLocation = theRef.Location; - theStorage.ChangeCompSolid(theParentCompSolidId).SolidRefIds.Append(aRefId); + theStorage.AttachSolidToCompSolid(theOwnerCompSolidId, theSolidId, theOrientation); } //================================================================================================= -BRepGraph_ChildRefId appendChildRef(BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParentId, - const BRepGraphInc::NodeInstance& theRef) +void appendChildRef(BRepGraphInc_Storage& theStorage, + const BRepGraph_CompoundId theOwnerCompoundId, + const BRepGraph_NodeId theChildId, + const TopAbs_Orientation theOrientation, + const TopLoc_Location& theLocation) { - const BRepGraph_ChildRefId aRefId = theStorage.AppendChildRef(); - BRepGraphInc::ChildRef& anEntry = theStorage.ChangeChildRef(aRefId); - anEntry.ParentId = theParentId; - anEntry.ChildDefId = theRef.DefId; - anEntry.Orientation = theRef.Orientation; - anEntry.LocalLocation = theRef.Location; - if (theParentId.NodeKind == BRepGraph_NodeId::Kind::Compound) - { - theStorage.ChangeCompound(BRepGraph_CompoundId(theParentId)).ChildRefIds.Append(aRefId); - } - return aRefId; + theStorage.AttachChildToCompound(theOwnerCompoundId, theChildId, theLocation, theOrientation); } //================================================================================================= -//! Per-vertex data extracted from TopoDS in parallel phase. struct ExtractedVertex { - TopoDS_Vertex Shape; - gp_Pnt Point; - double Tolerance = 0.0; + TopoDS_Vertex Shape; + gp_Pnt Point; + double Tolerance = 0.0; + TopLoc_Location BakedLocation; + bool IsGenerated = false; }; -//! Internal/external vertex extracted from an edge. -struct ExtractedInternalVertex -{ - TopoDS_Vertex Shape; - gp_Pnt Point; - double Tolerance = 0.0; - TopAbs_Orientation Orientation = TopAbs_INTERNAL; -}; - -//! Per-edge data extracted from TopoDS in parallel phase. struct ExtractedEdge { - TopoDS_Edge Shape; - occ::handle Curve3d; - double ParamFirst = 0.0; - double ParamLast = 0.0; - double Tolerance = 0.0; - bool IsDegenerate = false; - bool SameParameter = false; - bool SameRange = false; - ExtractedVertex StartVertex; - ExtractedVertex EndVertex; - NCollection_DynamicArray InternalVertices; - TopAbs_Orientation OrientationInWire = TopAbs_FORWARD; - occ::handle PCurve2d; - double PCFirst = 0.0; - double PCLast = 0.0; - gp_Pnt2d PCUV1; - gp_Pnt2d PCUV2; - occ::handle Polygon3D; - occ::handle PolyOnSurf; + TopoDS_Edge Shape; + occ::handle Curve3d; + double ParamFirst = 0.0; + double ParamLast = 0.0; + double Tolerance = 0.0; + ExtractedVertex StartVertex; + ExtractedVertex EndVertex; + TopAbs_Orientation OrientationInWire = TopAbs_FORWARD; + occ::handle PCurve2d; + double PCFirst = 0.0; + double PCLast = 0.0; + occ::handle Polygon3D; + occ::handle PolyOnSurf; + occ::handle PolyOnTri; + TopLoc_Location BakedLocation; + bool IsGenerated = false; }; -//! Per-wire data extracted in parallel phase. struct ExtractedWire { + ExtractedWire() + : Edges(THE_EDGE_BUCKET) + { + } + TopoDS_Wire Shape; - bool IsOuter = false; NCollection_DynamicArray Edges; + TopLoc_Location BakedLocation; + bool IsGenerated = false; + bool HasPartialExplorerOrder = false; }; -//! All data extracted from a single face. -struct FaceLocalData +struct FaceBuildData { - // Phase 1 context. - TopoDS_Face Face; - TopLoc_Location ParentGlobalLoc; - BRepGraph_ShellId ParentShellId; + FaceBuildData() + : Wires(THE_WIRE_BUCKET) + { + } - // Phase 2 extracted geometry. - occ::handle Surface; - occ::handle OriginalSurface; //!< Pre-transform surface for PCurve matching - occ::handle ActiveTriangulation; - double Tolerance = 0.0; - TopAbs_Orientation Orientation = TopAbs_FORWARD; - bool NaturalRestriction = false; + TopoDS_Face Face; + BRepGraph_FaceId FaceId; + + occ::handle Surface; + occ::handle RawTriangulation; + occ::handle ActiveTriangulation; + TopLoc_Location BakedLocation; + double Tolerance = 0.0; + bool UnsupportedNaturalBoundary = false; + bool NeedsSynthesis = false; NCollection_DynamicArray Wires; - NCollection_DynamicArray - DirectVertices; //!< INTERNAL/EXTERNAL vertex children }; -//! Extract stored PCurve(s) from edge for a given face's surface. -//! Iterates BRep_TEdge::Curves() directly, avoiding BRep_Tool::CurveOnSurface -//! which can compute phantom PCurves via CurveOnPlane for planar surfaces. -//! Uses multi-pass matching: -//! Pass 1: exact (Surface, Location) match via IsCurveOnSurface(S, L) -//! Pass 2: surface-handle-only fallback for TopLoc_Location structural equality bug -//! (only when a unique CR matches the surface - prevents wrong-context selection) -//! Pass 3: original (pre-transform) surface handle match when face surface -//! was transformed via applyRepresentationLocation -//! Returns ONE PCurve matching the input edge's orientation. For seam edges -//! (IsCurveOnClosedSurface), picks PCurve() for FORWARD or PCurve2() for REVERSED. -//! -//! WARNING: Passes 2-3 are workarounds for TopLoc_Location structural equality -//! issues where an explicit identity datum does not compare equal to a default -//! empty identity. If the upstream TopLoc_Location comparison is fixed, passes -//! 2-3 may become redundant but should remain harmless (pass 1 will match first). -//! @param[in] theEdge edge with context orientation and location -//! @param[in] theFace face with context location -//! @param[out] thePCurve PCurve matching theEdge.Orientation() (or null) -//! @param[out] theFirst parameter range start -//! @param[out] theLast parameter range end -//! @param[in] theOrigSurface pre-transform surface handle for fallback matching -//! when the face's raw TFace surface differs from edge CRs -//! @return true if a stored PCurve was found -static bool extractStoredPCurves( - const TopoDS_Edge& theEdge, - const TopoDS_Face& theFace, - occ::handle& thePCurve, - double& theFirst, - double& theLast, - const occ::handle& theOrigSurface = occ::handle()) +struct FacePCurveContext { - TopLoc_Location aFaceLoc; - const occ::handle& aSurf = BRep_Tool::Surface(theFace, aFaceLoc); - if (aSurf.IsNull()) + occ::handle RawSurface; + TopLoc_Location SurfaceLocation; +}; + +static bool findStoredPCurve( + const NCollection_List>& theCurveReps, + const occ::handle& theSurface, + const TopLoc_Location& theLocation, + const bool theReversed, + occ::handle& thePCurve, + double& theFirst, + double& theLast) +{ + for (const occ::handle& aCurveRep : theCurveReps) + { + if (!aCurveRep->IsCurveOnSurface(theSurface, theLocation)) + { + continue; + } + const occ::handle aGCurve = occ::down_cast(aCurveRep); + if (aGCurve.IsNull()) + { + return false; + } + + aGCurve->Range(theFirst, theLast); + thePCurve = + aGCurve->IsCurveOnClosedSurface() && theReversed ? aGCurve->PCurve2() : aGCurve->PCurve(); + return true; + } + return false; +} + +static bool extractStoredPCurve(const TopoDS_Edge& theEdge, + const FacePCurveContext& theContext, + occ::handle& thePCurve, + double& theFirst, + double& theLast) +{ + if (theContext.RawSurface.IsNull()) { return false; } @@ -292,276 +357,112 @@ static bool extractStoredPCurves( return false; } const NCollection_List>& aCurves = aTEdge->Curves(); - const bool aReversed = (theEdge.Orientation() == TopAbs_REVERSED); - - // Expected CurveRepresentation location for this face+edge context. - // This is the same formula used by BRep_Tool::CurveOnSurface internally. - const TopLoc_Location aExpectedLoc = aFaceLoc.Predivided(theEdge.Location()); - - // Lambda to extract PCurve data from a matched CurveRepresentation. - // Picks the PCurve matching the input edge's orientation. - const auto anExtractFromCR = [&](const occ::handle& theCR) -> bool { - const BRep_GCurve* aGC = static_cast(theCR.get()); - aGC->Range(theFirst, theLast); - thePCurve = aGC->IsCurveOnClosedSurface() && aReversed ? aGC->PCurve2() : aGC->PCurve(); - return true; - }; - - // Pass 1: exact match by (Surface, Location). Correctly distinguishes - // multiple CurveOnSurface entries for the same surface with different Locations. - for (const occ::handle& aCR : aCurves) - { - if (!aCR->IsCurveOnSurface()) - { - continue; - } - if (aCR->IsCurveOnSurface(aSurf, aExpectedLoc)) - { - return anExtractFromCR(aCR); - } - } - - // Pass 1b: match using raw TFace.Location (preserves datum pointers). - // When Pass 1 fails due to TopLoc_Location structural inequality (the composed - // face.Location() * TFace.Location() creates a chain with different structure - // than what was used during CR creation), retry with JUST TFace.Location(). - // The TFace.Location is a fixed TShape property whose datum pointers are shared - // with the CR's stored location chain, enabling structural match to succeed. - { - const TopLoc_Location& aTFaceLoc = - static_cast(theFace.TShape().get())->Location(); - if (!aTFaceLoc.IsIdentity()) - { - const TopLoc_Location aRawExpectedLoc = aTFaceLoc.Predivided(theEdge.Location()); - for (const occ::handle& aCR : aCurves) - { - if (!aCR->IsCurveOnSurface()) - { - continue; - } - if (aCR->IsCurveOnSurface(aSurf, aRawExpectedLoc)) - { - return anExtractFromCR(aCR); - } - } - } - } - - // Pass 2: fallback to surface-handle-only match. - // Handles the TopLoc_Location structural equality bug where - // an explicit identity datum does not compare equal to a default empty identity. - for (const occ::handle& aCR : aCurves) - { - if (!aCR->IsCurveOnSurface() || aCR->Surface() != aSurf) - { - continue; - } - return anExtractFromCR(aCR); - } - - // Pass 3: match by the original (pre-transform) surface handle. - // When applyRepresentationLocation creates a new surface via Transformed(), - // the edge's CRs still reference the OLD surface handle. If the face's raw - // TFace surface differs from the old surface (e.g., compound children at - // different locations), passes 1-2 fail. Here we retry with the original - // surface handle that the edge CRs actually reference. - if (!theOrigSurface.IsNull() && theOrigSurface != aSurf) - { - // Pass 3a: exact (original surface, expected location) match. - for (const occ::handle& aCR : aCurves) - { - if (!aCR->IsCurveOnSurface()) - { - continue; - } - if (aCR->IsCurveOnSurface(theOrigSurface, aExpectedLoc)) - { - return anExtractFromCR(aCR); - } - } - // Pass 3a.5: raw TFace.Location match on original surface. - { - const TopLoc_Location& aTFaceLoc = - static_cast(theFace.TShape().get())->Location(); - if (!aTFaceLoc.IsIdentity()) - { - const TopLoc_Location aRawExpectedLoc = aTFaceLoc.Predivided(theEdge.Location()); - for (const occ::handle& aCR : aCurves) - { - if (!aCR->IsCurveOnSurface()) - { - continue; - } - if (aCR->IsCurveOnSurface(theOrigSurface, aRawExpectedLoc)) - { - return anExtractFromCR(aCR); - } - } - } - } - // Pass 3b: original surface handle-only match (location structural equality fallback). - for (const occ::handle& aCR : aCurves) - { - if (!aCR->IsCurveOnSurface() || aCR->Surface() != theOrigSurface) - { - continue; - } - return anExtractFromCR(aCR); - } - } - - return false; + const bool aReversed = (theEdge.Orientation() == TopAbs_REVERSED); + const TopLoc_Location aExpectedLoc = theContext.SurfaceLocation.Predivided(theEdge.Location()); + return findStoredPCurve(aCurves, + theContext.RawSurface, + aExpectedLoc, + aReversed, + thePCurve, + theFirst, + theLast); } -//! Get the raw BRep_TVertex point without applying vertex Location. -//! Stores the point in the TShape-local (definition) frame, consistent -//! with how edge curves and face surfaces are stored after -//! applyRepresentationLocation. -gp_Pnt rawVertexPoint(const TopoDS_Vertex& theVertex) +bool rawVertexPoint(const TopoDS_Vertex& theVertex, gp_Pnt& thePoint) { - return static_cast(theVertex.TShape().get())->Pnt(); + const occ::handle& aTShape = theVertex.TShape(); + if (aTShape.IsNull()) + { + return false; + } + thePoint = static_cast(aTShape.get())->Pnt(); + return true; } -//! Map TopAbs_ShapeEnum to BRepGraph_NodeId::Kind. -//! Asserts on unhandled ShapeEnum (e.g., TopAbs_SHAPE). -BRepGraph_NodeId::Kind shapeTypeToNodeKind(TopAbs_ShapeEnum theType) +bool isForwardChildType(const TopAbs_ShapeEnum theOwnerType, const TopAbs_ShapeEnum theChildType) { - switch (theType) + switch (theOwnerType) { case TopAbs_COMPOUND: - return BRepGraph_NodeId::Kind::Compound; + return theChildType != TopAbs_SHAPE; case TopAbs_COMPSOLID: - return BRepGraph_NodeId::Kind::CompSolid; + return theChildType == TopAbs_SOLID; case TopAbs_SOLID: - return BRepGraph_NodeId::Kind::Solid; + return theChildType == TopAbs_SHELL; case TopAbs_SHELL: - return BRepGraph_NodeId::Kind::Shell; - case TopAbs_FACE: - return BRepGraph_NodeId::Kind::Face; + return theChildType == TopAbs_FACE; case TopAbs_WIRE: - return BRepGraph_NodeId::Kind::Wire; - case TopAbs_EDGE: - return BRepGraph_NodeId::Kind::Edge; - case TopAbs_VERTEX: - return BRepGraph_NodeId::Kind::Vertex; + return theChildType == TopAbs_EDGE; default: - Standard_ASSERT_VOID(false, "shapeTypeToNodeKind: unhandled ShapeEnum"); - return BRepGraph_NodeId::Kind::Solid; // unreachable in practice + return false; } } -//! Check if a shape's TShape is already registered in storage with the expected kind. -//! Returns the existing NodeId pointer if found, nullptr otherwise. -const BRepGraph_NodeId* findExistingNode(const BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theShape, - BRepGraph_NodeId::Kind theExpectedKind) +bool isCoreParityOrientation(const TopAbs_Orientation theOrientation) { - const BRepGraph_NodeId* anExisting = theStorage.FindNodeByTShape(theShape.TShape().get()); - if (anExisting != nullptr && anExisting->NodeKind == theExpectedKind) - { - return anExisting; - } - return nullptr; + return theOrientation == TopAbs_FORWARD || theOrientation == TopAbs_REVERSED; } -//! Register a vertex entity by TShape dedup, or return the existing VertexId. -//! @param[in,out] theStorage incidence storage -//! @param[in] theVertex original TopoDS_Vertex -//! @param[in] thePoint 3D point (pre-extracted) -//! @param[in] theTolerance vertex tolerance (pre-extracted) -//! @return typed VertexId, or default-constructed (invalid) if theVertex is null -BRepGraph_VertexId registerOrReuseVertex(BRepGraphInc_Storage& theStorage, - const TopoDS_Vertex& theVertex, - const gp_Pnt& thePoint, - const double theTolerance) +BRepGraph_LayerTopoSupplement::AttachmentKind containerSupplementKind( + const TopAbs_ShapeEnum theOwnerType) { - if (theVertex.IsNull()) + switch (theOwnerType) { - return BRepGraph_VertexId(); + case TopAbs_COMPOUND: + return BRepGraph_LayerTopoSupplement::AttachmentKind::CompoundAuxShape; + case TopAbs_COMPSOLID: + return BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape; + case TopAbs_SOLID: + return BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape; + case TopAbs_SHELL: + return BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape; + default: + return BRepGraph_LayerTopoSupplement::AttachmentKind::GenericSupplementShape; } - const BRepGraph_NodeId* anExisting = - findExistingNode(theStorage, theVertex, BRepGraph_NodeId::Kind::Vertex); - if (anExisting != nullptr) - { - return BRepGraph_VertexId(*anExisting); - } - - const BRepGraph_VertexId aVtxId = theStorage.AppendVertex(); - BRepGraphInc::VertexDef& aVtxEnt = theStorage.ChangeVertex(aVtxId); - aVtxEnt.Point = thePoint; - aVtxEnt.Tolerance = theTolerance; - theStorage.BindTShapeToNode(theVertex.TShape().get(), aVtxId); - theStorage.BindOriginal(aVtxId, theVertex); - return aVtxId; } -//! Convenience overload: extracts point and tolerance from the vertex. -BRepGraph_VertexId registerOrReuseVertex(BRepGraphInc_Storage& theStorage, - const TopoDS_Vertex& theVertex) -{ - if (theVertex.IsNull()) - { - return BRepGraph_VertexId(); - } - return registerOrReuseVertex(theStorage, - theVertex, - rawVertexPoint(theVertex), - BRep_Tool::Tolerance(theVertex)); -} - -//! Factor representation location from combined location and apply to geometry. -//! Returns the transformed geometry if the representation location is non-identity, -//! otherwise returns the original unchanged. -//! @tparam T Geom_Surface or Geom_Curve (must support Transformed()) template -occ::handle applyRepresentationLocation(const occ::handle& theGeom, - const TopLoc_Location& theShapeLoc, - const TopLoc_Location& theCombinedLoc) +static occ::handle applyBakedLocation(const occ::handle& theGeom, + const TopLoc_Location& theLocation) { - if (theGeom.IsNull()) + if (theGeom.IsNull() || theLocation.IsIdentity()) { return theGeom; } - // Do NOT use theCombinedLoc.IsIdentity() as an early return - TopLoc_Location - // chain composition can structurally cancel to Identity (empty chain) even when - // the actual repLoc (theShapeLoc^-1 * theCombinedLoc) is non-Identity. - // This happens when the edge instance location and the CR location on TEdge - // form inverse pairs that cancel in Multiplied(). - const TopLoc_Location aRepLoc = theShapeLoc.Inverted() * theCombinedLoc; - if (aRepLoc.IsIdentity()) - { - return theGeom; - } - return occ::down_cast(theGeom->Transformed(aRepLoc.Transformation())); + return occ::down_cast(theGeom->Transformed(theLocation.Transformation())); } -//! Apply representation location to a Polygon3D by transforming its nodes. -static occ::handle applyRepLocationToPolygon3D( - const occ::handle& thePolygon3D, - const TopLoc_Location& theShapeLoc, - const TopLoc_Location& theCombinedLoc) +static gp_Pnt applyBakedLocation(const gp_Pnt& thePoint, const TopLoc_Location& theLocation) { - if (thePolygon3D.IsNull()) - { - return thePolygon3D; - } - const TopLoc_Location aRepLoc = theShapeLoc.Inverted() * theCombinedLoc; - if (aRepLoc.IsIdentity()) + return theLocation.IsIdentity() ? thePoint : thePoint.Transformed(theLocation.Transformation()); +} + +static occ::handle applyBakedLocationToPolygon3D( + const occ::handle& thePolygon3D, + const TopLoc_Location& theLocation) +{ + if (thePolygon3D.IsNull() || theLocation.IsIdentity()) { return thePolygon3D; } - const gp_Trsf& aTrsf = aRepLoc.Transformation(); + const gp_Trsf& aTrsf = theLocation.Transformation(); const NCollection_Array1& aNodes = thePolygon3D->Nodes(); - NCollection_Array1 aNewNodes(aNodes.Lower(), aNodes.Upper()); - for (int aNodeIdx = aNodes.Lower(); aNodeIdx <= aNodes.Upper(); ++aNodeIdx) + NCollection_Array1 aNewNodes(aNodes.Size()); + for (size_t aNodeIdx = 0; aNodeIdx < aNodes.Size(); ++aNodeIdx) { - aNewNodes.SetValue(aNodeIdx, aNodes.Value(aNodeIdx).Transformed(aTrsf)); + aNewNodes.ChangeAt(aNodeIdx) = aNodes.At(aNodeIdx).Transformed(aTrsf); } occ::handle aTransPoly; if (thePolygon3D->HasParameters()) { - aTransPoly = new Poly_Polygon3D(aNewNodes, thePolygon3D->Parameters()); + const NCollection_Array1& aParams = thePolygon3D->Parameters(); + NCollection_Array1 aNewParams(aParams.Size()); + for (size_t aParamIdx = 0; aParamIdx < aParams.Size(); ++aParamIdx) + { + aNewParams.ChangeAt(aParamIdx) = aParams.At(aParamIdx); + } + aTransPoly = new Poly_Polygon3D(aNewNodes, aNewParams); } else { @@ -571,280 +472,437 @@ static occ::handle applyRepLocationToPolygon3D( return aTransPoly; } -//! Deduplication maps for representation entities. -//! Keyed by raw Handle pointer - same underlying geometry object -> same rep entity. -struct RepDedup +static occ::handle applyBakedLocationToTriangulation( + const occ::handle& theTriangulation, + const TopLoc_Location& theLocation) { - NCollection_DataMap Surfaces; - NCollection_DataMap Curves3D; - NCollection_DataMap Curves2D; - NCollection_DataMap Triangulations; - NCollection_DataMap Polygons3D; - NCollection_DataMap Polygons2D; - NCollection_DataMap - PolygonsOnTri; + if (theTriangulation.IsNull() || theLocation.IsIdentity()) + { + return theTriangulation; + } + + occ::handle aCopy = theTriangulation->Copy(); + const gp_Trsf& aTrsf = theLocation.Transformation(); + Poly_ArrayOfNodes& aNodes = aCopy->InternalNodes(); + for (int aNodeIdx = 0; aNodeIdx < aNodes.Length(); ++aNodeIdx) + { + aNodes.SetValue(aNodeIdx, aNodes.Value(aNodeIdx).Transformed(aTrsf)); + } + if (aCopy->HasNormals()) + { + for (int aNodeIdx = 1; aNodeIdx <= aCopy->NbNodes(); ++aNodeIdx) + { + aCopy->SetNormal(aNodeIdx, aCopy->Normal(aNodeIdx).Transformed(aTrsf)); + } + } + return aCopy; +} + +struct LocatedNodeBinding +{ + const TopoDS_TShape* TShape = nullptr; + TopLoc_Location Location; + BRepGraph_NodeId Node; }; -//! Create or reuse a SurfaceRep for the given surface handle. -BRepGraph_SurfaceRepId getOrCreateSurfaceRep(BRepGraphInc_Storage& theStorage, - RepDedup& theDedup, - const occ::handle& theSurface) +using LocatedNodeBindingIndex = uint32_t; + +enum class RootRole { - if (theSurface.IsNull()) - { - return BRepGraph_SurfaceRepId(); - } - const Geom_Surface* aPtr = theSurface.get(); - const BRepGraph_SurfaceRepId* anExisting = theDedup.Surfaces.Seek(aPtr); - if (anExisting != nullptr) - { - return *anExisting; - } - const BRepGraph_SurfaceRepId aRepId = theStorage.AppendSurfaceRep(); - BRepGraphInc::SurfaceRep& aRep = theStorage.ChangeSurfaceRep(aRepId); - aRep.Surface = theSurface; - theDedup.Surfaces.Bind(aPtr, aRepId); - return aRepId; -} + Nested, + Root +}; -//! Create or reuse a Curve3DRep for the given 3D curve handle. -BRepGraph_Curve3DRepId getOrCreateCurve3DRep(BRepGraphInc_Storage& theStorage, - RepDedup& theDedup, - const occ::handle& theCurve) +struct BuildContext; + +BRepGraph_NodeId findExistingNode(const BuildContext& theBuild, + const TopoDS_Shape& theShape, + BRepGraph_NodeId::Kind theExpectedKind, + const TopLoc_Location& theBakedLocation); + +void bindLocatedNode(BuildContext& theBuild, + const TopoDS_Shape& theShape, + const BRepGraph_NodeId theNodeId, + const TopLoc_Location& theBakedLocation, + const bool theBindTShape); + +enum class TopologyBuildMode { - if (theCurve.IsNull()) - { - return BRepGraph_Curve3DRepId(); - } - const Geom_Curve* aPtr = theCurve.get(); - const BRepGraph_Curve3DRepId* anExisting = theDedup.Curves3D.Seek(aPtr); - if (anExisting != nullptr) - { - return *anExisting; - } - const BRepGraph_Curve3DRepId aRepId = theStorage.AppendCurve3DRep(); - BRepGraphInc::Curve3DRep& aRep = theStorage.ChangeCurve3DRep(aRepId); - aRep.Curve = theCurve; - theDedup.Curves3D.Bind(aPtr, aRepId); - return aRepId; -} + FullHierarchy, + Flattened +}; -//! Create or reuse a Curve2DRep for the given 2D curve handle. -BRepGraph_Curve2DRepId getOrCreateCurve2DRep(BRepGraphInc_Storage& theStorage, - RepDedup& theDedup, - const occ::handle& theCurve) +struct BuildContext { - if (theCurve.IsNull()) + BuildContext(BRepGraphInc_Storage& theStorage, + const bool theParallel, + const TopologyBuildMode theMode, + const occ::handle& theSupplementLayer, + NCollection_LinearVector* theAppendedRoots = nullptr) + : Storage(theStorage), + Parallel(theParallel), + Mode(theMode), + SupplementLayer(theSupplementLayer), + AppendedRoots(theAppendedRoots), + PendingFaces(THE_PENDING_FACE_BUCKET), + LocatedNodes(THE_LOCATED_NODE_BUCKET) { - return BRepGraph_Curve2DRepId(); - } - const Geom2d_Curve* aPtr = theCurve.get(); - const BRepGraph_Curve2DRepId* anExisting = theDedup.Curves2D.Seek(aPtr); - if (anExisting != nullptr) - { - return *anExisting; - } - const BRepGraph_Curve2DRepId aRepId = theStorage.AppendCurve2DRep(); - BRepGraphInc::Curve2DRep& aRep = theStorage.ChangeCurve2DRep(aRepId); - aRep.Curve = theCurve; - theDedup.Curves2D.Bind(aPtr, aRepId); - return aRepId; -} - -//! Create or reuse a TriangulationRep for the given triangulation handle. -BRepGraph_TriangulationRepId getOrCreateTriangulationRep( - BRepGraphInc_Storage& theStorage, - RepDedup& theDedup, - const occ::handle& theTriangulation) -{ - if (theTriangulation.IsNull()) - { - return BRepGraph_TriangulationRepId(); - } - const Poly_Triangulation* aPtr = theTriangulation.get(); - const BRepGraph_TriangulationRepId* anExisting = theDedup.Triangulations.Seek(aPtr); - if (anExisting != nullptr) - { - return *anExisting; - } - const BRepGraph_TriangulationRepId aRepId = theStorage.AppendTriangulationRep(); - BRepGraphInc::TriangulationRep& aRep = theStorage.ChangeTriangulationRep(aRepId); - aRep.Triangulation = theTriangulation; - theDedup.Triangulations.Bind(aPtr, aRepId); - return aRepId; -} - -//! Create or reuse a Polygon3DRep for the given polygon handle. -BRepGraph_Polygon3DRepId getOrCreatePolygon3DRep(BRepGraphInc_Storage& theStorage, - RepDedup& theDedup, - const occ::handle& thePolygon) -{ - if (thePolygon.IsNull()) - { - return BRepGraph_Polygon3DRepId(); - } - const Poly_Polygon3D* aPtr = thePolygon.get(); - const BRepGraph_Polygon3DRepId* anExisting = theDedup.Polygons3D.Seek(aPtr); - if (anExisting != nullptr) - { - return *anExisting; - } - const BRepGraph_Polygon3DRepId aRepId = theStorage.AppendPolygon3DRep(); - BRepGraphInc::Polygon3DRep& aRep = theStorage.ChangePolygon3DRep(aRepId); - aRep.Polygon = thePolygon; - theDedup.Polygons3D.Bind(aPtr, aRepId); - return aRepId; -} - -//! Create or reuse a Polygon2DRep for the given polygon-on-surface handle. -BRepGraph_Polygon2DRepId getOrCreatePolygon2DRep(BRepGraphInc_Storage& theStorage, - RepDedup& theDedup, - const occ::handle& thePolygon) -{ - if (thePolygon.IsNull()) - { - return BRepGraph_Polygon2DRepId(); - } - const Poly_Polygon2D* aPtr = thePolygon.get(); - const BRepGraph_Polygon2DRepId* anExisting = theDedup.Polygons2D.Seek(aPtr); - if (anExisting != nullptr) - { - return *anExisting; - } - const BRepGraph_Polygon2DRepId aRepId = theStorage.AppendPolygon2DRep(); - BRepGraphInc::Polygon2DRep& aRep = theStorage.ChangePolygon2DRep(aRepId); - aRep.Polygon = thePolygon; - theDedup.Polygons2D.Bind(aPtr, aRepId); - return aRepId; -} - -//! Create or reuse a PolygonOnTriRep for the given polygon-on-triangulation handle. -//! theTriRepId is the global TriangulationRepId (not face-local). -BRepGraph_PolygonOnTriRepId getOrCreatePolygonOnTriRep( - BRepGraphInc_Storage& theStorage, - RepDedup& theDedup, - const occ::handle& thePolygon, - const BRepGraph_TriangulationRepId theTriRepId) -{ - if (thePolygon.IsNull()) - { - return BRepGraph_PolygonOnTriRepId(); - } - const Poly_PolygonOnTriangulation* aPtr = thePolygon.get(); - const BRepGraph_PolygonOnTriRepId* anExisting = theDedup.PolygonsOnTri.Seek(aPtr); - if (anExisting != nullptr) - { - return *anExisting; - } - const BRepGraph_PolygonOnTriRepId aRepId = theStorage.AppendPolygonOnTriRep(); - BRepGraphInc::PolygonOnTriRep& aRep = theStorage.ChangePolygonOnTriRep(aRepId); - aRep.Polygon = thePolygon; - aRep.TriangulationRepId = theTriRepId; - theDedup.PolygonsOnTri.Bind(aPtr, aRepId); - return aRepId; -} - -//! Register an edge entity from pre-extracted data, with TShape dedup. -//! Creates the entity (with vertices) if new, returns the EdgeId in both cases. -BRepGraph_EdgeId registerExtractedEdge(BRepGraphInc_Storage& theStorage, - const ExtractedEdge& theEdgeData, - RepDedup& theRepDedup) -{ - const BRepGraph_NodeId* anExisting = - findExistingNode(theStorage, theEdgeData.Shape, BRepGraph_NodeId::Kind::Edge); - if (anExisting != nullptr) - { - return BRepGraph_EdgeId(*anExisting); - } - - const BRepGraph_EdgeId anEdgeId = theStorage.AppendEdge(); - BRepGraphInc::EdgeDef& anEdgeEnt = theStorage.ChangeEdge(anEdgeId); - anEdgeEnt.Tolerance = theEdgeData.Tolerance; - anEdgeEnt.IsDegenerate = theEdgeData.IsDegenerate; - anEdgeEnt.SameParameter = theEdgeData.SameParameter; - anEdgeEnt.SameRange = theEdgeData.SameRange; - anEdgeEnt.IsClosed = theEdgeData.Shape.Closed(); - anEdgeEnt.ParamFirst = theEdgeData.ParamFirst; - anEdgeEnt.ParamLast = theEdgeData.ParamLast; - - if (!theEdgeData.Curve3d.IsNull()) - { - anEdgeEnt.Curve3DRepId = getOrCreateCurve3DRep(theStorage, theRepDedup, theEdgeData.Curve3d); - } - - // Vertex registration (boundary vertices stored as VertexRefId into Ref table). - // Vertices may be null for infinite edges or degenerate topology. - if (!theEdgeData.StartVertex.Shape.IsNull()) - { - BRepGraphInc::VertexInstance aVR; - aVR.DefId = registerOrReuseVertex(theStorage, - theEdgeData.StartVertex.Shape, - theEdgeData.StartVertex.Point, - theEdgeData.StartVertex.Tolerance); - aVR.Orientation = TopAbs_FORWARD; - aVR.Location = theEdgeData.StartVertex.Shape.Location(); - anEdgeEnt.StartVertexRefId = appendVertexRef(theStorage, anEdgeId, aVR); - } - if (!theEdgeData.EndVertex.Shape.IsNull()) - { - BRepGraphInc::VertexInstance aVR; - aVR.DefId = registerOrReuseVertex(theStorage, - theEdgeData.EndVertex.Shape, - theEdgeData.EndVertex.Point, - theEdgeData.EndVertex.Tolerance); - aVR.Orientation = TopAbs_REVERSED; - aVR.Location = theEdgeData.EndVertex.Shape.Location(); - anEdgeEnt.EndVertexRefId = appendVertexRef(theStorage, anEdgeId, aVR); - } - - // Register internal/external vertices. - for (const ExtractedInternalVertex& anIntVtx : theEdgeData.InternalVertices) - { - const BRepGraph_VertexId anIntVtxId = - registerOrReuseVertex(theStorage, anIntVtx.Shape, anIntVtx.Point, anIntVtx.Tolerance); - if (anIntVtxId.IsValid()) + if (AppendedRoots != nullptr) { - BRepGraphInc::VertexInstance aVR; - aVR.DefId = anIntVtxId; - aVR.Orientation = anIntVtx.Orientation; - aVR.Location = anIntVtx.Shape.Location(); - anEdgeEnt.InternalVertexRefIds.Append(appendVertexRef(theStorage, anEdgeId, aVR)); + AppendedRootSet.Reserve(THE_ROOT_SET_BUCKET); } } - theStorage.BindTShapeToNode(theEdgeData.Shape.TShape().get(), anEdgeId); - theStorage.BindOriginal(anEdgeId, theEdgeData.Shape); - return anEdgeId; + BRepGraphInc_Storage& Storage; + bool Parallel; + TopologyBuildMode Mode; + occ::handle SupplementLayer; + NCollection_LinearVector* AppendedRoots = nullptr; + NCollection_FlatMap AppendedRootSet; + NCollection_DynamicArray PendingFaces; + NCollection_DynamicArray LocatedNodes; + NCollection_DataMap> + LocatedNodeIndex; + bool HasWireOrderWarnings = false; +}; + +bool isWireOrderWarning(const BRepGraphInc_Storage::WireCoEdgeOrderStatus theStatus) +{ + using Status = BRepGraphInc_Storage::WireCoEdgeOrderStatus; + return theStatus == Status::ToleranceOrdered || theStatus == Status::Partial + || theStatus == Status::InvalidInput; } -//! Create a NodeInstance from a child shape, resolving its index via TShape lookup. -//! Returns true if the ref was created successfully (child was found in storage). -bool makeAuxChildRef(const BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theChild, - BRepGraphInc::NodeInstance& theRef) +void recordWireOrderStatus(BuildContext& theBuild, + const BRepGraphInc_Storage::WireCoEdgeOrderStatus theStatus) { - const BRepGraph_NodeId* aChildNodeId = theStorage.FindNodeByTShape(theChild.TShape().get()); - if (aChildNodeId == nullptr) + theBuild.HasWireOrderWarnings = theBuild.HasWireOrderWarnings || isWireOrderWarning(theStatus); +} + +BRepGraph_NodeId findExistingNode(const BuildContext& theBuild, + const TopoDS_Shape& theShape, + BRepGraph_NodeId::Kind theExpectedKind, + const TopLoc_Location& theBakedLocation) +{ + const TopoDS_TShape* aTShape = theShape.TShape().get(); + if (theBakedLocation.IsIdentity()) + { + const BRepGraph_NodeId* aStoredNode = theBuild.Storage.FindNodeByTShape(aTShape); + if (aStoredNode != nullptr && aStoredNode->NodeKind == theExpectedKind) + { + return *aStoredNode; + } + } + + const NCollection_LinearVector* anIndices = + theBuild.LocatedNodeIndex.Seek(aTShape); + if (anIndices == nullptr) + { + return BRepGraph_NodeId(); + } + for (const LocatedNodeBindingIndex aBindingIndex : *anIndices) + { + const LocatedNodeBinding& aBinding = + theBuild.LocatedNodes.Value(static_cast(aBindingIndex)); + if (aBinding.TShape == aTShape && aBinding.Node.NodeKind == theExpectedKind + && aBinding.Location.IsEqual(theBakedLocation)) + { + return aBinding.Node; + } + } + return BRepGraph_NodeId(); +} + +void bindLocatedNode(BuildContext& theBuild, + const TopoDS_Shape& theShape, + const BRepGraph_NodeId theNodeId, + const TopLoc_Location& theBakedLocation, + const bool theBindTShape) +{ + const TopoDS_TShape* aTShape = theShape.TShape().get(); + const LocatedNodeBindingIndex aBindingIndex = + static_cast(theBuild.LocatedNodes.Size()); + LocatedNodeBinding& aBinding = theBuild.LocatedNodes.Appended(); + aBinding.TShape = aTShape; + aBinding.Location = theBakedLocation; + aBinding.Node = theNodeId; + + if (!theBuild.LocatedNodeIndex.IsBound(aTShape)) + { + theBuild.LocatedNodeIndex.Bind(aTShape, NCollection_LinearVector()); + } + theBuild.LocatedNodeIndex.ChangeFind(aTShape).Append(aBindingIndex); + + if (theBindTShape && theBakedLocation.IsIdentity() && !theBuild.Storage.HasTShapeBinding(aTShape)) + { + theBuild.Storage.BindTShapeToNode(aTShape, theNodeId); + } +} + +BRepGraph_VertexId registerOrReuseVertex(BuildContext& theBuild, + const TopoDS_Vertex& theVertex, + const gp_Pnt& thePoint, + const double theTolerance, + const TopLoc_Location& theBakedLocation, + const bool theIsGenerated = false) +{ + if (theVertex.IsNull()) + { + return BRepGraph_VertexId(); + } + const BRepGraph_NodeId anExisting = + findExistingNode(theBuild, theVertex, BRepGraph_NodeId::Kind::Vertex, theBakedLocation); + if (anExisting.IsValid()) + { + return BRepGraph_VertexId(anExisting); + } + + const BRepGraph_VertexId aVtxId = theBuild.Storage.AppendVertex(); + BRepGraphInc::VertexDef& aVtxEnt = theBuild.Storage.ChangeVertex(aVtxId); + aVtxEnt.Point = thePoint; + aVtxEnt.Tolerance = theTolerance; + if (!theIsGenerated) + { + bindLocatedNode(theBuild, theVertex, aVtxId, theBakedLocation, true); + theBuild.Storage.BindOriginal(aVtxId, theVertex); + } + return aVtxId; +} + +BRepGraph_VertexId registerOrReuseVertex(BuildContext& theBuild, + const TopoDS_Vertex& theVertex, + const TopLoc_Location& theBakedLocation) +{ + if (theVertex.IsNull()) + { + return BRepGraph_VertexId(); + } + gp_Pnt aPoint; + if (!rawVertexPoint(theVertex, aPoint)) + { + return BRepGraph_VertexId(); + } + return registerOrReuseVertex(theBuild, + theVertex, + applyBakedLocation(aPoint, theBakedLocation), + BRep_Tool::Tolerance(theVertex), + theBakedLocation); +} + +bool attachNonCoreContainerChild(BuildContext& theBuild, + const BRepGraph_NodeId theOwner, + const TopAbs_ShapeEnum theOwnerType, + const TopoDS_Shape& theChild) +{ + if (theBuild.SupplementLayer.IsNull()) { return false; } - theRef.DefId = *aChildNodeId; - theRef.Orientation = theChild.Orientation(); - theRef.Location = theChild.Location(); - return true; + return theBuild.SupplementLayer->AddAttachment(theOwner, + containerSupplementKind(theOwnerType), + theChild) + != 0; } -//! Extract first, last, and internal/external vertices from an edge. -//! Extract start (FORWARD), end (REVERSED), and internal vertices from an edge. -//! TopoDS_Iterator may yield multiple FORWARD or REVERSED vertices (rare but legal, -//! e.g., edges rebuilt by BRepTools_Modifier). In that case the *last* encountered -//! vertex of each orientation becomes the boundary vertex; earlier ones are demoted -//! to the internal list with their original orientation preserved. INTERNAL and -//! EXTERNAL vertices go directly to the internal list. -static void edgeVertices(const TopoDS_Edge& theEdge, - TopoDS_Vertex& theFirst, - TopoDS_Vertex& theLast, - NCollection_DynamicArray& theInternal) +BuildCounts captureBuildCounts(const BRepGraphInc_Storage& theStorage) +{ + BuildCounts aCounts; + aCounts.NbVertices = theStorage.NbVertices(); + aCounts.NbEdges = theStorage.NbEdges(); + aCounts.NbWires = theStorage.NbWires(); + aCounts.NbFaces = theStorage.NbFaces(); + aCounts.NbShells = theStorage.NbShells(); + aCounts.NbSolids = theStorage.NbSolids(); + aCounts.NbCompounds = theStorage.NbCompounds(); + aCounts.NbCompSolids = theStorage.NbCompSolids(); + aCounts.NbCoEdges = theStorage.NbCoEdges(); + aCounts.NbChildRefs = theStorage.NbChildRefs(); + aCounts.NbSolidRefs = theStorage.NbSolidRefs(); + aCounts.NbFaceRefs = theStorage.NbFaceRefs(); + aCounts.NbShellRefs = theStorage.NbShellRefs(); + aCounts.NbWireRefs = theStorage.NbWireRefs(); + return aCounts; +} + +static BRepGraph_VertexRefId appendEdgeVertexRef(BuildContext& theBuild, + const ExtractedVertex& theVertex, + const TopAbs_Orientation theOrientation, + const BRepGraph_EdgeId theParentEdgeId) +{ + if (theVertex.Shape.IsNull()) + { + return BRepGraph_VertexRefId(); + } + + const BRepGraph_VertexId aVertexId = registerOrReuseVertex(theBuild, + theVertex.Shape, + theVertex.Point, + theVertex.Tolerance, + theVertex.BakedLocation, + theVertex.IsGenerated); + return appendVertexRef(theBuild.Storage, aVertexId, theOrientation, theParentEdgeId); +} + +BRepGraph_EdgeId registerEdge(BuildContext& theBuild, + const ExtractedEdge& theEdgeData, + const occ::handle& theSupplementLayer) +{ + if (theEdgeData.IsGenerated) + { + // Generated edges are not deduplicated; each gets its own representation. + } + const BRepGraph_NodeId anExisting = findExistingNode(theBuild, + theEdgeData.Shape, + BRepGraph_NodeId::Kind::Edge, + theEdgeData.BakedLocation); + if (anExisting.IsValid()) + { + const BRepGraph_EdgeId anEdgeId(anExisting); + BRepGraphInc::EdgeDef& anEdgeEnt = theBuild.Storage.ChangeEdge(anEdgeId); + if (!anEdgeEnt.Polygon3DRepId.IsValid() && !theEdgeData.Polygon3D.IsNull()) + { + const BRepGraph_EdgePolygon3DRepId aRepId = theBuild.Storage.AppendEdgePolygon3DRep(); + theBuild.Storage.ChangeEdgePolygon3DRep(aRepId).Polygon = theEdgeData.Polygon3D; + anEdgeEnt.Polygon3DRepId = aRepId; + if (anEdgeEnt.Polygon3DRepId.IsValid()) + { + theBuild.Storage.ChangeEdgePolygon3DRep(anEdgeEnt.Polygon3DRepId).ParentEdgeId = anEdgeId; + } + } + return anEdgeId; + } + + const BRepGraph_EdgeId anEdgeId = theBuild.Storage.AppendEdge(); + BRepGraphInc::EdgeDef& anEdgeEnt = theBuild.Storage.ChangeEdge(anEdgeId); + anEdgeEnt.Tolerance = theEdgeData.Tolerance; + + if (!theEdgeData.Curve3d.IsNull()) + { + const BRepGraph_EdgeCurve3DRepId aRepId = theBuild.Storage.AppendEdgeCurve3DRep(); + theBuild.Storage.ChangeEdgeCurve3DRep(aRepId).Curve = theEdgeData.Curve3d; + anEdgeEnt.Curve3DRepId = aRepId; + if (anEdgeEnt.Curve3DRepId.IsValid()) + { + BRepGraphInc::EdgeCurve3DRep& aUse = + theBuild.Storage.ChangeEdgeCurve3DRep(anEdgeEnt.Curve3DRepId); + aUse.ParentEdgeId = anEdgeId; + aUse.ParamFirst = theEdgeData.ParamFirst; + aUse.ParamLast = theEdgeData.ParamLast; + } + } + anEdgeEnt.StartVertexRefId = + appendEdgeVertexRef(theBuild, theEdgeData.StartVertex, TopAbs_FORWARD, anEdgeId); + anEdgeEnt.EndVertexRefId = + appendEdgeVertexRef(theBuild, theEdgeData.EndVertex, TopAbs_REVERSED, anEdgeId); + const auto anAppendVertexRelation = [&](const BRepGraph_VertexRefId theRefId) { + if (!theRefId.IsValid()) + { + return; + } + const BRepGraph_VertexId aVertexId = theBuild.Storage.VertexRef(theRefId).ChildVertexId; + if (aVertexId.IsValid()) + { + theBuild.Storage.AttachEdgeToVertex(anEdgeId, aVertexId); + } + }; + anAppendVertexRelation(anEdgeEnt.StartVertexRefId); + anAppendVertexRelation(anEdgeEnt.EndVertexRefId); + if (!theEdgeData.Polygon3D.IsNull()) + { + const BRepGraph_EdgePolygon3DRepId aPolyRepId = theBuild.Storage.AppendEdgePolygon3DRep(); + theBuild.Storage.ChangeEdgePolygon3DRep(aPolyRepId).Polygon = theEdgeData.Polygon3D; + anEdgeEnt.Polygon3DRepId = aPolyRepId; + if (anEdgeEnt.Polygon3DRepId.IsValid()) + { + theBuild.Storage.ChangeEdgePolygon3DRep(anEdgeEnt.Polygon3DRepId).ParentEdgeId = anEdgeId; + } + } + + if (!theEdgeData.IsGenerated) + { + bindLocatedNode(theBuild, theEdgeData.Shape, anEdgeId, theEdgeData.BakedLocation, true); + theBuild.Storage.BindOriginal(anEdgeId, theEdgeData.Shape); + attachSupplement(theSupplementLayer, anEdgeId, theEdgeData.Shape); + } + return anEdgeId; +} + +BRepGraph_WireId appendWireDef(BuildContext& theBuild, + const TopoDS_Wire& theWire, + const bool theBindTShape, + const TopLoc_Location& theBakedLocation, + const bool theIsGenerated = false) +{ + const BRepGraph_WireId aWireId = theBuild.Storage.AppendWire(); + if (!theIsGenerated && theBindTShape) + { + bindLocatedNode(theBuild, theWire, aWireId, theBakedLocation, true); + } + if (!theIsGenerated) + { + theBuild.Storage.BindOriginal(aWireId, theWire); + } + return aWireId; +} + +void appendFaceCoEdge(BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theWireId, + const BRepGraph_FaceId theFaceId, + const ExtractedEdge& theEdgeData, + const BRepGraph_EdgeId theEdgeId) +{ + const BRepGraph_CoEdgeId aCoEdgeId = + theStorage.CreateCoEdgeUse(theWireId, theEdgeId, theFaceId, theEdgeData.OrientationInWire); + BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.ChangeCoEdge(aCoEdgeId); + if (!theEdgeData.PolyOnSurf.IsNull()) + { + const BRepGraph_CoEdgePolygon2DRepId aRepId = theStorage.AppendCoEdgePolygon2DRep(); + theStorage.ChangeCoEdgePolygon2DRep(aRepId).Polygon = theEdgeData.PolyOnSurf; + aCoEdge.Polygon2DRepId = aRepId; + if (aCoEdge.Polygon2DRepId.IsValid()) + { + theStorage.ChangeCoEdgePolygon2DRep(aCoEdge.Polygon2DRepId).ParentCoEdgeId = aCoEdgeId; + } + } + + if (!theEdgeData.PCurve2d.IsNull()) + { + const BRepGraph_CoEdgeCurve2DRepId aCurveRepId = theStorage.AppendCoEdgeCurve2DRep(); + theStorage.ChangeCoEdgeCurve2DRep(aCurveRepId).Curve = theEdgeData.PCurve2d; + aCoEdge.Curve2DRepId = aCurveRepId; + if (aCoEdge.Curve2DRepId.IsValid()) + { + BRepGraphInc::CoEdgeCurve2DRep& aUse = + theStorage.ChangeCoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + aUse.ParentCoEdgeId = aCoEdgeId; + aUse.ParamFirst = theEdgeData.PCFirst; + aUse.ParamLast = theEdgeData.PCLast; + } + } + + if (!theEdgeData.PolyOnTri.IsNull()) + { + const BRepGraph_CoEdgePolygonOnTriRepId aTriRepId = theStorage.AppendCoEdgePolygonOnTriRep(); + theStorage.ChangeCoEdgePolygonOnTriRep(aTriRepId).Polygon = theEdgeData.PolyOnTri; + aCoEdge.PolygonOnTriRepId = aTriRepId; + if (aCoEdge.PolygonOnTriRepId.IsValid()) + { + theStorage.ChangeCoEdgePolygonOnTriRep(aCoEdge.PolygonOnTriRepId).ParentCoEdgeId = aCoEdgeId; + } + } +} + +void appendWireCoEdge(BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theWireId, + const BRepGraph_EdgeId theEdgeId, + const TopAbs_Orientation theOrientation) +{ + theStorage.CreateCoEdgeUse(theWireId, theEdgeId, BRepGraph_FaceId(), theOrientation); +} + +static void edgeVertices(const TopoDS_Edge& theEdge, + TopoDS_Vertex& theFirst, + TopoDS_Vertex& theLast) { for (TopoDS_Iterator aVIt(theEdge, false, false); aVIt.More(); aVIt.Next()) { @@ -855,199 +913,249 @@ static void edgeVertices(const TopoDS_Edge& theE const TopoDS_Vertex aVertex = TopoDS::Vertex(aVIt.Value()); if (aVertex.Orientation() == TopAbs_FORWARD) { - if (!theFirst.IsNull()) - { - // Preserve previous FORWARD vertex in internal list. - ExtractedInternalVertex& anIntVtx = theInternal.Appended(); - anIntVtx.Shape = theFirst; - anIntVtx.Point = rawVertexPoint(theFirst); - anIntVtx.Tolerance = BRep_Tool::Tolerance(theFirst); - anIntVtx.Orientation = TopAbs_FORWARD; - } theFirst = aVertex; } else if (aVertex.Orientation() == TopAbs_REVERSED) { - if (!theLast.IsNull()) - { - // Preserve previous REVERSED vertex in internal list. - ExtractedInternalVertex& anIntVtx = theInternal.Appended(); - anIntVtx.Shape = theLast; - anIntVtx.Point = rawVertexPoint(theLast); - anIntVtx.Tolerance = BRep_Tool::Tolerance(theLast); - anIntVtx.Orientation = TopAbs_REVERSED; - } theLast = aVertex; } - else - { - ExtractedInternalVertex& anIntVtx = theInternal.Appended(); - anIntVtx.Shape = aVertex; - anIntVtx.Point = rawVertexPoint(aVertex); - anIntVtx.Tolerance = BRep_Tool::Tolerance(aVertex); - anIntVtx.Orientation = aVertex.Orientation(); - } } - // Note: do NOT copy theFirst<->theLast when one is null. - // Single-vertex edges (e.g., infinite edges) legitimately have only one vertex. - // Closed edges already have both FORWARD and REVERSED vertices in the iterator. } -//! Extract edge geometry and parametric data in a face context. -//! Fills theEdgeData with 3D curve, vertices, PCurves, and polygons. -static void extractEdgeInFace(ExtractedEdge& theEdgeData, - const TopoDS_Edge& theEdge, - const TopoDS_Face& theForwardFace, - const occ::handle& theFaceSurface, - const occ::handle& theOrigSurface) +static void extractEdgeDefinition(ExtractedEdge& theEdgeData, + const TopoDS_Edge& theEdge, + const TopLoc_Location& theParentLocation) { theEdgeData.Shape = theEdge; theEdgeData.Tolerance = BRep_Tool::Tolerance(theEdge); - theEdgeData.IsDegenerate = BRep_Tool::Degenerated(theEdge); - theEdgeData.SameParameter = BRep_Tool::SameParameter(theEdge); - theEdgeData.SameRange = BRep_Tool::SameRange(theEdge); theEdgeData.OrientationInWire = theEdge.Orientation(); + theEdgeData.BakedLocation = theParentLocation * theEdge.Location(); - // 3D curve with representation location applied to definition frame. { double aFirst = 0.0, aLast = 0.0; TopLoc_Location aCurveCombinedLoc; theEdgeData.Curve3d = BRep_Tool::Curve(theEdge, aCurveCombinedLoc, aFirst, aLast); theEdgeData.ParamFirst = aFirst; theEdgeData.ParamLast = aLast; - theEdgeData.Curve3d = applyRepresentationLocation(theEdgeData.Curve3d, - theEdge.Location(), - aCurveCombinedLoc); + theEdgeData.Curve3d = + applyBakedLocation(theEdgeData.Curve3d, theParentLocation * aCurveCombinedLoc); } - // Vertices: use FORWARD-oriented edge for orientation-independent extraction. TopoDS_Vertex aVFirst, aVLast; + edgeVertices(TopoDS::Edge(theEdge.Oriented(TopAbs_FORWARD)), aVFirst, aVLast); + gp_Pnt aVertexPoint; + if (!aVFirst.IsNull() && rawVertexPoint(aVFirst, aVertexPoint)) { - const TopoDS_Edge aFwdEdge = TopoDS::Edge(theEdge.Oriented(TopAbs_FORWARD)); - edgeVertices(aFwdEdge, aVFirst, aVLast, theEdgeData.InternalVertices); - } - - if (!aVFirst.IsNull()) - { - theEdgeData.StartVertex.Shape = aVFirst; - theEdgeData.StartVertex.Point = rawVertexPoint(aVFirst); + theEdgeData.StartVertex.Shape = aVFirst; + theEdgeData.StartVertex.BakedLocation = theEdgeData.BakedLocation * aVFirst.Location(); + theEdgeData.StartVertex.Point = + applyBakedLocation(aVertexPoint, theEdgeData.StartVertex.BakedLocation); theEdgeData.StartVertex.Tolerance = BRep_Tool::Tolerance(aVFirst); } - if (!aVLast.IsNull()) + if (!aVLast.IsNull() && rawVertexPoint(aVLast, aVertexPoint)) { - theEdgeData.EndVertex.Shape = aVLast; - theEdgeData.EndVertex.Point = rawVertexPoint(aVLast); + theEdgeData.EndVertex.Shape = aVLast; + theEdgeData.EndVertex.BakedLocation = theEdgeData.BakedLocation * aVLast.Location(); + theEdgeData.EndVertex.Point = + applyBakedLocation(aVertexPoint, theEdgeData.EndVertex.BakedLocation); theEdgeData.EndVertex.Tolerance = BRep_Tool::Tolerance(aVLast); } - // Extract this yield's PCurve directly from BRep_TEdge::Curves(), bypassing - // BRep_Tool::CurveOnSurface which can fail due to TopLoc_Location structural - // equality bug and can compute phantom PCurves via CurveOnPlane. - // The input theEdge carries the iterator's natural orientation, so the - // extractor returns the matching PCurve (PCurve() for FORWARD, PCurve2() for - // REVERSED on a closed surface). The opposite half is the *other* yield's - // ExtractedEdge - we don't need to fetch it here. - { - double aPCFirst = 0.0, aPCLast = 0.0; - extractStoredPCurves(theEdge, - theForwardFace, - theEdgeData.PCurve2d, - aPCFirst, - aPCLast, - theOrigSurface); - - theEdgeData.PCFirst = aPCFirst; - theEdgeData.PCLast = aPCLast; - - // When the surface was transformed (TFace.Location != Identity -> theFaceSurface - // differs from the raw TFace surface), the stored CR may belong to a different face - // context using the same raw surface. Verify by calling BRep_Tool::CurveOnSurface - // which correctly handles CurveOnPlane for planar surfaces and properly composes - // face+edge locations. If BRep_Tool COMPUTED a PCurve (not stored) AND it differs - // from what we extracted, our stored-only extraction picked a CR from the wrong - // context. Discard ours so reconstruction doesn't attach the wrong PCurve. - if (!theEdgeData.PCurve2d.IsNull() && theFaceSurface != theOrigSurface) - { - double aBTFirst = 0.0, aBTLast = 0.0; - bool aBTIsStored = false; - occ::handle aBTPCurve = - BRep_Tool::CurveOnSurface(theEdge, theForwardFace, aBTFirst, aBTLast, &aBTIsStored); - if (!aBTPCurve.IsNull() && !aBTIsStored && aBTPCurve.get() != theEdgeData.PCurve2d.get()) - { - theEdgeData.PCurve2d.Nullify(); - theEdgeData.PCFirst = 0.0; - theEdgeData.PCLast = 0.0; - } - } - - if (!theEdgeData.PCurve2d.IsNull() && !theFaceSurface.IsNull()) - { - BRep_Tool::UVPoints(theEdge, theForwardFace, theEdgeData.PCUV1, theEdgeData.PCUV2); - } - } - - // Polygon3D with representation location applied. - { - TopLoc_Location aPoly3DLoc; - theEdgeData.Polygon3D = BRep_Tool::Polygon3D(theEdge, aPoly3DLoc); - theEdgeData.Polygon3D = - applyRepLocationToPolygon3D(theEdgeData.Polygon3D, theEdge.Location(), aPoly3DLoc); - } - - // PolygonOnSurface: fetch with theEdge's orientation so seam halves yield distinct polygons. - theEdgeData.PolyOnSurf = BRep_Tool::PolygonOnSurface(theEdge, theForwardFace); + TopLoc_Location aPoly3DLoc; + theEdgeData.Polygon3D = BRep_Tool::Polygon3D(theEdge, aPoly3DLoc); + theEdgeData.Polygon3D = + applyBakedLocationToPolygon3D(theEdgeData.Polygon3D, theParentLocation * aPoly3DLoc); } -//! Extract per-face geometry/topology data from TopoDS. -void extractFaceData(FaceLocalData& theData) +static void extractEdgeInFace(ExtractedEdge& theEdgeData, + const TopoDS_Edge& theEdge, + const TopoDS_Face& theForwardFace, + const occ::handle& theTriangulation, + const FacePCurveContext& thePCurveContext, + const TopLoc_Location& theParentLocation, + const bool theIsGenerated = false) { - const TopoDS_Face& aFace = theData.Face; + extractEdgeDefinition(theEdgeData, theEdge, theParentLocation); + theEdgeData.IsGenerated = theIsGenerated; + theEdgeData.StartVertex.IsGenerated = theIsGenerated; + theEdgeData.EndVertex.IsGenerated = theIsGenerated; + extractStoredPCurve(theEdge, + thePCurveContext, + theEdgeData.PCurve2d, + theEdgeData.PCFirst, + theEdgeData.PCLast); - // Extract surface with representation location applied to definition frame. + theEdgeData.PolyOnSurf = BRep_Tool::PolygonOnSurface(theEdge, theForwardFace); + if (!theTriangulation.IsNull()) { - TopLoc_Location aSurfCombinedLoc; - theData.Surface = BRep_Tool::Surface(aFace, aSurfCombinedLoc); - theData.OriginalSurface = theData.Surface; // save pre-transform handle for PCurve matching - theData.Surface = applyRepresentationLocation(theData.Surface, - aFace.Location(), - aSurfCombinedLoc); + TopLoc_Location aPolyTriLoc; + theEdgeData.PolyOnTri = + BRep_Tool::PolygonOnTriangulation(theEdge, theTriangulation, aPolyTriLoc); + } +} + +struct OrderedExtractedEdge +{ + uint32_t EdgeIndex = 0; +}; + +uint32_t findExtractedEdgeByExplorerEdge(const ExtractedWire& theWireData, + const BRepGraphInc_BitFlags& theUsed, + const TopoDS_Edge& theExplorerEdge, + const bool theMatchOrientation) +{ + const uint32_t aNbEdges = static_cast(theWireData.Edges.Size()); + for (uint32_t anIdx = 0; anIdx < aNbEdges; ++anIdx) + { + if (theUsed.Test(anIdx)) + { + continue; + } + + const ExtractedEdge& anEdgeData = theWireData.Edges.Value(static_cast(anIdx)); + if (!anEdgeData.Shape.IsSame(theExplorerEdge)) + { + continue; + } + if (theMatchOrientation && anEdgeData.OrientationInWire != theExplorerEdge.Orientation()) + { + continue; + } + return anIdx; + } + return aNbEdges; +} + +bool orderExtractedWire(ExtractedWire& theWireData, const TopoDS_Face* theFace) +{ + const uint32_t aNbEdges = static_cast(theWireData.Edges.Size()); + if (aNbEdges < 2) + { + return true; } - // Extract active triangulation only. + BRepTools_WireExplorer anExplorer; + if (theFace != nullptr && !theFace->IsNull()) { - TopLoc_Location aDummyLoc; - theData.ActiveTriangulation = BRep_Tool::Triangulation(aFace, aDummyLoc); + anExplorer.Init(theWireData.Shape, *theFace); + } + else + { + anExplorer.Init(theWireData.Shape); } - theData.Tolerance = BRep_Tool::Tolerance(aFace); - theData.Orientation = aFace.Orientation(); - theData.NaturalRestriction = BRep_Tool::NaturalRestriction(aFace); + BRepGraphInc_BitFlags aUsed; + aUsed.Resize(aNbEdges); + + NCollection_LinearVector anOrder(aNbEdges); + for (; anExplorer.More(); anExplorer.Next()) + { + const TopoDS_Edge& anExplorerEdge = anExplorer.Current(); + uint32_t aMatchedIdx = + findExtractedEdgeByExplorerEdge(theWireData, aUsed, anExplorerEdge, true); + if (aMatchedIdx == aNbEdges) + { + aMatchedIdx = findExtractedEdgeByExplorerEdge(theWireData, aUsed, anExplorerEdge, false); + } + if (aMatchedIdx == aNbEdges) + { + continue; + } + + aUsed.Set(aMatchedIdx); + OrderedExtractedEdge anOrdered; + anOrdered.EdgeIndex = aMatchedIdx; + anOrder.Append(anOrdered); + } + + if (anOrder.IsEmpty()) + { + return false; + } + + const bool isCompleteExplorerOrder = anOrder.Size() == aNbEdges; + + for (uint32_t anIdx = 0; anIdx < aNbEdges; ++anIdx) + { + if (aUsed.Test(anIdx)) + { + continue; + } + + OrderedExtractedEdge anOrdered; + anOrdered.EdgeIndex = anIdx; + anOrder.Append(anOrdered); + } + + if (anOrder.Size() != aNbEdges) + { + return false; + } + + NCollection_DynamicArray aReordered(THE_EDGE_BUCKET); + for (const OrderedExtractedEdge& anOrdered : anOrder) + { + ExtractedEdge& anEdgeData = aReordered.Appended(); + anEdgeData = theWireData.Edges.Value(static_cast(anOrdered.EdgeIndex)); + } + theWireData.Edges = std::move(aReordered); + return isCompleteExplorerOrder; +} + +static bool hasInfiniteRequiredBound(const occ::handle& theSurface) +{ + double aU1 = 0.0; + double aU2 = 0.0; + double aV1 = 0.0; + double aV2 = 0.0; + theSurface->Bounds(aU1, aU2, aV1, aV2); + + if (!theSurface->IsUPeriodic() && (Precision::IsInfinite(aU1) || Precision::IsInfinite(aU2))) + { + return true; + } + if (!theSurface->IsVPeriodic() && (Precision::IsInfinite(aV1) || Precision::IsInfinite(aV2))) + { + return true; + } + return false; +} + +void extractFaceData(FaceBuildData& theData) +{ + const TopoDS_Face& aFace = theData.Face; + const TopLoc_Location aFaceParentLocation = theData.BakedLocation * aFace.Location().Inverted(); + + TopLoc_Location aSurfCombinedLoc; + occ::handle aRawSurface = BRep_Tool::Surface(aFace, aSurfCombinedLoc); + theData.Surface = + applyBakedLocation(aRawSurface, aFaceParentLocation * aSurfCombinedLoc); + + TopLoc_Location aTriangulationLoc; + theData.RawTriangulation = BRep_Tool::Triangulation(aFace, aTriangulationLoc); + theData.ActiveTriangulation = + applyBakedLocationToTriangulation(theData.RawTriangulation, + aFaceParentLocation * aTriangulationLoc); + + theData.Tolerance = BRep_Tool::Tolerance(aFace); const TopoDS_Face aForwardFace = TopoDS::Face(aFace.Oriented(TopAbs_FORWARD)); - const TopoDS_Wire anOuterWire = BRepTools::OuterWire(aForwardFace); + FacePCurveContext aPCurveContext; + aPCurveContext.RawSurface = aRawSurface; + aPCurveContext.SurfaceLocation = aSurfCombinedLoc; for (TopoDS_Iterator aChildIt(aForwardFace, false, false); aChildIt.More(); aChildIt.Next()) { const TopoDS_Shape& aChild = aChildIt.Value(); - if (aChild.ShapeType() == TopAbs_VERTEX) - { - const TopoDS_Vertex& aVertex = TopoDS::Vertex(aChild); - ExtractedInternalVertex& aVtxData = theData.DirectVertices.Appended(); - aVtxData.Shape = aVertex; - aVtxData.Point = rawVertexPoint(aVertex); - aVtxData.Tolerance = BRep_Tool::Tolerance(aVertex); - aVtxData.Orientation = aVertex.Orientation(); - continue; - } if (aChild.ShapeType() != TopAbs_WIRE) { continue; } const TopoDS_Wire& aWire = TopoDS::Wire(aChild); - ExtractedWire aWireData; - aWireData.Shape = aWire; - aWireData.IsOuter = aWire.IsSame(anOuterWire); + ExtractedWire& aWireData = theData.Wires.Appended(); + aWireData.Shape = aWire; + aWireData.BakedLocation = theData.BakedLocation * aWire.Location(); for (TopoDS_Iterator anEdgeIt(aWire, false, false); anEdgeIt.More(); anEdgeIt.Next()) { @@ -1060,1257 +1168,670 @@ void extractFaceData(FaceLocalData& theData) extractEdgeInFace(anEdgeData, TopoDS::Edge(anEdgeShape), aForwardFace, - theData.Surface, - theData.OriginalSurface); + theData.RawTriangulation, + aPCurveContext, + aWireData.BakedLocation); } - - theData.Wires.Append(std::move(aWireData)); + aWireData.HasPartialExplorerOrder = + !orderExtractedWire(aWireData, aRawSurface.IsNull() ? nullptr : &aForwardFace); } -} -//! Register pre-extracted face data into incidence storage. -//! Uses unified TShapeToNodeId map and populates OriginalShapes. -void registerFaceData(BRepGraphInc_Storage& theStorage, - const NCollection_DynamicArray& theFaceData, - RepDedup& theRepDedup) -{ - for (const FaceLocalData& aData : theFaceData) + if (theData.Wires.IsEmpty() && BRep_Tool::NaturalRestriction(aFace)) { - const TopoDS_Face& aCurFace = aData.Face; - - // Create or reuse FaceDef. - const BRepGraph_NodeId* anExistingFace = - findExistingNode(theStorage, aCurFace, BRepGraph_NodeId::Kind::Face); - - BRepGraph_FaceId aFaceId; - bool aIsNewFaceDef = false; - if (anExistingFace != nullptr) + if (theData.Surface.IsNull() || hasInfiniteRequiredBound(theData.Surface)) { - aFaceId = BRepGraph_FaceId(*anExistingFace); + theData.UnsupportedNaturalBoundary = true; } else { - aIsNewFaceDef = true; - aFaceId = theStorage.AppendFace(); - BRepGraphInc::FaceDef& aFace = theStorage.ChangeFace(aFaceId); - aFace.Tolerance = aData.Tolerance; - aFace.NaturalRestriction = aData.NaturalRestriction; - aFace.SurfaceRepId = getOrCreateSurfaceRep(theStorage, theRepDedup, aData.Surface); + theData.NeedsSynthesis = true; + } + } +} - if (!aData.ActiveTriangulation.IsNull()) +void registerFaceData(BuildContext& theBuild, + const NCollection_DynamicArray& theFaceData, + const occ::handle& theSupplementLayer) +{ + BRepGraphInc_Storage& theStorage = theBuild.Storage; + for (const FaceBuildData& aData : theFaceData) + { + const BRepGraph_FaceId aFaceId = aData.FaceId; + BRepGraphInc::FaceDef& aFace = theStorage.ChangeFace(aFaceId); + aFace.Tolerance = aData.Tolerance; + if (!aData.Surface.IsNull()) + { + const BRepGraph_FaceSurfaceRepId aRepId = theStorage.AppendFaceSurfaceRep(); + theStorage.ChangeFaceSurfaceRep(aRepId).Surface = aData.Surface; + aFace.SurfaceRepId = aRepId; + if (aFace.SurfaceRepId.IsValid()) { - aFace.TriangulationRepId = - getOrCreateTriangulationRep(theStorage, theRepDedup, aData.ActiveTriangulation); + theStorage.ChangeFaceSurfaceRep(aFace.SurfaceRepId).ParentFaceId = aFaceId; } - - theStorage.BindTShapeToNode(aCurFace.TShape().get(), aFaceId); - theStorage.BindOriginal(aFaceId, aCurFace); } - // Link face to parent shell (with per-instance location for shared TShapes). - if (aData.ParentShellId.IsValid()) + if (!aData.ActiveTriangulation.IsNull()) { - BRepGraphInc::FaceInstance aRef; - aRef.DefId = aFaceId; - aRef.Orientation = aData.Orientation; - aRef.Location = aCurFace.Location(); - appendFaceRef(theStorage, aData.ParentShellId, aRef); + const BRepGraph_FaceTriangulationRepId aTriRepId = theStorage.AppendFaceTriangulationRep(); + theStorage.ChangeFaceTriangulationRep(aTriRepId).Triangulation = aData.ActiveTriangulation; + aFace.TriangulationRepId = aTriRepId; + if (aFace.TriangulationRepId.IsValid()) + { + theStorage.ChangeFaceTriangulationRep(aFace.TriangulationRepId).ParentFaceId = aFaceId; + } } - - // Pre-fetch face entity for triangulation access in edge loop. - BRepGraphInc::FaceDef& aFaceMut = theStorage.ChangeFace(aFaceId); - const BRepGraphInc::FaceDef& aFaceDef = aFaceMut; - - // Process wires - only for newly created face definitions. - // Shared faces (same TShape referenced multiple times in a shell) must NOT - // duplicate wire/edge/coedge data on the single FaceDef. - if (!aIsNewFaceDef) - { - continue; - } - for (const ExtractedWire& aWireData : aData.Wires) { - - // Dedup wire by TShape. - const BRepGraph_NodeId* anExistingWire = - findExistingNode(theStorage, aWireData.Shape, BRepGraph_NodeId::Kind::Wire); - - BRepGraph_WireId aWireId; - bool aIsNewWireDef = false; - - if (anExistingWire != nullptr) - { - aWireId = BRepGraph_WireId(*anExistingWire); - } - else - { - aWireId = theStorage.AppendWire(); - theStorage.BindTShapeToNode(aWireData.Shape.TShape().get(), aWireId); - theStorage.BindOriginal(aWireId, aWireData.Shape); - aIsNewWireDef = true; - } - - // Link wire to face. - { - BRepGraphInc::WireInstance aWireRef; - aWireRef.DefId = aWireId; - aWireRef.Orientation = aWireData.Shape.Orientation(); - aWireRef.Location = aWireData.Shape.Location(); - aWireRef.IsOuter = aWireData.IsOuter; - appendWireRef(theStorage, aFaceId, aWireRef); - } + const BRepGraph_WireId aWireId = appendWireDef(theBuild, + aWireData.Shape, + false, + aWireData.BakedLocation, + aWireData.IsGenerated); + appendWireRef(theStorage, aFaceId, aWireId, aWireData.Shape.Orientation()); for (const ExtractedEdge& anEdgeData : aWireData.Edges) { - const BRepGraph_EdgeId anEdgeIdx = - registerExtractedEdge(theStorage, anEdgeData, theRepDedup); - - // Cache mutable edge reference for subsequent PCurve/Polygon appends. - // Safe: no new edges are appended within this scope. - BRepGraphInc::EdgeDef& anEdgeMut = theStorage.ChangeEdge(anEdgeIdx); - - // One TopoDS_Iterator yield <-> one CoEdge <-> one CoEdgeRef. Seam edges - // arrive as two yields with opposite orientations at their natural - // positions in the wire's loop. - BRepGraph_CoEdgeId aCoEdgeId; - if (aIsNewWireDef) - { - aCoEdgeId = theStorage.AppendCoEdge(); - BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.ChangeCoEdge(aCoEdgeId); - aCoEdge.EdgeDefId = anEdgeIdx; - aCoEdge.FaceDefId = aFaceId; - aCoEdge.Orientation = anEdgeData.OrientationInWire; - - if (!anEdgeData.PCurve2d.IsNull()) - { - aCoEdge.Curve2DRepId = - getOrCreateCurve2DRep(theStorage, theRepDedup, anEdgeData.PCurve2d); - aCoEdge.ParamFirst = anEdgeData.PCFirst; - aCoEdge.ParamLast = anEdgeData.PCLast; - aCoEdge.UV1 = anEdgeData.PCUV1; - aCoEdge.UV2 = anEdgeData.PCUV2; - } - aCoEdge.Polygon2DRepId = - getOrCreatePolygon2DRep(theStorage, theRepDedup, anEdgeData.PolyOnSurf); - - BRepGraphInc::CoEdgeInstance aRef; - aRef.DefId = aCoEdgeId; - aRef.Location = anEdgeData.Shape.Location(); - appendCoEdgeRef(theStorage, aWireId, aRef); - } - - // Polygon3D (once per edge). - if (!anEdgeData.Polygon3D.IsNull() && !anEdgeMut.Polygon3DRepId.IsValid()) - { - anEdgeMut.Polygon3DRepId = - getOrCreatePolygon3DRep(theStorage, theRepDedup, anEdgeData.Polygon3D); - } - - // Polygon-on-triangulation: fetch with this yield's edge orientation, - // so seam halves naturally produce their own polygon (or share when stored once). - if (aCoEdgeId.IsValid() && aFaceDef.TriangulationRepId.IsValid()) - { - const occ::handle& aTri = - theStorage.TriangulationRep(aFaceDef.TriangulationRepId).Triangulation; - if (!aTri.IsNull()) - { - TopLoc_Location aPolyTriLoc; - occ::handle aPolyOnTri = - BRep_Tool::PolygonOnTriangulation(anEdgeData.Shape, aTri, aPolyTriLoc); - if (!aPolyOnTri.IsNull()) - { - BRepGraph_TriangulationRepId aTriRepId = aFaceDef.TriangulationRepId; - if (!aPolyTriLoc.IsIdentity()) - { - const TopLoc_Location aRepLoc = - anEdgeData.Shape.Location().Inverted() * aPolyTriLoc; - if (!aRepLoc.IsIdentity()) - { - occ::handle aTriCopy = aTri->Copy(); - const gp_Trsf& aTrsf = aRepLoc.Transformation(); - for (int aNodeIdx = 1; aNodeIdx <= aTriCopy->NbNodes(); ++aNodeIdx) - { - aTriCopy->SetNode(aNodeIdx, aTriCopy->Node(aNodeIdx).Transformed(aTrsf)); - } - aTriRepId = getOrCreateTriangulationRep(theStorage, theRepDedup, aTriCopy); - aFaceMut.TriangulationRepId = aTriRepId; - } - } - - const BRepGraph_PolygonOnTriRepId aPolyOnTriRepId = - getOrCreatePolygonOnTriRep(theStorage, theRepDedup, aPolyOnTri, aTriRepId); - theStorage.ChangeCoEdge(aCoEdgeId).PolygonOnTriRepId = aPolyOnTriRepId; - } - } - } - } - - // Wire closure: copy directly from the original shape. - if (aIsNewWireDef) - { - theStorage.ChangeWire(aWireId).IsClosed = aWireData.Shape.Closed(); + const BRepGraph_EdgeId anEdgeId = registerEdge(theBuild, anEdgeData, theSupplementLayer); + appendFaceCoEdge(theStorage, aWireId, aFaceId, anEdgeData, anEdgeId); } + theBuild.HasWireOrderWarnings = + theBuild.HasWireOrderWarnings || aWireData.HasPartialExplorerOrder; + recordWireOrderStatus(theBuild, theStorage.CanonicalizeWireCoEdgeOrderStatus(aWireId)); } + } +} - // Register direct vertex children of the face (INTERNAL/EXTERNAL). - for (const ExtractedInternalVertex& aDirVtx : aData.DirectVertices) +void synthesizeFaceBoundaries(BuildContext& theBuild, + NCollection_DynamicArray& theFaceData) +{ + BRepGraphInc_Storage& theStorage = theBuild.Storage; + for (FaceBuildData& aData : theFaceData) + { + if (!aData.NeedsSynthesis) { - const BRepGraph_VertexId aVtxId = - registerOrReuseVertex(theStorage, aDirVtx.Shape, aDirVtx.Point, aDirVtx.Tolerance); - if (aVtxId.IsValid()) - { - BRepGraphInc::VertexInstance aVR; - aVR.DefId = aVtxId; - aVR.Orientation = aDirVtx.Orientation; - aVR.Location = aDirVtx.Shape.Location(); - theStorage.ChangeFace(aFaceId).VertexRefIds.Append( - appendVertexRef(theStorage, aFaceId, aVR)); - } - } - } -} - -//! Recursively traverse a TopoDS hierarchy, registering container entities -//! (Compound, CompSolid, Solid, Shell) and collecting face contexts into theFaceData. -//! Used by Perform() for Phase 1. -void traverseHierarchy(BRepGraphInc_Storage& theStorage, - NCollection_DynamicArray& theFaceData, - RepDedup& theRepDedup, - const TopoDS_Shape& theCurrentShape, - const TopLoc_Location& theParentGlobalLoc) -{ - if (theCurrentShape.IsNull()) - { - return; - } - - switch (theCurrentShape.ShapeType()) - { - case TopAbs_COMPOUND: { - const TopoDS_Compound& aCompound = TopoDS::Compound(theCurrentShape); - if (findExistingNode(theStorage, aCompound, BRepGraph_NodeId::Kind::Compound)) - { - break; - } - - const BRepGraph_CompoundId aCompId = theStorage.AppendCompound(); - theStorage.BindTShapeToNode(aCompound.TShape().get(), aCompId); - theStorage.BindOriginal(aCompId, aCompound); - - const TopLoc_Location aGlobalLoc = theParentGlobalLoc * aCompound.Location(); - - for (TopoDS_Iterator aChildIt(aCompound, false, false); aChildIt.More(); aChildIt.Next()) - { - const TopoDS_Shape& aChild = aChildIt.Value(); - BRepGraph_NodeId::Kind aChildKind = shapeTypeToNodeKind(aChild.ShapeType()); - - traverseHierarchy(theStorage, theFaceData, theRepDedup, aChild, aGlobalLoc); - - if (aChild.ShapeType() != TopAbs_SHAPE) - { - // Resolve child index via TShape lookup (handles dedup correctly). - // Face indices are deferred (invalid) because faces are registered in Phase 3; - // resolved in the Phase 3a fixup pass after registerFaceData(). - uint32_t aChildIdx = BRepGraph_NodeId::THE_INVALID_INDEX; - if (aChild.ShapeType() != TopAbs_FACE) - { - const BRepGraph_NodeId* aChildNodeId = - theStorage.FindNodeByTShape(aChild.TShape().get()); - if (aChildNodeId != nullptr) - { - aChildIdx = aChildNodeId->Index; - } - } - - BRepGraphInc::NodeInstance aRef; - aRef.DefId = BRepGraph_NodeId(aChildKind, aChildIdx); - aRef.Orientation = aChild.Orientation(); - aRef.Location = aChild.Location(); - appendChildRef(theStorage, aCompId, aRef); - } - } - break; + continue; } - case TopAbs_COMPSOLID: { - const TopoDS_CompSolid& aCompSolid = TopoDS::CompSolid(theCurrentShape); - if (findExistingNode(theStorage, aCompSolid, BRepGraph_NodeId::Kind::CompSolid)) - { - break; - } + const occ::handle& aSurf = aData.Surface; + const double aTol = std::max(aData.Tolerance, Precision::Confusion()); - const BRepGraph_CompSolidId aCSolidId = theStorage.AppendCompSolid(); - theStorage.BindTShapeToNode(aCompSolid.TShape().get(), aCSolidId); - theStorage.BindOriginal(aCSolidId, aCompSolid); + double aU1 = 0.0; + double aU2 = 0.0; + double aV1 = 0.0; + double aV2 = 0.0; + aSurf->Bounds(aU1, aU2, aV1, aV2); - const TopLoc_Location aGlobalLoc = theParentGlobalLoc * aCompSolid.Location(); + // For planes PCurves are trivially derivable from the 3D curve. + // Check if surface is a plane or a trimmed surface with a plane basis. + const bool isPlane = aSurf->IsKind(STANDARD_TYPE(Geom_Plane)) + || (aSurf->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)) + && static_cast(aSurf.get()) + ->BasisSurface() + ->IsKind(STANDARD_TYPE(Geom_Plane))); - for (TopoDS_Iterator aChildIt(aCompSolid, false, false); aChildIt.More(); aChildIt.Next()) - { - if (aChildIt.Value().ShapeType() != TopAbs_SOLID) - { - continue; - } - traverseHierarchy(theStorage, theFaceData, theRepDedup, aChildIt.Value(), aGlobalLoc); - - const BRepGraph_NodeId* aSolidNodeId = - theStorage.FindNodeByTShape(aChildIt.Value().TShape().get()); - if (aSolidNodeId == nullptr) - { - continue; - } - - BRepGraphInc::SolidInstance aRef; - aRef.DefId = BRepGraph_SolidId::FromNodeId(*aSolidNodeId); - aRef.Orientation = aChildIt.Value().Orientation(); - aRef.Location = aChildIt.Value().Location(); - appendSolidRef(theStorage, aCSolidId, aRef); - } - break; - } - - case TopAbs_SOLID: { - const TopoDS_Solid& aSolid = TopoDS::Solid(theCurrentShape); - if (findExistingNode(theStorage, aSolid, BRepGraph_NodeId::Kind::Solid)) - { - break; - } - - const BRepGraph_SolidId aSolidId = theStorage.AppendSolid(); - theStorage.BindTShapeToNode(aSolid.TShape().get(), aSolidId); - theStorage.BindOriginal(aSolidId, aSolid); - - const TopLoc_Location aGlobalLoc = theParentGlobalLoc * aSolid.Location(); - - for (TopoDS_Iterator aChildIt(aSolid, false, false); aChildIt.More(); aChildIt.Next()) - { - const TopoDS_Shape& aChild = aChildIt.Value(); - traverseHierarchy(theStorage, theFaceData, theRepDedup, aChild, aGlobalLoc); - - if (aChild.ShapeType() == TopAbs_SHELL) - { - const BRepGraph_NodeId* aShellNodeId = theStorage.FindNodeByTShape(aChild.TShape().get()); - if (aShellNodeId == nullptr) - { - continue; - } - - BRepGraphInc::ShellInstance aRef; - aRef.DefId = BRepGraph_ShellId::FromNodeId(*aShellNodeId); - aRef.Orientation = aChild.Orientation(); - aRef.Location = aChild.Location(); - appendShellRef(theStorage, aSolidId, aRef); - } - else if (aChild.ShapeType() == TopAbs_EDGE || aChild.ShapeType() == TopAbs_VERTEX) - { - BRepGraphInc::NodeInstance aCR; - if (makeAuxChildRef(theStorage, aChild, aCR)) - { - const BRepGraph_ChildRefId aChildRefId = appendChildRef(theStorage, aSolidId, aCR); - theStorage.ChangeSolid(aSolidId).AuxChildRefIds.Append(aChildRefId); - } - } - } - break; - } - - case TopAbs_SHELL: { - const TopoDS_Shell& aShell = TopoDS::Shell(theCurrentShape); - if (findExistingNode(theStorage, aShell, BRepGraph_NodeId::Kind::Shell)) - { - break; - } - - const BRepGraph_ShellId aShellId = theStorage.AppendShell(); - BRepGraphInc::ShellDef& aShellEnt = theStorage.ChangeShell(aShellId); - aShellEnt.IsClosed = aShell.Closed(); - theStorage.BindTShapeToNode(aShell.TShape().get(), aShellId); - theStorage.BindOriginal(aShellId, aShell); - - const TopLoc_Location aGlobalLoc = theParentGlobalLoc * aShell.Location(); - - for (TopoDS_Iterator aChildIt(aShell, false, false); aChildIt.More(); aChildIt.Next()) - { - const TopoDS_Shape& aChild = aChildIt.Value(); - if (aChild.ShapeType() == TopAbs_FACE) - { - FaceLocalData& aData = theFaceData.Appended(); - aData.Face = TopoDS::Face(aChild); - aData.ParentGlobalLoc = aGlobalLoc; - aData.ParentShellId = aShellId; - } - else if (aChild.ShapeType() == TopAbs_WIRE || aChild.ShapeType() == TopAbs_EDGE) - { - traverseHierarchy(theStorage, theFaceData, theRepDedup, aChild, aGlobalLoc); - - BRepGraphInc::NodeInstance aCR; - if (makeAuxChildRef(theStorage, aChild, aCR)) - { - const BRepGraph_ChildRefId aChildRefId = appendChildRef(theStorage, aShellId, aCR); - theStorage.ChangeShell(aShellId).AuxChildRefIds.Append(aChildRefId); - } - } - } - break; - } - - case TopAbs_FACE: { - FaceLocalData& aData = theFaceData.Appended(); - aData.Face = TopoDS::Face(theCurrentShape); - aData.ParentGlobalLoc = theParentGlobalLoc; - break; - } - - case TopAbs_WIRE: { - const TopoDS_Wire& aWire = TopoDS::Wire(theCurrentShape); - if (findExistingNode(theStorage, aWire, BRepGraph_NodeId::Kind::Wire)) - { - break; - } - - const BRepGraph_WireId aWireId = theStorage.AppendWire(); - BRepGraphInc::WireDef& aWireEnt = theStorage.ChangeWire(aWireId); - aWireEnt.IsClosed = aWire.Closed(); - theStorage.BindTShapeToNode(aWire.TShape().get(), aWireId); - theStorage.BindOriginal(aWireId, aWire); - - for (TopoDS_Iterator anEdgeIt(aWire, false, false); anEdgeIt.More(); anEdgeIt.Next()) - { - if (anEdgeIt.Value().ShapeType() != TopAbs_EDGE) - { - continue; - } - const TopoDS_Edge& anEdge = TopoDS::Edge(anEdgeIt.Value()); - - // Recurse to create the edge entity (with dedup). - traverseHierarchy(theStorage, theFaceData, theRepDedup, anEdge, theParentGlobalLoc); - - // Resolve edge index via TShape lookup (handles dedup correctly). - const BRepGraph_NodeId* anEdgeNodeId = theStorage.FindNodeByTShape(anEdge.TShape().get()); - if (anEdgeNodeId != nullptr && anEdgeNodeId->NodeKind == BRepGraph_NodeId::Kind::Edge) - { - // Create CoEdge for free wire (no face context). - const BRepGraph_CoEdgeId aCoEdgeId = theStorage.AppendCoEdge(); - BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.ChangeCoEdge(aCoEdgeId); - aCoEdge.EdgeDefId = BRepGraph_EdgeId::FromNodeId(*anEdgeNodeId); - aCoEdge.Orientation = anEdge.Orientation(); - // FaceDefId left invalid for free wires. - // Curve2d left null for free wires. - - BRepGraphInc::CoEdgeInstance aCoEdgeRef; - aCoEdgeRef.DefId = aCoEdgeId; - aCoEdgeRef.Location = anEdge.Location(); - appendCoEdgeRef(theStorage, aWireId, aCoEdgeRef); - } - } - break; - } - - case TopAbs_EDGE: { - const TopoDS_Edge& anEdge = TopoDS::Edge(theCurrentShape); - if (findExistingNode(theStorage, anEdge, BRepGraph_NodeId::Kind::Edge)) - { - break; - } - - const BRepGraph_EdgeId anEdgeId = theStorage.AppendEdge(); - BRepGraphInc::EdgeDef& anEdgeEnt = theStorage.ChangeEdge(anEdgeId); - anEdgeEnt.Tolerance = BRep_Tool::Tolerance(anEdge); - anEdgeEnt.IsDegenerate = BRep_Tool::Degenerated(anEdge); - anEdgeEnt.SameParameter = BRep_Tool::SameParameter(anEdge); - anEdgeEnt.SameRange = BRep_Tool::SameRange(anEdge); - anEdgeEnt.IsClosed = anEdge.Closed(); - - // Extract 3D curve with representation location applied to definition frame. - { - double aFirst = 0.0, aLast = 0.0; - TopLoc_Location aCurveCombinedLoc; - occ::handle aCurve3d = - BRep_Tool::Curve(anEdge, aCurveCombinedLoc, aFirst, aLast); - anEdgeEnt.ParamFirst = aFirst; - anEdgeEnt.ParamLast = aLast; - aCurve3d = - applyRepresentationLocation(aCurve3d, anEdge.Location(), aCurveCombinedLoc); - anEdgeEnt.Curve3DRepId = getOrCreateCurve3DRep(theStorage, theRepDedup, aCurve3d); - } - - // Extract vertices. - TopoDS_Vertex aVFirst, aVLast; - NCollection_DynamicArray anInternalVerts; - edgeVertices(anEdge, aVFirst, aVLast, anInternalVerts); - - // Register vertices (using definition-frame points; Location stored on VertexRef). - // Vertices may be null for infinite edges or degenerate topology. - if (!aVFirst.IsNull()) - { - BRepGraphInc::VertexInstance aVR; - aVR.DefId = registerOrReuseVertex(theStorage, - aVFirst, - rawVertexPoint(aVFirst), - BRep_Tool::Tolerance(aVFirst)); - aVR.Orientation = TopAbs_FORWARD; - aVR.Location = aVFirst.Location(); - anEdgeEnt.StartVertexRefId = appendVertexRef(theStorage, anEdgeId, aVR); - } - if (!aVLast.IsNull()) - { - BRepGraphInc::VertexInstance aVR; - aVR.DefId = registerOrReuseVertex(theStorage, - aVLast, - rawVertexPoint(aVLast), - BRep_Tool::Tolerance(aVLast)); - aVR.Orientation = TopAbs_REVERSED; - aVR.Location = aVLast.Location(); - anEdgeEnt.EndVertexRefId = appendVertexRef(theStorage, anEdgeId, aVR); - } - - for (const ExtractedInternalVertex& anIntVtx : anInternalVerts) - { - const BRepGraph_VertexId anIntVtxId = - registerOrReuseVertex(theStorage, anIntVtx.Shape, anIntVtx.Point, anIntVtx.Tolerance); - if (anIntVtxId.IsValid()) - { - BRepGraphInc::VertexInstance aVR; - aVR.DefId = anIntVtxId; - aVR.Orientation = anIntVtx.Orientation; - aVR.Location = anIntVtx.Shape.Location(); - anEdgeEnt.InternalVertexRefIds.Append(appendVertexRef(theStorage, anEdgeId, aVR)); - } - } - - // Polygon3D: apply representation location for consistency. - { - TopLoc_Location aPoly3DLoc; - occ::handle aPolygon3D = BRep_Tool::Polygon3D(anEdge, aPoly3DLoc); - aPolygon3D = applyRepLocationToPolygon3D(aPolygon3D, anEdge.Location(), aPoly3DLoc); - anEdgeEnt.Polygon3DRepId = getOrCreatePolygon3DRep(theStorage, theRepDedup, aPolygon3D); - } - - theStorage.BindTShapeToNode(anEdge.TShape().get(), anEdgeId); - theStorage.BindOriginal(anEdgeId, anEdge); - break; - } - - case TopAbs_VERTEX: { - registerOrReuseVertex(theStorage, TopoDS::Vertex(theCurrentShape)); - break; - } - - default: - break; - } -} - -//! Append a root NodeId to the vector, skipping duplicates. -static void appendUniqueRootNode(NCollection_DynamicArray& theRoots, - const BRepGraph_NodeId& theNodeId) -{ - if (!theNodeId.IsValid()) - { - return; - } - - for (const BRepGraph_NodeId& aRoot : theRoots) - { - if (aRoot == theNodeId) + BRepGraphInc_BoundaryBuilder::SurfaceBoundary aBoundary; + if (!BRepGraphInc_BoundaryBuilder::BuildSurfaceBoundary(aBoundary, + aSurf, + aU1, + aU2, + aV1, + aV2, + aTol)) { - return; + continue; } + + NCollection_LinearVector aVertices; + aVertices.Reserve(aBoundary.Vertices.Size()); + for (const BRepGraphInc_BoundaryBuilder::SurfaceBoundaryVertex& aBoundaryVertex : + aBoundary.Vertices) + { + const BRepGraph_VertexId aVtxId = theStorage.AppendVertex(); + BRepGraphInc::VertexDef& aVtxEnt = theStorage.ChangeVertex(aVtxId); + aVtxEnt.Point = aBoundaryVertex.Point; + aVtxEnt.Tolerance = aTol; + aVertices.Append(aVtxId); + } + + NCollection_LinearVector aEdges; + aEdges.Reserve(aBoundary.Edges.Size()); + for (const BRepGraphInc_BoundaryBuilder::SurfaceBoundaryEdge& aBoundaryEdge : aBoundary.Edges) + { + BRepGraph_EdgeCurve3DRepId aCurveRepId; + if (!aBoundaryEdge.Curve3D.IsNull()) + { + aCurveRepId = theStorage.AppendEdgeCurve3DRep(); + theStorage.ChangeEdgeCurve3DRep(aCurveRepId).Curve = aBoundaryEdge.Curve3D; + } + + const BRepGraph_VertexId aStartVertex = aVertices.Value(aBoundaryEdge.StartVertex); + const BRepGraph_VertexId anEndVertex = aVertices.Value(aBoundaryEdge.EndVertex); + const BRepGraph_EdgeId anEdgeId = theStorage.AppendEdge(); + BRepGraphInc::EdgeDef& anEdgeEnt = theStorage.ChangeEdge(anEdgeId); + anEdgeEnt.Curve3DRepId = aCurveRepId; + anEdgeEnt.Tolerance = aTol; + if (anEdgeEnt.Curve3DRepId.IsValid()) + { + BRepGraphInc::EdgeCurve3DRep& aUse = + theStorage.ChangeEdgeCurve3DRep(anEdgeEnt.Curve3DRepId); + aUse.ParentEdgeId = anEdgeId; + aUse.ParamFirst = aBoundaryEdge.First; + aUse.ParamLast = aBoundaryEdge.Last; + } + + anEdgeEnt.StartVertexRefId = + appendVertexRef(theStorage, aStartVertex, TopAbs_FORWARD, anEdgeId); + anEdgeEnt.EndVertexRefId = + appendVertexRef(theStorage, anEndVertex, TopAbs_REVERSED, anEdgeId); + + theStorage.AttachEdgeToVertex(anEdgeId, aStartVertex); + if (aStartVertex != anEndVertex) + { + theStorage.AttachEdgeToVertex(anEdgeId, anEndVertex); + } + aEdges.Append(anEdgeId); + } + + // Create wire. + const BRepGraph_WireId aWireId = theStorage.AppendWire(); + + // Create CoEdges and attach to wire. + for (const BRepGraphInc_BoundaryBuilder::SurfaceBoundaryCoEdge& aBoundaryCoEdge : + aBoundary.CoEdges) + { + const BRepGraph_CoEdgeId aCoEdgeId = + theStorage.CreateCoEdgeUse(aWireId, + aEdges.Value(aBoundaryCoEdge.EdgeIndex), + aData.FaceId, + aBoundaryCoEdge.Orientation); + BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.ChangeCoEdge(aCoEdgeId); + + if (!isPlane && !aBoundaryCoEdge.PCurve.IsNull()) + { + const BRepGraph_CoEdgeCurve2DRepId aPCurveRepId = theStorage.AppendCoEdgeCurve2DRep(); + theStorage.ChangeCoEdgeCurve2DRep(aPCurveRepId).Curve = aBoundaryCoEdge.PCurve; + aCoEdge.Curve2DRepId = aPCurveRepId; + if (aCoEdge.Curve2DRepId.IsValid()) + { + BRepGraphInc::CoEdgeCurve2DRep& aUse = + theStorage.ChangeCoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + aUse.ParentCoEdgeId = aCoEdgeId; + aUse.ParamFirst = aBoundaryCoEdge.First; + aUse.ParamLast = aBoundaryCoEdge.Last; + } + } + } + + // Attach wire to face. + recordWireOrderStatus(theBuild, theStorage.CanonicalizeWireCoEdgeOrderStatus(aWireId)); + theStorage.AttachWireToFace(aData.FaceId, aWireId, TopAbs_FORWARD); } - theRoots.Append(theNodeId); } -//! Flatten hierarchy containers away for AppendFlattened(). -//! Face roots are collected for the parallel face pipeline; standalone -//! wire/edge/vertex roots are registered directly through traverseHierarchy(). -void flattenForAppend(BRepGraphInc_Storage& theStorage, - NCollection_DynamicArray& theFaceData, - RepDedup& theRepDedup, - const TopoDS_Shape& theCurrentShape, - const TopLoc_Location& theParentGlobalLoc, - NCollection_DynamicArray* theAppendedRoots) +void appendBuildRoot(BuildContext& theBuild, const BRepGraph_NodeId theNodeId) { - if (theCurrentShape.IsNull()) + if (theBuild.AppendedRoots != nullptr && theNodeId.IsValid() + && theBuild.AppendedRootSet.Add(theNodeId)) { - return; + theBuild.AppendedRoots->Append(theNodeId); + } +} + +BRepGraph_NodeId enqueueFace(BuildContext& theBuild, + const TopoDS_Face& theFace, + const TopLoc_Location& theParentLocation) +{ + const TopLoc_Location aBakedLocation = theParentLocation * theFace.Location(); + const BRepGraph_NodeId anExisting = + findExistingNode(theBuild, theFace, BRepGraph_NodeId::Kind::Face, aBakedLocation); + if (anExisting.IsValid()) + { + return anExisting; } + const BRepGraph_FaceId aFaceId = theBuild.Storage.AppendFace(); + bindLocatedNode(theBuild, theFace, aFaceId, aBakedLocation, true); + theBuild.Storage.BindOriginal(aFaceId, theFace); + attachSupplement(theBuild.SupplementLayer, aFaceId, theFace); + + FaceBuildData& aFaceData = theBuild.PendingFaces.Appended(); + aFaceData.Face = theFace; + aFaceData.FaceId = aFaceId; + aFaceData.BakedLocation = aBakedLocation; + return BRepGraph_NodeId(aFaceId); +} + +BRepGraph_NodeId traverseTopology(BuildContext& theBuild, + const TopoDS_Shape& theCurrentShape, + const TopLoc_Location& theParentLocation, + const RootRole theRootRole = RootRole::Nested); + +BRepGraph_NodeId traverseCompound(BuildContext& theBuild, + const TopoDS_Compound& theCompound, + const RootRole theRootRole) +{ + const TopLoc_Location aDefinitionLocation = + theRootRole == RootRole::Root ? theCompound.Location() : TopLoc_Location(); + const BRepGraph_NodeId anExisting = + findExistingNode(theBuild, theCompound, BRepGraph_NodeId::Kind::Compound, aDefinitionLocation); + if (anExisting.IsValid()) + { + return anExisting; + } + + const BRepGraph_CompoundId aCompId = theBuild.Storage.AppendCompound(); + bindLocatedNode(theBuild, theCompound, aCompId, aDefinitionLocation, true); + theBuild.Storage.BindOriginal(aCompId, theCompound); + + const TopLoc_Location aChildRootLocation = aDefinitionLocation; + + for (TopoDS_Iterator aChildIt(theCompound, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + const TopAbs_Orientation aChildOri = aChild.Orientation(); + if (!isForwardChildType(TopAbs_COMPOUND, aChild.ShapeType()) + || !isCoreParityOrientation(aChildOri)) + { + attachNonCoreContainerChild(theBuild, BRepGraph_NodeId(aCompId), TopAbs_COMPOUND, aChild); + continue; + } + const BRepGraph_NodeId aChildNode = + traverseTopology(theBuild, aChild, TopLoc_Location(), RootRole::Nested); + if (aChildNode.IsValid()) + { + appendChildRef(theBuild.Storage, + aCompId, + aChildNode, + aChildOri, + aChildRootLocation * aChild.Location()); + } + } + return BRepGraph_NodeId(aCompId); +} + +BRepGraph_NodeId traverseCompSolid(BuildContext& theBuild, + const TopoDS_CompSolid& theCompSolid, + const TopLoc_Location& theParentLocation) +{ + const TopLoc_Location aBakedLocation = theParentLocation * theCompSolid.Location(); + const BRepGraph_NodeId anExisting = + findExistingNode(theBuild, theCompSolid, BRepGraph_NodeId::Kind::CompSolid, aBakedLocation); + if (anExisting.IsValid()) + { + return anExisting; + } + + const BRepGraph_CompSolidId aCSolidId = theBuild.Storage.AppendCompSolid(); + bindLocatedNode(theBuild, theCompSolid, aCSolidId, aBakedLocation, true); + theBuild.Storage.BindOriginal(aCSolidId, theCompSolid); + + for (TopoDS_Iterator aChildIt(theCompSolid, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + const TopAbs_Orientation aChildOri = aChild.Orientation(); + if (!isForwardChildType(TopAbs_COMPSOLID, aChild.ShapeType()) + || !isCoreParityOrientation(aChildOri)) + { + attachNonCoreContainerChild(theBuild, BRepGraph_NodeId(aCSolidId), TopAbs_COMPSOLID, aChild); + continue; + } + + const BRepGraph_NodeId aChildNode = traverseTopology(theBuild, aChild, aBakedLocation); + if (!aChildNode.IsValid() || aChildNode.NodeKind != BRepGraph_NodeId::Kind::Solid) + { + continue; + } + + appendSolidRef(theBuild.Storage, + aCSolidId, + BRepGraph_SolidId::FromNodeId(aChildNode), + aChildOri); + } + return BRepGraph_NodeId(aCSolidId); +} + +BRepGraph_NodeId traverseSolid(BuildContext& theBuild, + const TopoDS_Solid& theSolid, + const TopLoc_Location& theParentLocation) +{ + const TopLoc_Location aBakedLocation = theParentLocation * theSolid.Location(); + const BRepGraph_NodeId anExisting = + findExistingNode(theBuild, theSolid, BRepGraph_NodeId::Kind::Solid, aBakedLocation); + if (anExisting.IsValid()) + { + return anExisting; + } + + const BRepGraph_SolidId aSolidId = theBuild.Storage.AppendSolid(); + bindLocatedNode(theBuild, theSolid, aSolidId, aBakedLocation, true); + theBuild.Storage.BindOriginal(aSolidId, theSolid); + + for (TopoDS_Iterator aChildIt(theSolid, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + const TopAbs_Orientation aChildOri = aChild.Orientation(); + if (!isForwardChildType(TopAbs_SOLID, aChild.ShapeType()) + || !isCoreParityOrientation(aChildOri)) + { + attachNonCoreContainerChild(theBuild, BRepGraph_NodeId(aSolidId), TopAbs_SOLID, aChild); + continue; + } + + const BRepGraph_NodeId aChildNode = traverseTopology(theBuild, aChild, aBakedLocation); + if (!aChildNode.IsValid() || aChildNode.NodeKind != BRepGraph_NodeId::Kind::Shell) + { + continue; + } + + appendShellRef(theBuild.Storage, + aSolidId, + BRepGraph_ShellId::FromNodeId(aChildNode), + aChildOri); + } + return BRepGraph_NodeId(aSolidId); +} + +BRepGraph_NodeId traverseShell(BuildContext& theBuild, + const TopoDS_Shell& theShell, + const TopLoc_Location& theParentLocation) +{ + const TopLoc_Location aBakedLocation = theParentLocation * theShell.Location(); + const BRepGraph_NodeId anExisting = + findExistingNode(theBuild, theShell, BRepGraph_NodeId::Kind::Shell, aBakedLocation); + if (anExisting.IsValid()) + { + return anExisting; + } + + const BRepGraph_ShellId aShellId = theBuild.Storage.AppendShell(); + bindLocatedNode(theBuild, theShell, aShellId, aBakedLocation, true); + theBuild.Storage.BindOriginal(aShellId, theShell); + + for (TopoDS_Iterator aChildIt(theShell, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + const TopAbs_Orientation aChildOri = aChild.Orientation(); + if (!isForwardChildType(TopAbs_SHELL, aChild.ShapeType()) + || !isCoreParityOrientation(aChildOri)) + { + attachNonCoreContainerChild(theBuild, BRepGraph_NodeId(aShellId), TopAbs_SHELL, aChild); + continue; + } + + const TopoDS_Face aFace = TopoDS::Face(aChild); + const BRepGraph_NodeId aFaceNode = enqueueFace(theBuild, aFace, aBakedLocation); + appendFaceRef(theBuild.Storage, aShellId, BRepGraph_FaceId::FromNodeId(aFaceNode), aChildOri); + } + return BRepGraph_NodeId(aShellId); +} + +BRepGraph_NodeId traverseWire(BuildContext& theBuild, + const TopoDS_Wire& theWire, + const TopLoc_Location& theParentLocation) +{ + const TopLoc_Location aBakedLocation = theParentLocation * theWire.Location(); + const BRepGraph_NodeId anExisting = + findExistingNode(theBuild, theWire, BRepGraph_NodeId::Kind::Wire, aBakedLocation); + if (anExisting.IsValid()) + { + return anExisting; + } + + const BRepGraph_WireId aWireId = appendWireDef(theBuild, theWire, true, aBakedLocation); + ExtractedWire aWireData; + aWireData.Shape = theWire; + aWireData.BakedLocation = aBakedLocation; + + for (TopoDS_Iterator anEdgeIt(theWire, false, false); anEdgeIt.More(); anEdgeIt.Next()) + { + const TopoDS_Shape& anEdgeShape = anEdgeIt.Value(); + if (!isForwardChildType(TopAbs_WIRE, anEdgeShape.ShapeType())) + { + continue; + } + + ExtractedEdge& anEdgeData = aWireData.Edges.Appended(); + extractEdgeDefinition(anEdgeData, TopoDS::Edge(anEdgeShape), aBakedLocation); + } + + theBuild.HasWireOrderWarnings = + theBuild.HasWireOrderWarnings || !orderExtractedWire(aWireData, nullptr); + for (const ExtractedEdge& anEdgeData : aWireData.Edges) + { + const BRepGraph_EdgeId anEdgeId = registerEdge(theBuild, anEdgeData, theBuild.SupplementLayer); + if (!anEdgeId.IsValid()) + { + continue; + } + + appendWireCoEdge(theBuild.Storage, aWireId, anEdgeId, anEdgeData.OrientationInWire); + } + recordWireOrderStatus(theBuild, theBuild.Storage.CanonicalizeWireCoEdgeOrderStatus(aWireId)); + return BRepGraph_NodeId(aWireId); +} + +BRepGraph_NodeId traverseEdge(BuildContext& theBuild, + const TopoDS_Edge& theEdge, + const TopLoc_Location& theParentLocation) +{ + const TopLoc_Location aBakedLocation = theParentLocation * theEdge.Location(); + const BRepGraph_NodeId anExisting = + findExistingNode(theBuild, theEdge, BRepGraph_NodeId::Kind::Edge, aBakedLocation); + if (anExisting.IsValid()) + { + return anExisting; + } + + ExtractedEdge anEdgeData; + extractEdgeDefinition(anEdgeData, theEdge, theParentLocation); + return BRepGraph_NodeId(registerEdge(theBuild, anEdgeData, theBuild.SupplementLayer)); +} + +BRepGraph_NodeId registerTopology(BuildContext& theBuild, + const TopoDS_Shape& theCurrentShape, + const TopLoc_Location& theParentLocation, + const RootRole theRootRole) +{ switch (theCurrentShape.ShapeType()) { case TopAbs_COMPOUND: + return traverseCompound(theBuild, TopoDS::Compound(theCurrentShape), theRootRole); case TopAbs_COMPSOLID: + return traverseCompSolid(theBuild, TopoDS::CompSolid(theCurrentShape), theParentLocation); case TopAbs_SOLID: - case TopAbs_SHELL: { - for (TopoDS_Iterator aChildIt(theCurrentShape, false, false); aChildIt.More(); - aChildIt.Next()) - { - flattenForAppend(theStorage, - theFaceData, - theRepDedup, - aChildIt.Value(), - theParentGlobalLoc * theCurrentShape.Location(), - theAppendedRoots); - } - break; - } - case TopAbs_FACE: { - FaceLocalData& aData = theFaceData.Appended(); - aData.Face = TopoDS::Face(theCurrentShape); - aData.ParentGlobalLoc = theParentGlobalLoc; - break; - } + return traverseSolid(theBuild, TopoDS::Solid(theCurrentShape), theParentLocation); + case TopAbs_SHELL: + return traverseShell(theBuild, TopoDS::Shell(theCurrentShape), theParentLocation); + case TopAbs_FACE: + return enqueueFace(theBuild, TopoDS::Face(theCurrentShape), theParentLocation); case TopAbs_WIRE: + return traverseWire(theBuild, TopoDS::Wire(theCurrentShape), theParentLocation); case TopAbs_EDGE: - case TopAbs_VERTEX: { - traverseHierarchy(theStorage, theFaceData, theRepDedup, theCurrentShape, theParentGlobalLoc); - if (theAppendedRoots != nullptr) - { - const BRepGraph_NodeId* aNodeId = - theStorage.FindNodeByTShape(theCurrentShape.TShape().get()); - if (aNodeId != nullptr) - { - appendUniqueRootNode(*theAppendedRoots, *aNodeId); - } - } - break; - } + return traverseEdge(theBuild, TopoDS::Edge(theCurrentShape), theParentLocation); + case TopAbs_VERTEX: + return BRepGraph_NodeId( + registerOrReuseVertex(theBuild, + TopoDS::Vertex(theCurrentShape), + theParentLocation * theCurrentShape.Location())); default: - break; + return BRepGraph_NodeId(); } } -//================================================================================================= - -void populateRegularityLayer(BRepGraphInc_Storage& theStorage, - const occ::handle& theRegularityLayer, - const bool theExtractRegularities, - const uint32_t theOldNbEdges, - const occ::handle& theTmpAlloc) +void traverseFlattenedChildren(BuildContext& theBuild, + const TopoDS_Shape& theContainer, + const TopLoc_Location& theParentLocation) { - if (theRegularityLayer.IsNull()) + const TopAbs_ShapeEnum aContainerType = theContainer.ShapeType(); + const TopLoc_Location aContainerLocation = theParentLocation * theContainer.Location(); + for (TopoDS_Iterator aChildIt(theContainer, false, false); aChildIt.More(); aChildIt.Next()) { - return; - } - - if (theOldNbEdges == 0) - { - theRegularityLayer->Clear(); - } - if (!theExtractRegularities) - { - return; - } - - // Surface-to-face map covers all faces (new edges may reference old faces). - NCollection_DataMap aSurfToFaceIdx(1, theTmpAlloc); - const uint32_t aNbFaces = theStorage.NbFaces(); - for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); aFaceId.IsValid(aNbFaces); ++aFaceId) - { - const TopoDS_Shape* anOrigFace = theStorage.FindOriginal(aFaceId); - if (anOrigFace == nullptr || anOrigFace->IsNull()) + const TopoDS_Shape& aChild = aChildIt.Value(); + if (isForwardChildType(aContainerType, aChild.ShapeType())) { - continue; - } - - TopLoc_Location aLoc; - occ::handle aRawSurf = BRep_Tool::Surface(TopoDS::Face(*anOrigFace), aLoc); - if (!aRawSurf.IsNull()) - { - aSurfToFaceIdx.TryBind(aRawSurf.get(), aFaceId); - } - } - - // Only process new edges in incremental mode. - const uint32_t aNbEdges = theStorage.NbEdges(); - for (BRepGraph_EdgeId anEdgeId(theOldNbEdges); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const TopoDS_Shape* anOrigShape = theStorage.FindOriginal(anEdgeId); - if (anOrigShape == nullptr || anOrigShape->IsNull()) - { - continue; - } - - const TopoDS_Edge& anEdge = TopoDS::Edge(*anOrigShape); - const occ::handle aTEdge = occ::down_cast(anEdge.TShape()); - if (aTEdge.IsNull()) - { - continue; - } - - for (const occ::handle& aCRep : aTEdge->Curves()) - { - if (aCRep.IsNull()) - { - continue; - } - - // Inter-face regularity: BRep_CurveOn2Surfaces stores G^k between F1 and F2. - if (const occ::handle aCon2S = - occ::down_cast(aCRep); - !aCon2S.IsNull()) - { - const Geom_Surface* aSurf1Ptr = aCon2S->Surface().get(); - const Geom_Surface* aSurf2Ptr = aCon2S->Surface2().get(); - if (aSurf1Ptr == nullptr || aSurf2Ptr == nullptr) - { - continue; - } - const BRepGraph_FaceId* aFaceIdx1 = aSurfToFaceIdx.Seek(aSurf1Ptr); - const BRepGraph_FaceId* aFaceIdx2 = aSurfToFaceIdx.Seek(aSurf2Ptr); - if (aFaceIdx1 == nullptr || aFaceIdx2 == nullptr) - { - continue; - } - theRegularityLayer->SetRegularity(anEdgeId, *aFaceIdx1, *aFaceIdx2, aCon2S->Continuity()); - continue; - } - - // Seam regularity: BRep_CurveOnClosedSurface owns the (PCurve, PCurve2) - // pair on a single closed surface; record continuity with F1 == F2. - if (aCRep->IsCurveOnClosedSurface()) - { - const Geom_Surface* aSurfPtr = aCRep->Surface().get(); - if (aSurfPtr == nullptr) - { - continue; - } - const BRepGraph_FaceId* aFaceIdx = aSurfToFaceIdx.Seek(aSurfPtr); - if (aFaceIdx == nullptr) - { - continue; - } - theRegularityLayer->SetRegularity(anEdgeId, *aFaceIdx, *aFaceIdx, aCRep->Continuity()); - } + traverseTopology(theBuild, aChild, aContainerLocation); } } } -//================================================================================================= - -void populateParamLayer(BRepGraphInc_Storage& theStorage, - const occ::handle& theParamLayer, - const bool theExtractVertexPointReps, - const uint32_t theOldNbVertices, - const occ::handle& theTmpAlloc) +BRepGraph_NodeId traverseTopology(BuildContext& theBuild, + const TopoDS_Shape& theCurrentShape, + const TopLoc_Location& theParentLocation, + const RootRole theRootRole) { - if (theParamLayer.IsNull()) + if (theCurrentShape.IsNull()) { - return; + return BRepGraph_NodeId(); } - if (theOldNbVertices == 0) + const TopAbs_ShapeEnum aShapeType = theCurrentShape.ShapeType(); + if (theBuild.Mode == TopologyBuildMode::Flattened + && (aShapeType == TopAbs_COMPOUND || aShapeType == TopAbs_COMPSOLID + || aShapeType == TopAbs_SOLID || aShapeType == TopAbs_SHELL)) { - theParamLayer->Clear(); - } - if (!theExtractVertexPointReps) - { - return; + traverseFlattenedChildren(theBuild, theCurrentShape, theParentLocation); + return BRepGraph_NodeId(); } - NCollection_DataMap aCurveToEdgeDef(1, theTmpAlloc); - const uint32_t aNbEdges = theStorage.NbEdges(); - for (BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); anEdgeId.IsValid(aNbEdges); - ++anEdgeId) + const BRepGraph_NodeId aNode = + registerTopology(theBuild, theCurrentShape, theParentLocation, theRootRole); + if (theBuild.Mode == TopologyBuildMode::Flattened) { - const TopoDS_Shape* anOrigEdge = theStorage.FindOriginal(anEdgeId); - if (anOrigEdge == nullptr || anOrigEdge->IsNull()) - { - continue; - } - - double aFirst = 0.0; - double aLast = 0.0; - TopLoc_Location aLoc; - occ::handle aRawCurve = - BRep_Tool::Curve(TopoDS::Edge(*anOrigEdge), aLoc, aFirst, aLast); - if (!aRawCurve.IsNull()) - { - aCurveToEdgeDef.TryBind(aRawCurve.get(), anEdgeId); - } - } - - NCollection_DataMap aSurfToFaceDef(1, theTmpAlloc); - NCollection_DynamicArray aFaceRawSurfaces(1, theTmpAlloc); - const uint32_t aNbFaces = theStorage.NbFaces(); - for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); aFaceId.IsValid(aNbFaces); ++aFaceId) - { - const TopoDS_Shape* anOrigFace = theStorage.FindOriginal(aFaceId); - const Geom_Surface* aRawSurfPtr = nullptr; - if (anOrigFace != nullptr && !anOrigFace->IsNull()) - { - TopLoc_Location aLoc; - occ::handle aRawSurf = BRep_Tool::Surface(TopoDS::Face(*anOrigFace), aLoc); - if (!aRawSurf.IsNull()) - { - aSurfToFaceDef.TryBind(aRawSurf.get(), aFaceId); - aRawSurfPtr = aRawSurf.get(); - } - } - aFaceRawSurfaces.Append(aRawSurfPtr); - } - - NCollection_DataMap> - aPCurveToCoEdges(1, theTmpAlloc); - const uint32_t aNbCoEdges = theStorage.NbCoEdges(); - for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aNbCoEdges); ++aCoEdgeId) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aCoEdgeId); - if (!aCoEdge.Curve2DRepId.IsValid() || !aCoEdge.FaceDefId.IsValid(theStorage.NbFaces())) - { - continue; - } - - const occ::handle& aPCurve = theStorage.Curve2DRep(aCoEdge.Curve2DRepId).Curve; - if (aPCurve.IsNull()) - { - continue; - } - - NCollection_DynamicArray* aCoEdges = - aPCurveToCoEdges.ChangeSeek(aPCurve.get()); - if (aCoEdges == nullptr) - { - NCollection_DynamicArray aNewCoEdges(1, theTmpAlloc); - aNewCoEdges.Append(aCoEdgeId); - aPCurveToCoEdges.Bind(aPCurve.get(), aNewCoEdges); - } - else - { - aCoEdges->Append(aCoEdgeId); - } - } - - // Only process new vertices in incremental mode. - const uint32_t aNbVertices = theStorage.NbVertices(); - for (BRepGraph_VertexId aVertexId(theOldNbVertices); aVertexId.IsValid(aNbVertices); ++aVertexId) - { - const TopoDS_Shape* aVtxShape = theStorage.FindOriginal(aVertexId); - if (aVtxShape == nullptr || aVtxShape->IsNull()) - { - continue; - } - - const TopoDS_Vertex& aVertex = TopoDS::Vertex(*aVtxShape); - const occ::handle& aTVertex = occ::down_cast(aVertex.TShape()); - if (aTVertex.IsNull()) - { - continue; - } - - for (const occ::handle& aPtRep : aTVertex->Points()) - { - if (aPtRep.IsNull()) - { - continue; - } - - if (const occ::handle aPOC = occ::down_cast(aPtRep)) - { - const BRepGraph_NodeId* anEdgeId = aCurveToEdgeDef.Seek(aPOC->Curve().get()); - if (anEdgeId != nullptr) - { - theParamLayer->SetPointOnCurve(aVertexId, - BRepGraph_EdgeId::FromNodeId(*anEdgeId), - aPOC->Parameter()); - } - } - else if (const occ::handle aPOCS = - occ::down_cast(aPtRep)) - { - const NCollection_DynamicArray* aCandidates = - aPCurveToCoEdges.Seek(aPOCS->PCurve().get()); - if (aCandidates == nullptr) - { - continue; - } - - const Geom_Surface* aSurfacePtr = aPOCS->Surface().get(); - BRepGraph_CoEdgeId aMatchedCoEdge; - for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCandidates) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aCoEdgeId); - if (!aCoEdge.FaceDefId.IsValidIn(aFaceRawSurfaces)) - { - continue; - } - if (aFaceRawSurfaces.Value(static_cast(aCoEdge.FaceDefId.Index)) == aSurfacePtr) - { - aMatchedCoEdge = aCoEdgeId; - break; - } - } - - if (aMatchedCoEdge.IsValid()) - { - theParamLayer->SetPointOnPCurve(aVertexId, aMatchedCoEdge, aPOCS->Parameter()); - } - } - else if (const occ::handle aPOS = - occ::down_cast(aPtRep)) - { - const BRepGraph_NodeId* aFaceId = aSurfToFaceDef.Seek(aPOS->Surface().get()); - if (aFaceId != nullptr) - { - theParamLayer->SetPointOnSurface(aVertexId, - BRepGraph_FaceId::FromNodeId(*aFaceId), - aPOS->Parameter(), - aPOS->Parameter2()); - } - } - } + appendBuildRoot(theBuild, aNode); } + return aNode; } -//================================================================================================= - -void populateOptionalLayers(BRepGraphInc_Storage& theStorage, - const occ::handle& theParamLayer, - const occ::handle& theRegularityLayer, - const BRepGraphInc_Populate::Options& theOptions, - const uint32_t theOldNbEdges, - const uint32_t theOldNbVertices, - const occ::handle& theTmpAlloc) +BRepGraphInc_Populate::BuildStatus runTopologyBuild(BuildContext& theBuild, + const TopoDS_Shape& theRootShape) { - populateRegularityLayer(theStorage, - theRegularityLayer, - theOptions.ExtractRegularities, - theOldNbEdges, - theTmpAlloc); - populateParamLayer(theStorage, - theParamLayer, - theOptions.ExtractVertexPointReps, - theOldNbVertices, - theTmpAlloc); + traverseTopology(theBuild, theRootShape, TopLoc_Location(), RootRole::Root); + + const uint32_t aNbPendingFaces = static_cast(theBuild.PendingFaces.Size()); + if (aNbPendingFaces == 0) + { + return theBuild.HasWireOrderWarnings ? BRepGraphInc_Populate::BuildStatus::SuccessWithWarnings + : BRepGraphInc_Populate::BuildStatus::Success; + } + + BRepGraph_ParallelPolicy::Workload aFaceExtraction; + aFaceExtraction.PrimaryItems = aNbPendingFaces; + const bool isParallelExtraction = + BRepGraph_ParallelPolicy::ShouldRun(theBuild.Parallel, aFaceExtraction); + OSD_Parallel::For( + 0, + static_cast(aNbPendingFaces), + [&](const int theIndex) { extractFaceData(theBuild.PendingFaces.ChangeValue(theIndex)); }, + !isParallelExtraction); + + BRepGraphInc_Populate::BuildStatus aStatus = BRepGraphInc_Populate::BuildStatus::Success; + for (const FaceBuildData& aFaceData : theBuild.PendingFaces) + { + if (aFaceData.UnsupportedNaturalBoundary) + { + aStatus = BRepGraphInc_Populate::BuildStatus::SuccessWithWarnings; + } + } + + registerFaceData(theBuild, theBuild.PendingFaces, theBuild.SupplementLayer); + + synthesizeFaceBoundaries(theBuild, theBuild.PendingFaces); + + if (theBuild.HasWireOrderWarnings) + { + aStatus = BRepGraphInc_Populate::BuildStatus::SuccessWithWarnings; + } + + return aStatus; +} + +BRepGraphInc_Populate::BuildStatus buildTopology( + BRepGraph& theGraph, + BRepGraphInc_Storage& theStorage, + const TopoDS_Shape& theShape, + const bool theParallel, + const TopologyBuildMode theMode, + const BRepGraphInc_Populate::Options& theOptions, + const BuildCounts& theOldCounts, + NCollection_LinearVector* theAppendedRoots = nullptr) +{ + const occ::handle aSupplementLayer = + theGraph.LayerRegistry().Find(); + + BuildContext aBuild(theStorage, theParallel, theMode, aSupplementLayer, theAppendedRoots); + + BRepGraphInc_Populate::BuildStatus aStatus = runTopologyBuild(aBuild, theShape); + (void)theOptions; + (void)theOldCounts; + return aStatus; } } // anonymous namespace //================================================================================================= -void BRepGraphInc_Populate::Perform( - BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theShape, - const bool theParallel, - const Options& theOptions, - const occ::handle& theParamLayer, - const occ::handle& theRegularityLayer, - const occ::handle& theTmpAlloc) +BRepGraphInc_Populate::BuildStatus BRepGraphInc_Populate::Perform(BRepGraph& theGraph, + const TopoDS_Shape& theShape, + bool theParallel, + const Options& theOptions) { - theStorage.Clear(); + theGraph.incStorage().Clear(); if (theShape.IsNull()) { - return; + return BuildStatus::Failed; } - // Use temporary allocator if provided, else default. - // Must NOT use the storage's persistent allocator for scratch data. - const occ::handle& aTmpAlloc = - !theTmpAlloc.IsNull() ? theTmpAlloc : NCollection_BaseAllocator::CommonBaseAllocator(); - const int aParallelWorkers = theParallel ? BRepGraph_ParallelPolicy::WorkerCount() : 1; - - // Phase 1 (sequential): Recursively explore hierarchy, collecting face contexts. - NCollection_DynamicArray aFaceData(256, aTmpAlloc); - RepDedup aRepDedup; - - traverseHierarchy(theStorage, aFaceData, aRepDedup, theShape, TopLoc_Location()); - - // Phase 2 (parallel): Extract per-face geometry/topology. - BRepGraph_ParallelPolicy::Workload aFaceExtractWork; - aFaceExtractWork.PrimaryItems = aFaceData.Length(); - const bool isParallelFaceExtraction = - BRepGraph_ParallelPolicy::ShouldRun(theParallel, aParallelWorkers, aFaceExtractWork); - OSD_Parallel::For( - 0, - aFaceData.Length(), - [&](const int theIndex) { extractFaceData(aFaceData.ChangeValue(theIndex)); }, - !isParallelFaceExtraction); - - // Phase 3 (sequential): Register into storage with deduplication. - registerFaceData(theStorage, aFaceData, aRepDedup); - - // Phase 3a: Fix compound Face ChildRefs (face indices were unknown during Phase 1, - // resolved now after registerFaceData). All other child types were resolved - // immediately in traverseShape via FindNodeByTShape. - const uint32_t aNbCompounds = theStorage.myCompounds.Nb(); - for (BRepGraph_CompoundId aCompoundId(0); aCompoundId.IsValid(aNbCompounds); ++aCompoundId) - { - const BRepGraph_NodeId aParentId(aCompoundId); - const TopoDS_Shape* aCompOrig = theStorage.myOriginalShapes.Seek(aParentId); - if (aCompOrig == nullptr) - { - continue; - } - - uint32_t aRefOrd = 0; - for (TopoDS_Iterator aChildIt(*aCompOrig, false, false); aChildIt.More(); aChildIt.Next()) - { - BRepGraphInc::ChildRef* aRef = nullptr; - uint32_t aCurrentOrd = 0; - const uint32_t aNbChildRefs = theStorage.NbChildRefs(); - for (BRepGraph_ChildRefId aChildRefId = BRepGraph_ChildRefId::Start(); - aChildRefId.IsValid(aNbChildRefs); - ++aChildRefId) - { - BRepGraphInc::ChildRef& aCandidate = theStorage.ChangeChildRef(aChildRefId); - if (aCandidate.ParentId != aParentId || aCandidate.IsRemoved) - { - continue; - } - if (aCurrentOrd == aRefOrd) - { - aRef = &aCandidate; - break; - } - ++aCurrentOrd; - } - - if (aRef == nullptr) - { - break; - } - - if (!aRef->ChildDefId.IsValid()) - { - const BRepGraph_NodeId* aNodeId = - theStorage.myTShapeToNodeId.Seek(aChildIt.Value().TShape().get()); - if (aNodeId != nullptr - && aNodeId->NodeKind == shapeTypeToNodeKind(aChildIt.Value().ShapeType())) - { - aRef->ChildDefId = *aNodeId; - } - } - ++aRefOrd; - } - } - - populateOptionalLayers(theStorage, - theParamLayer, - theRegularityLayer, - theOptions, - 0, - 0, - aTmpAlloc); - - // Build reverse indices. - theStorage.BuildReverseIndex(); - - theStorage.myIsDone = true; + return buildTopology(theGraph, + theGraph.incStorage(), + theShape, + theParallel, + TopologyBuildMode::FullHierarchy, + theOptions, + BuildCounts{}); } //================================================================================================= -void BRepGraphInc_Populate::AppendFlattened( - BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theShape, - const bool theParallel, - NCollection_DynamicArray& theAppendedRoots, - const Options& theOptions, - const occ::handle& theParamLayer, - const occ::handle& theRegularityLayer, - const occ::handle& theTmpAlloc) +BRepGraphInc_Populate::BuildStatus BRepGraphInc_Populate::AppendFlattened( + BRepGraph& theGraph, + const TopoDS_Shape& theShape, + bool theParallel, + NCollection_LinearVector& theAppendedRoots, + const Options& theOptions) { if (theShape.IsNull()) { - return; + return BuildStatus::Failed; } - // Use temporary allocator if provided, else default. - // Must NOT use the storage's persistent allocator for scratch data. - const occ::handle& aTmpAlloc = - !theTmpAlloc.IsNull() ? theTmpAlloc : NCollection_BaseAllocator::CommonBaseAllocator(); - const int aParallelWorkers = theParallel ? BRepGraph_ParallelPolicy::WorkerCount() : 1; - - // Snapshot entity counts before appending, for incremental updates. - const uint32_t anOldNbEdges = theStorage.NbEdges(); - const uint32_t anOldNbWires = theStorage.NbWires(); - const uint32_t anOldNbFaces = theStorage.NbFaces(); - const uint32_t anOldNbShells = theStorage.NbShells(); - const uint32_t anOldNbSolids = theStorage.NbSolids(); - const uint32_t anOldNbCompounds = theStorage.NbCompounds(); - const uint32_t anOldNbCompSolids = theStorage.NbCompSolids(); - const uint32_t anOldNbVertices = theStorage.NbVertices(); - const uint32_t anOldNbChildRefs = theStorage.NbChildRefs(); - const uint32_t anOldNbSolidRefs = theStorage.NbSolidRefs(); - - // Collect face contexts by flattening hierarchy. - NCollection_DynamicArray aFaceData(256, aTmpAlloc); - RepDedup aRepDedup; - - flattenForAppend(theStorage, - aFaceData, - aRepDedup, - theShape, - TopLoc_Location(), - &theAppendedRoots); - - // Parallel face extraction. - BRepGraph_ParallelPolicy::Workload aFaceExtractWork; - aFaceExtractWork.PrimaryItems = aFaceData.Length(); - const bool isParallelFaceExtraction = - BRepGraph_ParallelPolicy::ShouldRun(theParallel, aParallelWorkers, aFaceExtractWork); - OSD_Parallel::For( - 0, - aFaceData.Length(), - [&](const int theIndex) { extractFaceData(aFaceData.ChangeValue(theIndex)); }, - !isParallelFaceExtraction); - - // Sequential registration (reuses existing dedup maps). - registerFaceData(theStorage, aFaceData, aRepDedup); - - for (const FaceLocalData& aFaceDataElem : aFaceData) - { - const BRepGraph_NodeId* aFaceNodeId = - theStorage.FindNodeByTShape(aFaceDataElem.Face.TShape().get()); - if (aFaceNodeId != nullptr) - { - appendUniqueRootNode(theAppendedRoots, *aFaceNodeId); - } - } - - populateOptionalLayers(theStorage, - theParamLayer, - theRegularityLayer, - theOptions, - anOldNbEdges, - anOldNbVertices, - aTmpAlloc); - - // Incrementally update reverse indices for newly appended entities only. - theStorage.BuildDeltaReverseIndex(anOldNbEdges, - anOldNbWires, - anOldNbFaces, - anOldNbShells, - anOldNbSolids, - anOldNbCompounds, - anOldNbCompSolids, - anOldNbChildRefs, - anOldNbSolidRefs); - - theStorage.myIsDone = true; + return buildTopology(theGraph, + theGraph.incStorage(), + theShape, + theParallel, + TopologyBuildMode::Flattened, + theOptions, + captureBuildCounts(theGraph.incStorage()), + &theAppendedRoots); } //================================================================================================= -void BRepGraphInc_Populate::Append(BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theShape, - const bool theParallel, - const Options& theOptions, - const occ::handle& theParamLayer, - const occ::handle& theRegularityLayer, - const occ::handle& theTmpAlloc) +BRepGraphInc_Populate::BuildStatus BRepGraphInc_Populate::Append(BRepGraph& theGraph, + const TopoDS_Shape& theShape, + bool theParallel, + const Options& theOptions) { if (theShape.IsNull()) { - return; + return BuildStatus::Failed; } - const occ::handle& aTmpAlloc = - !theTmpAlloc.IsNull() ? theTmpAlloc : NCollection_BaseAllocator::CommonBaseAllocator(); - const int aParallelWorkers = theParallel ? BRepGraph_ParallelPolicy::WorkerCount() : 1; - - // Snapshot entity counts before appending, for incremental updates. - const uint32_t anOldNbEdges = theStorage.NbEdges(); - const uint32_t anOldNbWires = theStorage.NbWires(); - const uint32_t anOldNbFaces = theStorage.NbFaces(); - const uint32_t anOldNbShells = theStorage.NbShells(); - const uint32_t anOldNbSolids = theStorage.NbSolids(); - const uint32_t anOldNbVertices = theStorage.NbVertices(); - const uint32_t anOldNbCompounds = theStorage.NbCompounds(); - const uint32_t anOldNbCompSolids = theStorage.NbCompSolids(); - const uint32_t anOldNbChildRefs = theStorage.NbChildRefs(); - const uint32_t anOldNbSolidRefs = theStorage.NbSolidRefs(); - - // Phase 1 (sequential): Traverse the full hierarchy. - // Existing shapes are deduplicated via findExistingNode; only new shapes are added. - NCollection_DynamicArray aFaceData(256, aTmpAlloc); - RepDedup aRepDedup; - traverseHierarchy(theStorage, aFaceData, aRepDedup, theShape, TopLoc_Location()); - - // Phase 2 (parallel): Per-face geometry extraction. - BRepGraph_ParallelPolicy::Workload aFaceExtractWork; - aFaceExtractWork.PrimaryItems = aFaceData.Length(); - const bool isParallelFaceExtraction = - BRepGraph_ParallelPolicy::ShouldRun(theParallel, aParallelWorkers, aFaceExtractWork); - OSD_Parallel::For( - 0, - aFaceData.Length(), - [&](const int theIndex) { extractFaceData(aFaceData.ChangeValue(theIndex)); }, - !isParallelFaceExtraction); - - // Phase 3 (sequential): Register into storage with deduplication. - registerFaceData(theStorage, aFaceData, aRepDedup); - - // Phase 3a: Fix compound ChildRef linkages in NEWLY APPENDED compounds only. - // Pre-existing compounds are not re-processed - Append assumes complete subgraph - // hierarchies with no cross-references to existing containers. - // - // Cost is O(total children across new compounds). Earlier versions rescanned every - // ChildRef in the storage for each child (O(children x total refs)); the new compound's - // own ChildRefIds vector is already in order, so we iterate it directly. - for (BRepGraph_CompoundId aCompoundId(anOldNbCompounds); - aCompoundId.IsValid(theStorage.myCompounds.Nb()); - ++aCompoundId) - { - const BRepGraph_NodeId aCompoundNode = BRepGraph_NodeId(aCompoundId); - const TopoDS_Shape* aCompOrig = theStorage.myOriginalShapes.Seek(aCompoundNode); - if (aCompOrig == nullptr) - { - continue; - } - - const BRepGraphInc::CompoundDef& aCompDef = theStorage.Compound(aCompoundId); - const size_t aNbOwnedRefs = aCompDef.ChildRefIds.Size(); - - size_t aRefOrd = 0; - for (TopoDS_Iterator aChildIt(*aCompOrig, false, false); aChildIt.More(); aChildIt.Next()) - { - if (aRefOrd >= aNbOwnedRefs) - { - break; - } - const BRepGraph_ChildRefId aOwnedRefId = aCompDef.ChildRefIds.Value(aRefOrd); - BRepGraphInc::ChildRef& aRef = theStorage.ChangeChildRef(aOwnedRefId); - - if (aRef.IsRemoved || aRef.ParentId != aCompoundNode) - { - ++aRefOrd; - continue; - } - - if (!aRef.ChildDefId.IsValid()) - { - const BRepGraph_NodeId* aNodeId = - theStorage.myTShapeToNodeId.Seek(aChildIt.Value().TShape().get()); - if (aNodeId != nullptr - && aNodeId->NodeKind == shapeTypeToNodeKind(aChildIt.Value().ShapeType())) - { - aRef.ChildDefId = *aNodeId; - } - } - ++aRefOrd; - } - } - - populateOptionalLayers(theStorage, - theParamLayer, - theRegularityLayer, - theOptions, - anOldNbEdges, - anOldNbVertices, - aTmpAlloc); - - // Incrementally update reverse indices for newly appended entities only. - theStorage.BuildDeltaReverseIndex(anOldNbEdges, - anOldNbWires, - anOldNbFaces, - anOldNbShells, - anOldNbSolids, - anOldNbCompounds, - anOldNbCompSolids, - anOldNbChildRefs, - anOldNbSolidRefs); - - theStorage.myIsDone = true; + return buildTopology(theGraph, + theGraph.incStorage(), + theShape, + theParallel, + TopologyBuildMode::FullHierarchy, + theOptions, + captureBuildCounts(theGraph.incStorage())); } diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx index 010eee225a..defcb4e25d 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Populate.hxx @@ -15,112 +15,83 @@ #define _BRepGraphInc_Populate_HeaderFile #include - -#include -#include +#include #include +#include class TopoDS_Shape; -class BRepGraphInc_Storage; -class BRepGraph_LayerParam; -class BRepGraph_LayerRegularity; +class BRepGraph; -//! @brief Backend population pipeline for BRepGraphInc_Storage. +//! @brief Backend topology/geometry population for BRepGraph. //! //! This class is part of the BRepGraphInc backend and is intended for //! backend maintenance, tests, and low-level infrastructure only. -//! External code should enter through BRepGraph_Builder::Add(), which owns the +//! External code should enter through BRepGraph::ShapesView::Add(), which owns the //! public lifecycle, cache invalidation, and layer coordination. //! -//! Adapted from BRepGraph_Builder, but writes to incidence-table storage -//! instead of Def/Usage two-layer storage. Entity structs carry forward -//! child references directly (no separate Usage objects). -//! -//! The population pipeline: -//! 1. Sequential hierarchy traversal (Compound/CompSolid/Solid/Shell) -//! 2. Parallel per-face geometry extraction -//! 3. Sequential registration with TShape deduplication -//! 4. Reverse index construction +//! The builder stores forward child relations only. Reverse relations are rebuilt +//! by BRepGraphInc_Storage after population. class BRepGraphInc_Populate { public: DEFINE_STANDARD_ALLOC - //! Options controlling which post-passes are executed during population. + //! Result of a build operation. + enum class BuildStatus + { + Success, //!< All faces built successfully. + SuccessWithWarnings, //!< Build completed with diagnostics, e.g. unbounded natural faces. + Failed //!< Build failed (e.g., null shape, internal error). + }; + + //! Options controlling population. struct Options { - bool ExtractRegularities; //!< Phase 3b: edge regularities - bool ExtractVertexPointReps; //!< Phase 3c: vertex point representations - - Options() - : ExtractRegularities(true), - ExtractVertexPointReps(true) - { - } + Options() = default; }; //! Build backend incidence storage from a TopoDS_Shape. - //! @param[out] theStorage storage to populate (cleared first) + //! @param[out] theGraph graph whose storage to populate (cleared first) //! @param[in] theShape root shape //! @param[in] theParallel if true, face-level extraction runs in parallel //! @param[in] theOptions optional post-pass controls - //! @param[in] theParamLayer optional point-rep layer to populate - //! @param[in] theRegularityLayer optional edge-regularity layer to populate - //! @param[in] theTmpAlloc optional allocator for temporary scratch data - static Standard_EXPORT void Perform( - BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theShape, - const bool theParallel, - const Options& theOptions = Options(), - const occ::handle& theParamLayer = occ::handle(), - const occ::handle& theRegularityLayer = - occ::handle(), - const occ::handle& theTmpAlloc = - occ::handle()); + //! @return build status indicating success, warnings, or failure + [[nodiscard]] static Standard_EXPORT BuildStatus Perform(BRepGraph& theGraph, + const TopoDS_Shape& theShape, + bool theParallel, + const Options& theOptions = Options()); //! Extend existing backend storage with additional shapes (no clear). //! Flattens hierarchy containers away; Solid/Shell/Compound/CompSolid inputs //! contribute appended face roots instead of container entities. //! Recomputes the built-in metadata layers from the populated storage. - //! @param[in,out] theStorage storage to extend + //! @param[in,out] theGraph graph whose storage to extend //! @param[in] theShape shape to append //! @param[in] theParallel if true, face-level extraction runs in parallel //! @param[out] theAppendedRoots collected root NodeIds for non-container shapes //! @param[in] theOptions optional post-pass controls - //! @param[in] theTmpAlloc optional allocator for temporary scratch data - static Standard_EXPORT void AppendFlattened( - BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theShape, - const bool theParallel, - NCollection_DynamicArray& theAppendedRoots, - const Options& theOptions = Options(), - const occ::handle& theParamLayer = occ::handle(), - const occ::handle& theRegularityLayer = - occ::handle(), - const occ::handle& theTmpAlloc = - occ::handle()); + //! @return build status indicating success, warnings, or failure + [[nodiscard]] static Standard_EXPORT BuildStatus + AppendFlattened(BRepGraph& theGraph, + const TopoDS_Shape& theShape, + bool theParallel, + NCollection_LinearVector& theAppendedRoots, + const Options& theOptions = Options()); //! Extend existing backend storage with additional shapes (no clear). //! Preserves the full shape hierarchy: Solid/Shell/Compound/CompSolid nodes //! are created alongside Face/Edge/Vertex nodes. Shapes already present in //! the storage (same TShape pointer) are deduplicated and not re-added. - //! @param[in,out] theStorage storage to extend - //! @param[in] theShape shape to append - //! @param[in] theParallel if true, face-level extraction runs in parallel - //! @param[in] theOptions optional post-pass controls - //! @param[in] theTmpAlloc optional allocator for temporary scratch data - static Standard_EXPORT void Append( - BRepGraphInc_Storage& theStorage, - const TopoDS_Shape& theShape, - const bool theParallel, - const Options& theOptions = Options(), - const occ::handle& theParamLayer = occ::handle(), - const occ::handle& theRegularityLayer = - occ::handle(), - const occ::handle& theTmpAlloc = - occ::handle()); + //! @param[in,out] theGraph graph whose storage to extend + //! @param[in] theShape shape to append + //! @param[in] theParallel if true, face-level extraction runs in parallel + //! @param[in] theOptions optional post-pass controls + //! @return build status indicating success, warnings, or failure + [[nodiscard]] static Standard_EXPORT BuildStatus Append(BRepGraph& theGraph, + const TopoDS_Shape& theShape, + bool theParallel, + const Options& theOptions = Options()); -private: BRepGraphInc_Populate() = delete; }; diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.cxx index c137767d69..18c4f17265 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.cxx @@ -12,15 +12,14 @@ // commercial license or contractual agreement. #include + +#include #include - -#include -#include - +#include +#include +#include +#include #include -#include -#include -#include #include #include #include @@ -33,147 +32,124 @@ //================================================================================================= -static void restoreEdgeRegularities(const BRepGraph_LayerRegularity* theRegularities, - const BRepGraph_EdgeId theEdgeId, - BRepGraphInc_Reconstruct::Cache& theCache, - BRep_Builder& theBuilder, - TopoDS_Edge& theEdgeShape) +static void replaySupplementAttachments(const BRepGraph_LayerTopoSupplement* theSupplement, + const BRepGraph_NodeId theOwner, + TopoDS_Shape& theOwnerShape) { - if (theRegularities == nullptr) + if (theSupplement == nullptr || theOwnerShape.IsNull()) { return; } - const BRepGraph_LayerRegularity::EdgeRegularities* aRegularities = - theRegularities->FindEdgeRegularities(theEdgeId); - if (aRegularities == nullptr) + const NCollection_LinearVector& anAttached = theSupplement->AttachedTo(theOwner); + if (anAttached.IsEmpty()) { return; } - for (const BRepGraph_LayerRegularity::RegularityEntry& aRegEntry : aRegularities->Entries) + BRep_Builder aBuilder; + for (const uint64_t aUid : anAttached) { - // Seam continuity (F1 == F2) lives on the BRep_CurveOnClosedSurface that - // owns the PCurve pair; the face-path PCurve installation handles it - // directly. BRep_Builder::Continuity would create a spurious - // BRep_CurveOn2Surfaces with S1 == S2 here. - if (aRegEntry.FaceEntity1 == aRegEntry.FaceEntity2) + const BRepGraph_LayerTopoSupplement::Entry* anEntry = theSupplement->FindByUid(aUid); + if (anEntry == nullptr || anEntry->Shape.IsNull()) { continue; } - const TopoDS_Shape* aFaceShape1 = theCache.Seek(aRegEntry.FaceEntity1); - const TopoDS_Shape* aFaceShape2 = theCache.Seek(aRegEntry.FaceEntity2); - if (aFaceShape1 != nullptr && aFaceShape2 != nullptr) - { - theBuilder.Continuity(theEdgeShape, - TopoDS::Face(*aFaceShape1), - TopoDS::Face(*aFaceShape2), - aRegEntry.Continuity); - } + aBuilder.Add(theOwnerShape, anEntry->Shape); } } //================================================================================================= -static void restoreVertexPointReps(const BRepGraphInc_Storage& theStorage, - const BRepGraph_LayerParam* theParams, - const BRepGraph_VertexId theVertexId, - BRepGraphInc_Reconstruct::Cache& theCache, - BRep_Builder& theBuilder) +BRepGraphInc_Reconstruct::Cache::TempScope::TempScope(Cache& theCache) + : myCache(theCache) { - if (theParams == nullptr || !theVertexId.IsValid(theStorage.NbVertices())) + if (myCache.myTempScopeDepth == 0 && !myCache.myTempAllocator.IsNull()) { - return; + myCache.myTempAllocator->Reset(false); } + ++myCache.myTempScopeDepth; +} - const BRepGraph_LayerParam::VertexParams* aParams = theParams->FindVertexParams(theVertexId); - if (aParams == nullptr || aParams->IsEmpty()) +//================================================================================================= + +BRepGraphInc_Reconstruct::Cache::TempScope::~TempScope() +{ + --myCache.myTempScopeDepth; + if (myCache.myTempScopeDepth == 0 && !myCache.myTempAllocator.IsNull()) { - return; - } - - const TopoDS_Shape* aVtxCached = theCache.Seek(theVertexId); - if (aVtxCached == nullptr || aVtxCached->IsNull()) - { - return; - } - - const BRepGraphInc::VertexDef& aVtxDef = theStorage.Vertex(theVertexId); - TopoDS_Vertex aVtxShape = TopoDS::Vertex(*aVtxCached); - - for (const BRepGraph_LayerParam::PointOnCurveEntry& aPOC : aParams->PointsOnCurve) - { - const TopoDS_Shape* anEdgeCached = theCache.Seek(aPOC.EdgeDefId); - if (anEdgeCached != nullptr && !anEdgeCached->IsNull()) - { - theBuilder.UpdateVertex(aVtxShape, - aPOC.Parameter, - TopoDS::Edge(*anEdgeCached), - aVtxDef.Tolerance); - } - } - - for (const BRepGraph_LayerParam::PointOnSurfaceEntry& aPOS : aParams->PointsOnSurface) - { - const TopoDS_Shape* aFaceCached = theCache.Seek(aPOS.FaceDefId); - if (aFaceCached != nullptr && !aFaceCached->IsNull()) - { - theBuilder.UpdateVertex(aVtxShape, - aPOS.ParameterU, - aPOS.ParameterV, - TopoDS::Face(*aFaceCached), - aVtxDef.Tolerance); - } - } - - for (const BRepGraph_LayerParam::PointOnPCurveEntry& aPOPC : aParams->PointsOnPCurve) - { - if (!aPOPC.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aPOPC.CoEdgeDefId); - if (!aCoEdge.Curve2DRepId.IsValid()) - { - continue; - } - - const TopoDS_Shape* anEdgeCached = theCache.Seek(aCoEdge.EdgeDefId); - const TopoDS_Shape* aFaceCached = theCache.Seek(aCoEdge.FaceDefId); - if (anEdgeCached != nullptr && !anEdgeCached->IsNull() && aFaceCached != nullptr - && !aFaceCached->IsNull()) - { - theBuilder.UpdateVertex(aVtxShape, - aPOPC.Parameter, - TopoDS::Edge(*anEdgeCached), - TopoDS::Face(*aFaceCached), - aVtxDef.Tolerance); - } + myCache.myTempAllocator->Reset(false); } } -TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theNode, - const BRepGraph_LayerParam* theParams, - const BRepGraph_LayerRegularity* theRegularities) +//================================================================================================= + +BRepGraphInc_Reconstruct::Cache::Cache() + : myAllocator(new NCollection_IncAllocator()), + myTempAllocator(new NCollection_IncAllocator()) +{ + for (int aKindIdx = 0; aKindIdx < THE_KIND_COUNT; ++aKindIdx) + { + myKinds[aKindIdx] = NCollection_DynamicArray(THE_DEFAULT_INCREMENT, myAllocator); + } +} + +//================================================================================================= + +const TopoDS_Shape* BRepGraphInc_Reconstruct::Cache::Seek(const BRepGraph_NodeId theNode) const +{ + const int aKindIdx = static_cast(theNode.NodeKind); + if (aKindIdx < 0 || aKindIdx >= THE_KIND_COUNT) + { + return nullptr; + } + const NCollection_DynamicArray& aVec = myKinds[aKindIdx]; + if (theNode.Index >= aVec.Size()) + { + return nullptr; + } + const TopoDS_Shape& aShape = aVec.Value(static_cast(theNode.Index)); + return aShape.IsNull() ? nullptr : &aShape; +} + +//================================================================================================= + +void BRepGraphInc_Reconstruct::Cache::Bind(const BRepGraph_NodeId theNode, + const TopoDS_Shape& theShape) +{ + const int aKindIdx = static_cast(theNode.NodeKind); + if (aKindIdx < 0 || aKindIdx >= THE_KIND_COUNT) + { + return; + } + NCollection_DynamicArray& aVec = myKinds[aKindIdx]; + aVec.SetValue(static_cast(theNode.Index), theShape); +} + +//================================================================================================= + +TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraph& theGraph, + const BRepGraph_NodeId theNode) { Cache aCache; - return Node(theStorage, theNode, aCache, theParams, theRegularities); + return Node(theGraph, theNode, aCache); } //================================================================================================= -TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theNode, - Cache& theCache, - const BRepGraph_LayerParam* theParams, - const BRepGraph_LayerRegularity* theRegularities) +TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraph& theGraph, + const BRepGraph_NodeId theNode, + Cache& theCache) { if (!theNode.IsValid()) { return TopoDS_Shape(); } + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + const BRepGraph_LayerTopoSupplement* aSupplement = + theGraph.LayerRegistry().FindLayer().get(); + Cache::TempScope aTempScope(theCache); // Check cache first. @@ -189,7 +165,7 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the switch (theNode.NodeKind) { case BRepGraph_NodeId::Kind::Vertex: { - const BRepGraphInc::VertexDef& aVtx = theStorage.Vertex(BRepGraph_VertexId(theNode)); + const BRepGraphInc::VertexDef& aVtx = aStorage.Vertex(BRepGraph_VertexId(theNode)); TopoDS_Vertex aNewVtx; aBB.MakeVertex(aNewVtx, aVtx.Point, aVtx.Tolerance); aResult = aNewVtx; @@ -197,16 +173,31 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the } case BRepGraph_NodeId::Kind::Edge: { - const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(BRepGraph_EdgeId(theNode)); + const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(BRepGraph_EdgeId(theNode)); TopoDS_Edge aNewEdge; - if (anEdge.IsDegenerate) + + BRepGraph_CacheDerivedState::EdgeEntry anEdgeEntry; + [[maybe_unused]] const bool isEdgeStateComputed = + BRepGraph_CacheDerivedState::ComputeEdgeStatus(theGraph, + BRepGraph_EdgeId(theNode), + anEdgeEntry); + + if (anEdgeEntry.Status + == BRepGraph_CacheDerivedState::EdgeGeometryStatus::DegenerateOnSurface) { aBB.MakeEdge(aNewEdge); aBB.Degenerated(aNewEdge, true); } - else if (anEdge.Curve3DRepId.IsValid()) + else { - const occ::handle& aCurve3d = theStorage.Curve3DRep(anEdge.Curve3DRepId).Curve; + // Get 3D curve from use record. + occ::handle aCurve3d; + if (anEdge.Curve3DRepId.IsValid() && anEdge.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D()) + && !aStorage.IsRemoved(anEdge.Curve3DRepId)) + { + aCurve3d = aStorage.EdgeCurve3DRep(anEdge.Curve3DRepId).Curve; + } + if (!aCurve3d.IsNull()) { aBB.MakeEdge(aNewEdge, aCurve3d, TopLoc_Location(), anEdge.Tolerance); @@ -216,69 +207,54 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the aBB.MakeEdge(aNewEdge); } } - else + + // Read range from use record. + double aParamFirst = 0.0; + double aParamLast = 0.0; + if (anEdge.Curve3DRepId.IsValid() && anEdge.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D()) + && !aStorage.IsRemoved(anEdge.Curve3DRepId)) { - aBB.MakeEdge(aNewEdge); + const BRepGraphInc::EdgeCurve3DRep& aUse = aStorage.EdgeCurve3DRep(anEdge.Curve3DRepId); + aParamFirst = aUse.ParamFirst; + aParamLast = aUse.ParamLast; } - aBB.Range(aNewEdge, anEdge.ParamFirst, anEdge.ParamLast); - aBB.SameParameter(aNewEdge, anEdge.SameParameter); - aBB.SameRange(aNewEdge, anEdge.SameRange); + aBB.Range(aNewEdge, aParamFirst, aParamLast); + aBB.SameParameter(aNewEdge, anEdgeEntry.SameParameter); + aBB.SameRange(aNewEdge, anEdgeEntry.SameRange); if (anEdge.StartVertexRefId.IsValid()) { - const BRepGraphInc::VertexRef& aStartVR = theStorage.VertexRef(anEdge.StartVertexRefId); - TopoDS_Shape aStartVtx = - Node(theStorage, aStartVR.VertexDefId, theCache, theParams, theRegularities); + const BRepGraphInc::VertexRef& aStartVR = aStorage.VertexRef(anEdge.StartVertexRefId); + TopoDS_Shape aStartVtx = Node(theGraph, aStartVR.ChildVertexId, theCache); if (!aStartVtx.IsNull()) { aStartVtx.Orientation(TopAbs_FORWARD); - if (!aStartVR.LocalLocation.IsIdentity()) - { - aStartVtx.Location(aStartVR.LocalLocation); - } aBB.Add(aNewEdge, aStartVtx); } } if (anEdge.EndVertexRefId.IsValid()) { - const BRepGraphInc::VertexRef& anEndVR = theStorage.VertexRef(anEdge.EndVertexRefId); - TopoDS_Shape anEndVtx = - Node(theStorage, anEndVR.VertexDefId, theCache, theParams, theRegularities); + const BRepGraphInc::VertexRef& anEndVR = aStorage.VertexRef(anEdge.EndVertexRefId); + TopoDS_Shape anEndVtx = Node(theGraph, anEndVR.ChildVertexId, theCache); if (!anEndVtx.IsNull()) { anEndVtx.Orientation(TopAbs_REVERSED); - if (!anEndVR.LocalLocation.IsIdentity()) - { - anEndVtx.Location(anEndVR.LocalLocation); - } aBB.Add(aNewEdge, anEndVtx); } } - for (const BRepGraph_VertexRefId& aVRefId : anEdge.InternalVertexRefIds) - { - const BRepGraphInc::VertexRef& aVR = theStorage.VertexRef(aVRefId); - TopoDS_Shape aVtx = Node(theStorage, aVR.VertexDefId, theCache, theParams, theRegularities); - if (!aVtx.IsNull()) - { - aVtx.Orientation(aVR.Orientation); - if (!aVR.LocalLocation.IsIdentity()) - { - aVtx.Location(aVR.LocalLocation); - } - aBB.Add(aNewEdge, aVtx); - } - } - // Attach Polygon3D discretization. - if (anEdge.Polygon3DRepId.IsValid()) + // Attach Polygon3D discretization via use record. + if (anEdge.Polygon3DRepId.IsValid() + && anEdge.Polygon3DRepId.IsValid(aStorage.NbEdgePolygons3D()) + && !aStorage.IsRemoved(anEdge.Polygon3DRepId)) { const occ::handle& aPolygon3D = - theStorage.Polygon3DRep(anEdge.Polygon3DRepId).Polygon; + aStorage.EdgePolygon3DRep(anEdge.Polygon3DRepId).Polygon; if (!aPolygon3D.IsNull()) { aBB.UpdateEdge(aNewEdge, aPolygon3D, TopLoc_Location()); } } - if (anEdge.IsClosed) + if (anEdgeEntry.IsClosed) { aNewEdge.Closed(true); } @@ -287,34 +263,34 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the } case BRepGraph_NodeId::Kind::Wire: { - const BRepGraphInc::WireDef& aWire = theStorage.Wire(BRepGraph_WireId(theNode)); - TopoDS_Wire aNewWire; + TopoDS_Wire aNewWire; aBB.MakeWire(aNewWire); - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) + const BRepGraphInc::WireRelations& aWireRel = + aStorage.WireRelations(BRepGraph_WireId(theNode)); + for (const BRepGraph_CoEdgeId& aCoEdgeId : aWireRel.CoEdgeIds) { - const BRepGraphInc::CoEdgeRef& aCoEdgeRef = theStorage.CoEdgeRef(aCoEdgeRefId); - if (aCoEdgeRef.IsRemoved || !aCoEdgeRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) + if (!aCoEdgeId.IsValid(aStorage.NbCoEdges())) { continue; } - const BRepGraphInc::CoEdgeDef& aCoEdge = - theStorage.CoEdge(BRepGraph_CoEdgeId(aCoEdgeRef.CoEdgeDefId)); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid(theStorage.NbEdges())) + if (aStorage.IsRemoved(aCoEdgeId)) { continue; } - TopoDS_Shape anEdge = Node(theStorage, aCoEdge.EdgeDefId, theCache); + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); + if (!aCoEdge.ChildEdgeId.IsValid(aStorage.NbEdges())) + { + continue; + } + + TopoDS_Shape anEdge = Node(theGraph, aCoEdge.ChildEdgeId, theCache); if (!anEdge.IsNull()) { anEdge.Orientation(aCoEdge.Orientation); - if (!aCoEdgeRef.LocalLocation.IsIdentity()) - { - anEdge.Location(aCoEdgeRef.LocalLocation); - } aBB.Add(aNewWire, anEdge); } } - if (aWire.IsClosed) + if (BRepGraph_CacheDerivedState::ComputeWireIsClosed(theGraph, BRepGraph_WireId(theNode))) { aNewWire.Closed(true); } @@ -323,51 +299,38 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the } case BRepGraph_NodeId::Kind::Face: { - aResult = - FaceWithCache(theStorage, BRepGraph_FaceId(theNode), theCache, theParams, theRegularities); + aResult = FaceWithCache(theGraph, BRepGraph_FaceId(theNode), theCache); break; } case BRepGraph_NodeId::Kind::Shell: { - const BRepGraphInc::ShellDef& aShell = theStorage.Shell(BRepGraph_ShellId(theNode)); - TopoDS_Shell aNewShell; + TopoDS_Shell aNewShell; aBB.MakeShell(aNewShell); - for (const BRepGraph_FaceRefId& aFaceRefId : aShell.FaceRefIds) + const BRepGraphInc::ShellRelations& aShellRel = + aStorage.ShellRelations(BRepGraph_ShellId(theNode)); + for (const BRepGraph_FaceRefId& aFaceRefId : aShellRel.FaceRefIds) { - const BRepGraphInc::FaceRef& aRef = theStorage.FaceRef(aFaceRefId); - if (aRef.IsRemoved || !aRef.FaceDefId.IsValid(theStorage.NbFaces())) + if (aStorage.IsRemoved(aFaceRefId)) { continue; } - TopoDS_Shape aFace = - FaceWithCache(theStorage, aRef.FaceDefId, theCache, theParams, theRegularities); + const BRepGraphInc::FaceRef& aRef = aStorage.FaceRef(aFaceRefId); + if (!aRef.ChildFaceId.IsValid(aStorage.NbFaces())) + { + continue; + } + TopoDS_Shape aFace = FaceWithCache(theGraph, aRef.ChildFaceId, theCache); if (!aFace.IsNull()) { aFace.Orientation(aRef.Orientation); - if (!aRef.LocalLocation.IsIdentity()) - { - aFace.Location(aRef.LocalLocation); - } aBB.Add(aNewShell, aFace); } } - // Reconstruct free children (wires, edges) attached directly to the shell. - for (const BRepGraph_ChildRefId& aChildRefId : aShell.AuxChildRefIds) - { - const BRepGraphInc::ChildRef& aRef = theStorage.ChildRef(aChildRefId); - TopoDS_Shape aChild = - Node(theStorage, aRef.ChildDefId, theCache, theParams, theRegularities); - if (!aChild.IsNull()) - { - aChild.Orientation(aRef.Orientation); - if (!aRef.LocalLocation.IsIdentity()) - { - aChild.Location(aRef.LocalLocation); - } - aBB.Add(aNewShell, aChild); - } - } - if (aShell.IsClosed) + BRepGraph_CacheDerivedState::ShellEntry aShellEntry; + if (BRepGraph_CacheDerivedState::ComputeShellStatus(theGraph, + BRepGraph_ShellId(theNode), + aShellEntry) + && aShellEntry.Status == BRepGraph_CacheDerivedState::ShellClosureStatus::Closed) { aNewShell.Closed(true); } @@ -376,60 +339,49 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the } case BRepGraph_NodeId::Kind::Solid: { - const BRepGraphInc::SolidDef& aSolid = theStorage.Solid(BRepGraph_SolidId(theNode)); - TopoDS_Solid aNewSolid; + TopoDS_Solid aNewSolid; aBB.MakeSolid(aNewSolid); - for (const BRepGraph_ShellRefId& aShellRefId : aSolid.ShellRefIds) + const BRepGraphInc::SolidRelations& aSolidRel = + aStorage.SolidRelations(BRepGraph_SolidId(theNode)); + for (const BRepGraph_ShellRefId& aShellRefId : aSolidRel.ShellRefIds) { - const BRepGraphInc::ShellRef& aShellRef = theStorage.ShellRef(aShellRefId); - if (aShellRef.IsRemoved || !aShellRef.ShellDefId.IsValid(theStorage.NbShells())) + if (aStorage.IsRemoved(aShellRefId)) { continue; } - TopoDS_Shape aShell = - Node(theStorage, aShellRef.ShellDefId, theCache, theParams, theRegularities); + const BRepGraphInc::ShellRef& aShellRef = aStorage.ShellRef(aShellRefId); + if (!aShellRef.ChildShellId.IsValid(aStorage.NbShells())) + { + continue; + } + TopoDS_Shape aShell = Node(theGraph, aShellRef.ChildShellId, theCache); if (!aShell.IsNull()) { aShell.Orientation(aShellRef.Orientation); - if (!aShellRef.LocalLocation.IsIdentity()) - { - aShell.Location(aShellRef.LocalLocation); - } aBB.Add(aNewSolid, aShell); } } - // Free children of the solid (edges, vertices). - for (const BRepGraph_ChildRefId& aChildRefId : aSolid.AuxChildRefIds) - { - const BRepGraphInc::ChildRef& aCR = theStorage.ChildRef(aChildRefId); - TopoDS_Shape aChild = Node(theStorage, aCR.ChildDefId, theCache); - if (!aChild.IsNull()) - { - aChild.Orientation(aCR.Orientation); - if (!aCR.LocalLocation.IsIdentity()) - { - aChild.Location(aCR.LocalLocation); - } - aBB.Add(aNewSolid, aChild); - } - } aResult = aNewSolid; break; } case BRepGraph_NodeId::Kind::Compound: { - const BRepGraphInc::CompoundDef& aComp = theStorage.Compound(BRepGraph_CompoundId(theNode)); - TopoDS_Compound aNewComp; + TopoDS_Compound aNewComp; aBB.MakeCompound(aNewComp); - for (const BRepGraph_ChildRefId& aChildRefId : aComp.ChildRefIds) + const BRepGraphInc::CompoundRelations& aCompoundRel = + aStorage.CompoundRelations(BRepGraph_CompoundId(theNode)); + for (const BRepGraph_ChildRefId& aChildRefId : aCompoundRel.ChildRefIds) { - const BRepGraphInc::ChildRef& aRef = theStorage.ChildRef(aChildRefId); - if (aRef.IsRemoved || !aRef.ChildDefId.IsValid()) + if (aStorage.IsRemoved(aChildRefId)) { continue; } - TopoDS_Shape aChild = - Node(theStorage, aRef.ChildDefId, theCache, theParams, theRegularities); + const BRepGraphInc::ChildRef& aRef = aStorage.ChildRef(aChildRefId); + if (!aRef.ChildNodeId.IsValid()) + { + continue; + } + TopoDS_Shape aChild = Node(theGraph, aRef.ChildNodeId, theCache); if (!aChild.IsNull()) { aChild.Orientation(aRef.Orientation); @@ -445,25 +397,25 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the } case BRepGraph_NodeId::Kind::CompSolid: { - const BRepGraphInc::CompSolidDef& aCS = theStorage.CompSolid(BRepGraph_CompSolidId(theNode)); - TopoDS_CompSolid aNewCS; + TopoDS_CompSolid aNewCS; aBB.MakeCompSolid(aNewCS); - for (const BRepGraph_SolidRefId& aSolidRefId : aCS.SolidRefIds) + const BRepGraphInc::CompSolidRelations& aCompSolidRel = + aStorage.CompSolidRelations(BRepGraph_CompSolidId(theNode)); + for (const BRepGraph_SolidRefId& aSolidRefId : aCompSolidRel.SolidRefIds) { - const BRepGraphInc::SolidRef& aRef = theStorage.SolidRef(aSolidRefId); - if (aRef.IsRemoved || !aRef.SolidDefId.IsValid(theStorage.NbSolids())) + if (aStorage.IsRemoved(aSolidRefId)) { continue; } - TopoDS_Shape aSolid = - Node(theStorage, aRef.SolidDefId, theCache, theParams, theRegularities); + const BRepGraphInc::SolidRef& aRef = aStorage.SolidRef(aSolidRefId); + if (!aRef.ChildSolidId.IsValid(aStorage.NbSolids())) + { + continue; + } + TopoDS_Shape aSolid = Node(theGraph, aRef.ChildSolidId, theCache); if (!aSolid.IsNull()) { aSolid.Orientation(aRef.Orientation); - if (!aRef.LocalLocation.IsIdentity()) - { - aSolid.Location(aRef.LocalLocation); - } aBB.Add(aNewCS, aSolid); } } @@ -479,20 +431,25 @@ TopoDS_Shape BRepGraphInc_Reconstruct::Node(const BRepGraphInc_Storage& the return TopoDS_Shape(); } + if (theNode.NodeKind != BRepGraph_NodeId::Kind::Face) + { + replaySupplementAttachments(aSupplement, theNode, aResult); + } theCache.Bind(theNode, aResult); return aResult; } //================================================================================================= -TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( - const BRepGraphInc_Storage& theStorage, - const BRepGraph_FaceId theFaceId, - Cache& theCache, - const BRepGraph_LayerParam* theParams, - const BRepGraph_LayerRegularity* theRegularities) +TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache(const BRepGraph& theGraph, + const BRepGraph_FaceId theFaceId, + Cache& theCache) { - if (!theFaceId.IsValid(theStorage.NbFaces())) + const BRepGraphInc_Storage& aStorage = theGraph.incStorage(); + const BRepGraph_LayerTopoSupplement* aSupplement = + theGraph.LayerRegistry().FindLayer().get(); + + if (!theFaceId.IsValid(aStorage.NbFaces())) { return TopoDS_Shape(); } @@ -508,13 +465,14 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( } BRep_Builder aBB; - const BRepGraphInc::FaceDef& aFace = theStorage.Face(theFaceId); + const BRepGraphInc::FaceDef& aFace = aStorage.Face(theFaceId); - // Resolve surface from rep storage (may be null for bare topology faces). + // Resolve surface from use record (may be null for bare topology faces). occ::handle aFaceSurface; - if (aFace.SurfaceRepId.IsValid()) + if (aFace.SurfaceRepId.IsValid() && aFace.SurfaceRepId.IsValid(aStorage.NbFaceSurfaces()) + && !aStorage.IsRemoved(aFace.SurfaceRepId)) { - aFaceSurface = theStorage.SurfaceRep(aFace.SurfaceRepId).Surface; + aFaceSurface = aStorage.FaceSurfaceRep(aFace.SurfaceRepId).Surface; } TopoDS_Face aNewFace; @@ -527,19 +485,21 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( aBB.MakeFace(aNewFace); } - // Attach triangulation. - if (aFace.TriangulationRepId.IsValid()) + // Attach triangulation via use record. + occ::handle aFaceTriangulation; + if (aFace.TriangulationRepId.IsValid() + && aFace.TriangulationRepId.IsValid(aStorage.NbFaceTriangulations()) + && !aStorage.IsRemoved(aFace.TriangulationRepId)) { - const occ::handle& aTri = - theStorage.TriangulationRep(aFace.TriangulationRepId).Triangulation; - if (!aTri.IsNull()) + aFaceTriangulation = aStorage.FaceTriangulationRep(aFace.TriangulationRepId).Triangulation; + if (!aFaceTriangulation.IsNull()) { NCollection_List> aTriList(theCache.myTempAllocator); - aTriList.Append(aTri); + aTriList.Append(aFaceTriangulation); const occ::handle& aTFace = occ::down_cast(aNewFace.TShape()); if (!aTFace.IsNull()) { - aTFace->Triangulations(aTriList, aTri); + aTFace->Triangulations(aTriList, aFaceTriangulation); } } } @@ -553,16 +513,23 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( return TopoDS::Edge(*aCached); } - const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(theEdgeId); + const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(theEdgeId); TopoDS_Edge aNewEdge; - if (anEdge.IsDegenerate) + + BRepGraph_CacheDerivedState::EdgeEntry anEdgeEntry; + [[maybe_unused]] const bool isEdgeStateComputed = + BRepGraph_CacheDerivedState::ComputeEdgeStatus(theGraph, theEdgeId, anEdgeEntry); + + if (anEdgeEntry.Status == BRepGraph_CacheDerivedState::EdgeGeometryStatus::DegenerateOnSurface) { aBB.MakeEdge(aNewEdge); aBB.Degenerated(aNewEdge, true); } - else if (anEdge.Curve3DRepId.IsValid()) + else if (anEdge.Curve3DRepId.IsValid() && anEdge.Curve3DRepId.IsValid(aStorage.NbEdgeCurves3D()) + && !aStorage.IsRemoved(anEdge.Curve3DRepId)) { - const occ::handle& aCurve3d = theStorage.Curve3DRep(anEdge.Curve3DRepId).Curve; + const BRepGraphInc::EdgeCurve3DRep& aCurveRep = aStorage.EdgeCurve3DRep(anEdge.Curve3DRepId); + const occ::handle& aCurve3d = aCurveRep.Curve; if (!aCurve3d.IsNull()) { aBB.MakeEdge(aNewEdge, aCurve3d, TopLoc_Location(), anEdge.Tolerance); @@ -571,14 +538,14 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( { aBB.MakeEdge(aNewEdge); } + aBB.Range(aNewEdge, aCurveRep.ParamFirst, aCurveRep.ParamLast); } else { aBB.MakeEdge(aNewEdge); } - aBB.Range(aNewEdge, anEdge.ParamFirst, anEdge.ParamLast); - aBB.SameParameter(aNewEdge, anEdge.SameParameter); - aBB.SameRange(aNewEdge, anEdge.SameRange); + aBB.SameParameter(aNewEdge, anEdgeEntry.SameParameter); + aBB.SameRange(aNewEdge, anEdgeEntry.SameRange); // Vertices (also cached). const auto aGetOrBuildVertex = [&](const BRepGraph_VertexId theVtxId) -> TopoDS_Shape { @@ -592,279 +559,210 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( { return *aVtxCached; } - const BRepGraphInc::VertexDef& aVtx = theStorage.Vertex(theVtxId); + const BRepGraphInc::VertexDef& aVtx = aStorage.Vertex(theVtxId); TopoDS_Vertex aNewVtx; aBB.MakeVertex(aNewVtx, aVtx.Point, aVtx.Tolerance); + replaySupplementAttachments(aSupplement, aVtxNodeId, aNewVtx); theCache.Bind(aVtxNodeId, aNewVtx); return aNewVtx; }; if (anEdge.StartVertexRefId.IsValid()) { - const BRepGraphInc::VertexRef& aStartVR = theStorage.VertexRef(anEdge.StartVertexRefId); - TopoDS_Shape aStartVtx = aGetOrBuildVertex(aStartVR.VertexDefId); + const BRepGraphInc::VertexRef& aStartVR = aStorage.VertexRef(anEdge.StartVertexRefId); + TopoDS_Shape aStartVtx = aGetOrBuildVertex(aStartVR.ChildVertexId); if (!aStartVtx.IsNull()) { aStartVtx.Orientation(TopAbs_FORWARD); - if (!aStartVR.LocalLocation.IsIdentity()) - { - aStartVtx.Location(aStartVR.LocalLocation); - } aBB.Add(aNewEdge, aStartVtx); } } if (anEdge.EndVertexRefId.IsValid()) { - const BRepGraphInc::VertexRef& anEndVR = theStorage.VertexRef(anEdge.EndVertexRefId); - TopoDS_Shape anEndVtx = aGetOrBuildVertex(anEndVR.VertexDefId); + const BRepGraphInc::VertexRef& anEndVR = aStorage.VertexRef(anEdge.EndVertexRefId); + TopoDS_Shape anEndVtx = aGetOrBuildVertex(anEndVR.ChildVertexId); if (!anEndVtx.IsNull()) { anEndVtx.Orientation(TopAbs_REVERSED); - if (!anEndVR.LocalLocation.IsIdentity()) - { - anEndVtx.Location(anEndVR.LocalLocation); - } aBB.Add(aNewEdge, anEndVtx); } } - for (const BRepGraph_VertexRefId& aVRefId : anEdge.InternalVertexRefIds) - { - const BRepGraphInc::VertexRef& aVR = theStorage.VertexRef(aVRefId); - TopoDS_Shape aVtx = aGetOrBuildVertex(aVR.VertexDefId); - if (!aVtx.IsNull()) - { - aVtx.Orientation(aVR.Orientation); - if (!aVR.LocalLocation.IsIdentity()) - { - aVtx.Location(aVR.LocalLocation); - } - aBB.Add(aNewEdge, aVtx); - } - } - // Polygon3D. - if (anEdge.Polygon3DRepId.IsValid()) + if (anEdge.Polygon3DRepId.IsValid() + && anEdge.Polygon3DRepId.IsValid(aStorage.NbEdgePolygons3D()) + && !aStorage.IsRemoved(anEdge.Polygon3DRepId)) { const occ::handle& aPolygon3D = - theStorage.Polygon3DRep(anEdge.Polygon3DRepId).Polygon; + aStorage.EdgePolygon3DRep(anEdge.Polygon3DRepId).Polygon; if (!aPolygon3D.IsNull()) { aBB.UpdateEdge(aNewEdge, aPolygon3D, TopLoc_Location()); } } - restoreEdgeRegularities(theRegularities, theEdgeId, theCache, aBB, aNewEdge); - - if (anEdge.IsClosed) + if (anEdgeEntry.IsClosed) { aNewEdge.Closed(true); } + replaySupplementAttachments(aSupplement, theEdgeId, aNewEdge); theCache.Bind(anEdgeNodeId, aNewEdge); return aNewEdge; }; - // Build wires for this face. - // Wire TShape is cached (1 NodeId = 1 TShape); PCurve attachment is per-face. - // theWireLocation is the wire's LocalLocation within the face (WireUsage.LocalLocation), - // needed to compute the correct CurveRepresentation location for PCurve binding. - const auto aBuildWireForFace = [&](BRepGraph_WireId theWireId, - const TopLoc_Location& theWireLocation) -> TopoDS_Wire { - const BRepGraphInc::WireDef& aWire = theStorage.Wire(theWireId); - BRepGraph_NodeId aWireNodeId = theWireId; + const auto aBuildWireForFace = [&](const BRepGraph_WireId theWireId) -> TopoDS_Wire { + BRepGraph_NodeId aWireNodeId = theWireId; // Get or create wire TShape. const TopoDS_Shape* aCachedWire = theCache.Seek(aWireNodeId); TopoDS_Wire aNewWire; // PCurve/Polygon installation must happen at most once per edge per wire, - // even when both halves of a seam pair appear in the wire's CoEdgeRefIds. + // even when both halves of a seam pair appear in the wire's CoEdgeIds. NCollection_Map aPCurvesInstalled(1, theCache.myTempAllocator); - const auto aProcessCoEdgeForFace = [&](const BRepGraph_CoEdgeRefId theCoEdgeRefId, - const bool theAddToWire) { - const BRepGraphInc::CoEdgeRef& aCoEdgeRef = theStorage.CoEdgeRef(theCoEdgeRefId); - if (aCoEdgeRef.IsRemoved || !aCoEdgeRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) + const auto aProcessCoEdgeForFace = [&](const BRepGraph_CoEdgeId theCoEdgeId, + const bool theAddToWire) { + if (!theCoEdgeId.IsValid(aStorage.NbCoEdges())) { return; } - const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aCoEdgeRef.CoEdgeDefId); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid(theStorage.NbEdges())) + if (aStorage.IsRemoved(theCoEdgeId)) { return; } - - TopoDS_Edge anEdge = aGetOrBuildEdge(aCoEdge.EdgeDefId); + const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(theCoEdgeId); + if (!aCoEdge.ChildEdgeId.IsValid(aStorage.NbEdges())) + { + return; + } + TopoDS_Edge anEdge = aGetOrBuildEdge(aCoEdge.ChildEdgeId); if (theAddToWire) { TopoDS_Edge anEdgeInWire = anEdge; anEdgeInWire.Orientation(aCoEdge.Orientation); - if (!aCoEdgeRef.LocalLocation.IsIdentity()) - { - anEdgeInWire.Location(aCoEdgeRef.LocalLocation); - } aBB.Add(aNewWire, anEdgeInWire); } - // Seam halves yield twice through CoEdgeRefIds; install PCurve/Polygon + // Seam halves yield twice through CoEdgeIds; install PCurve/Polygon // representations on the shared TEdge only once per edge. - if (!aPCurvesInstalled.Add(aCoEdge.EdgeDefId)) + if (!aPCurvesInstalled.Add(aCoEdge.ChildEdgeId)) { return; } - const BRepGraphInc::EdgeDef& anEdgeEnt = theStorage.Edge(aCoEdge.EdgeDefId); - - // Compute composed edge location within the face TShape hierarchy. - // This is wire-in-face Location * edge-in-wire Location. - const TopLoc_Location aEdgeInFaceLoc = theWireLocation * aCoEdgeRef.LocalLocation; - - // Temporarily apply composed location to the bare cached edge before UpdateEdge. - // UpdateEdge computes: stored_loc = L.Predivided(E.Location()) = L * E.Loc^-1. - // With L = Identity and E.Loc = aEdgeInFaceLoc: - // stored_loc = aEdgeInFaceLoc^-1 - // Later, BRep_Tool search computes the same loc from face/wire/edge context. OK - if (!aEdgeInFaceLoc.IsIdentity()) - { - anEdge.Location(aEdgeInFaceLoc); - } + const BRepGraphInc::EdgeDef& anEdgeEnt = aStorage.Edge(aCoEdge.ChildEdgeId); // For a seam, install BRep_CurveOnClosedSurface with PCurve()=FORWARD-half's // PCurve and PCurve2()=REVERSED-half's PCurve regardless of which half is // currently being visited. UV/range come from the FORWARD half too. const BRepGraphInc::CoEdgeDef* aFwdHalf = nullptr; const BRepGraphInc::CoEdgeDef* aRevHalf = nullptr; + if (aCoEdge.ChildEdgeId.IsValid(aStorage.NbEdges())) { - const NCollection_DynamicArray* aSiblings = - theStorage.ReverseIndex().CoEdgesOfEdge(aCoEdge.EdgeDefId); - if (aSiblings != nullptr) + const NCollection_LinearVector& aSiblings = + aStorage.EdgeRelations(aCoEdge.ChildEdgeId).CoEdgeIds; + for (const BRepGraph_CoEdgeId& aOtherId : aSiblings) { - for (const BRepGraph_CoEdgeId& aOtherId : *aSiblings) + if (aOtherId == theCoEdgeId) { - if (aOtherId == aCoEdgeRef.CoEdgeDefId) - continue; - const BRepGraphInc::CoEdgeDef& aOther = theStorage.CoEdge(aOtherId); - if (aOther.IsRemoved || aOther.FaceDefId != aCoEdge.FaceDefId - || aOther.Orientation == aCoEdge.Orientation) - { - continue; - } - if (aCoEdge.Orientation == TopAbs_FORWARD) - { - aFwdHalf = &aCoEdge; - aRevHalf = &aOther; - } - else - { - aFwdHalf = &aOther; - aRevHalf = &aCoEdge; - } - break; + continue; } + if (aStorage.IsRemoved(aOtherId)) + { + continue; + } + const BRepGraphInc::CoEdgeDef& aOther = aStorage.CoEdge(aOtherId); + if (aOther.FaceId != aCoEdge.FaceId || aOther.Orientation == aCoEdge.Orientation) + { + continue; + } + // Only consider siblings from the same wire to avoid matching + // orphaned coedges from dedup-merged wires/faces whose PCurves + // may reference a different surface. + if (aOther.ParentWireId != aCoEdge.ParentWireId) + { + continue; + } + if (aCoEdge.Orientation == TopAbs_FORWARD) + { + aFwdHalf = &aCoEdge; + aRevHalf = &aOther; + } + else + { + aFwdHalf = &aOther; + aRevHalf = &aCoEdge; + } + break; } } occ::handle aPC1, aPC2; double aPCFirst = 0.0, aPCLast = 0.0; - gp_Pnt2d aUV1, aUV2; - bool aHasUV = false; - GeomAbs_Shape aSeamContinuity = GeomAbs_C0; if (aFwdHalf != nullptr) { - if (aFwdHalf->Curve2DRepId.IsValid()) + if (aFwdHalf->Curve2DRepId.IsValid() + && aFwdHalf->Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + && !aStorage.IsRemoved(aFwdHalf->Curve2DRepId)) { - aPC1 = theStorage.Curve2DRep(aFwdHalf->Curve2DRepId).Curve; - aPCFirst = aFwdHalf->ParamFirst; - aPCLast = aFwdHalf->ParamLast; - aUV1 = aFwdHalf->UV1; - aUV2 = aFwdHalf->UV2; - aHasUV = true; + const BRepGraphInc::CoEdgeCurve2DRep& aFwdUse = + aStorage.CoEdgeCurve2DRep(aFwdHalf->Curve2DRepId); + aPC1 = aFwdUse.Curve; + aPCFirst = aFwdUse.ParamFirst; + aPCLast = aFwdUse.ParamLast; } - if (aRevHalf->Curve2DRepId.IsValid()) + if (aRevHalf->Curve2DRepId.IsValid() + && aRevHalf->Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + && !aStorage.IsRemoved(aRevHalf->Curve2DRepId)) { - aPC2 = theStorage.Curve2DRep(aRevHalf->Curve2DRepId).Curve; + const BRepGraphInc::CoEdgeCurve2DRep& aRevUse = + aStorage.CoEdgeCurve2DRep(aRevHalf->Curve2DRepId); + aPC2 = aRevUse.Curve; if (aPC1.IsNull()) { - aPCFirst = aRevHalf->ParamFirst; - aPCLast = aRevHalf->ParamLast; + aPCFirst = aRevUse.ParamFirst; + aPCLast = aRevUse.ParamLast; } } - // Seam continuity lives in BRepGraph_LayerRegularity with F1 == F2. - if (theRegularities != nullptr) - { - theRegularities->FindContinuity(aCoEdge.EdgeDefId, - aCoEdge.FaceDefId, - aCoEdge.FaceDefId, - &aSeamContinuity); - } } - else if (aCoEdge.Curve2DRepId.IsValid()) + else if (aCoEdge.Curve2DRepId.IsValid() + && aCoEdge.Curve2DRepId.IsValid(aStorage.NbCoEdgeCurves2D()) + && !aStorage.IsRemoved(aCoEdge.Curve2DRepId)) { - aPC1 = theStorage.Curve2DRep(aCoEdge.Curve2DRepId).Curve; - aPCFirst = aCoEdge.ParamFirst; - aPCLast = aCoEdge.ParamLast; - aUV1 = aCoEdge.UV1; - aUV2 = aCoEdge.UV2; - aHasUV = true; + const BRepGraphInc::CoEdgeCurve2DRep& aCoEdgeUse = + aStorage.CoEdgeCurve2DRep(aCoEdge.Curve2DRepId); + aPC1 = aCoEdgeUse.Curve; + aPCFirst = aCoEdgeUse.ParamFirst; + aPCLast = aCoEdgeUse.ParamLast; } if (!aPC1.IsNull() && !aPC2.IsNull()) { - if (aHasUV) - { - aBB.UpdateEdge(anEdge, - aPC1, - aPC2, - aFaceSurface, - TopLoc_Location(), - anEdgeEnt.Tolerance, - aUV1, - aUV2); - } - else - { - aBB.UpdateEdge(anEdge, aPC1, aPC2, aFaceSurface, TopLoc_Location(), anEdgeEnt.Tolerance); - } + gp_Pnt2d aUV1 = aPC1->Value(aPCFirst); + gp_Pnt2d aUV2 = aPC1->Value(aPCLast); + aBB.UpdateEdge(anEdge, + aPC1, + aPC2, + aFaceSurface, + TopLoc_Location(), + anEdgeEnt.Tolerance, + aUV1, + aUV2); aBB.Range(anEdge, aFaceSurface, TopLoc_Location(), aPCFirst, aPCLast); - - // Restore seam continuity (UpdateEdge creates CurveOnClosedSurface with C0). - if (aSeamContinuity != GeomAbs_C0) - { - // The stored CurveRepresentation location matches the edge's current location. - const TopLoc_Location aCRLoc = - aEdgeInFaceLoc.IsIdentity() ? TopLoc_Location() : aEdgeInFaceLoc.Inverted(); - const occ::handle& aTEdge = occ::down_cast(anEdge.TShape()); - if (!aTEdge.IsNull()) - { - for (occ::handle& aCR : aTEdge->ChangeCurves()) - { - if (!aCR.IsNull() && aCR->IsCurveOnClosedSurface() - && aCR->IsCurveOnSurface(aFaceSurface, aCRLoc)) - { - occ::down_cast(aCR)->Continuity(aSeamContinuity); - break; - } - } - } - } } else if (!aPC1.IsNull()) { - if (aHasUV) - { - aBB.UpdateEdge(anEdge, - aPC1, - aFaceSurface, - TopLoc_Location(), - anEdgeEnt.Tolerance, - aUV1, - aUV2); - } - else - { - aBB.UpdateEdge(anEdge, aPC1, aFaceSurface, TopLoc_Location(), anEdgeEnt.Tolerance); - } + gp_Pnt2d aUV1 = aPC1->Value(aPCFirst); + gp_Pnt2d aUV2 = aPC1->Value(aPCLast); + aBB.UpdateEdge(anEdge, + aPC1, + aFaceSurface, + TopLoc_Location(), + anEdgeEnt.Tolerance, + aUV1, + aUV2); aBB.Range(anEdge, aFaceSurface, TopLoc_Location(), aPCFirst, aPCLast); } else if (!aPC2.IsNull()) @@ -879,18 +777,24 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( occ::handle aPoly1, aPoly2; if (aFwdHalf != nullptr) { - if (aFwdHalf->Polygon2DRepId.IsValid()) + if (aFwdHalf->Polygon2DRepId.IsValid() + && aFwdHalf->Polygon2DRepId.IsValid(aStorage.NbCoEdgePolygons2D()) + && !aStorage.IsRemoved(aFwdHalf->Polygon2DRepId)) { - aPoly1 = theStorage.Polygon2DRep(aFwdHalf->Polygon2DRepId).Polygon; + aPoly1 = aStorage.CoEdgePolygon2DRep(aFwdHalf->Polygon2DRepId).Polygon; } - if (aRevHalf->Polygon2DRepId.IsValid()) + if (aRevHalf->Polygon2DRepId.IsValid() + && aRevHalf->Polygon2DRepId.IsValid(aStorage.NbCoEdgePolygons2D()) + && !aStorage.IsRemoved(aRevHalf->Polygon2DRepId)) { - aPoly2 = theStorage.Polygon2DRep(aRevHalf->Polygon2DRepId).Polygon; + aPoly2 = aStorage.CoEdgePolygon2DRep(aRevHalf->Polygon2DRepId).Polygon; } } - else if (aCoEdge.Polygon2DRepId.IsValid()) + else if (aCoEdge.Polygon2DRepId.IsValid() + && aCoEdge.Polygon2DRepId.IsValid(aStorage.NbCoEdgePolygons2D()) + && !aStorage.IsRemoved(aCoEdge.Polygon2DRepId)) { - aPoly1 = theStorage.Polygon2DRep(aCoEdge.Polygon2DRepId).Polygon; + aPoly1 = aStorage.CoEdgePolygon2DRep(aCoEdge.Polygon2DRepId).Polygon; } if (!aPoly1.IsNull() && !aPoly2.IsNull()) { @@ -910,84 +814,67 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( // BRep_Tool::PolygonOnTriangulation returns the correct one per orientation. { occ::handle aN1, aN2; - occ::handle aTri; if (aFwdHalf != nullptr) { - if (aFwdHalf->PolygonOnTriRepId.IsValid()) + if (aFwdHalf->PolygonOnTriRepId.IsValid() + && aFwdHalf->PolygonOnTriRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) + && !aStorage.IsRemoved(aFwdHalf->PolygonOnTriRepId)) { - const BRepGraphInc::PolygonOnTriRep& aRep = - theStorage.PolygonOnTriRep(aFwdHalf->PolygonOnTriRepId); - aN1 = aRep.Polygon; - if (aRep.TriangulationRepId.IsValid()) - { - aTri = theStorage.TriangulationRep(aRep.TriangulationRepId).Triangulation; - } + aN1 = aStorage.CoEdgePolygonOnTriRep(aFwdHalf->PolygonOnTriRepId).Polygon; } - if (aRevHalf->PolygonOnTriRepId.IsValid()) + if (aRevHalf->PolygonOnTriRepId.IsValid() + && aRevHalf->PolygonOnTriRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) + && !aStorage.IsRemoved(aRevHalf->PolygonOnTriRepId)) { - const BRepGraphInc::PolygonOnTriRep& aRep = - theStorage.PolygonOnTriRep(aRevHalf->PolygonOnTriRepId); - aN2 = aRep.Polygon; - if (aTri.IsNull() && aRep.TriangulationRepId.IsValid()) - { - aTri = theStorage.TriangulationRep(aRep.TriangulationRepId).Triangulation; - } + aN2 = aStorage.CoEdgePolygonOnTriRep(aRevHalf->PolygonOnTriRepId).Polygon; } } - else if (aCoEdge.PolygonOnTriRepId.IsValid()) + else if (aCoEdge.PolygonOnTriRepId.IsValid() + && aCoEdge.PolygonOnTriRepId.IsValid(aStorage.NbCoEdgePolygonsOnTri()) + && !aStorage.IsRemoved(aCoEdge.PolygonOnTriRepId)) { - const BRepGraphInc::PolygonOnTriRep& aRep = - theStorage.PolygonOnTriRep(aCoEdge.PolygonOnTriRepId); - aN1 = aRep.Polygon; - if (aRep.TriangulationRepId.IsValid()) - { - aTri = theStorage.TriangulationRep(aRep.TriangulationRepId).Triangulation; - } + aN1 = aStorage.CoEdgePolygonOnTriRep(aCoEdge.PolygonOnTriRepId).Polygon; } - if (!aTri.IsNull()) + if (!aFaceTriangulation.IsNull()) { if (!aN1.IsNull() && !aN2.IsNull()) { - aBB.UpdateEdge(anEdge, aN1, aN2, aTri, TopLoc_Location()); + aBB.UpdateEdge(anEdge, aN1, aN2, aFaceTriangulation, TopLoc_Location()); } else if (!aN1.IsNull()) { - aBB.UpdateEdge(anEdge, aN1, aTri, TopLoc_Location()); + aBB.UpdateEdge(anEdge, aN1, aFaceTriangulation, TopLoc_Location()); } else if (!aN2.IsNull()) { - aBB.UpdateEdge(anEdge, aN2, aTri, TopLoc_Location()); + aBB.UpdateEdge(anEdge, aN2, aFaceTriangulation, TopLoc_Location()); } } } - - // Reset temporary edge location after all UpdateEdge calls. - if (!aEdgeInFaceLoc.IsIdentity()) - { - anEdge.Location(TopLoc_Location()); - } }; if (aCachedWire != nullptr) { - aNewWire = TopoDS::Wire(*aCachedWire); - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) + aNewWire = TopoDS::Wire(*aCachedWire); + const BRepGraphInc::WireRelations& aWireRel = aStorage.WireRelations(theWireId); + for (const BRepGraph_CoEdgeId& aCoEdgeId : aWireRel.CoEdgeIds) { - aProcessCoEdgeForFace(aCoEdgeRefId, false); + aProcessCoEdgeForFace(aCoEdgeId, false); } } else { aBB.MakeWire(aNewWire); - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) + const BRepGraphInc::WireRelations& aWireRel = aStorage.WireRelations(theWireId); + for (const BRepGraph_CoEdgeId& aCoEdgeId : aWireRel.CoEdgeIds) { - aProcessCoEdgeForFace(aCoEdgeRefId, true); + aProcessCoEdgeForFace(aCoEdgeId, true); } theCache.Bind(aWireNodeId, aNewWire); } // Apply closure flag after all edges are added (BRep_Builder::Add may reset it). - if (aWire.IsClosed) + if (BRepGraph_CacheDerivedState::ComputeWireIsClosed(theGraph, theWireId)) { aNewWire.Closed(true); } @@ -995,132 +882,29 @@ TopoDS_Shape BRepGraphInc_Reconstruct::FaceWithCache( return aNewWire; }; - // Add wires to face: outer first, then inner. // Wire orientation must be applied before adding to face. - TopoDS_Wire anOuterWire; - NCollection_DynamicArray anInnerWires(Cache::THE_DEFAULT_INCREMENT, - theCache.myTempAllocator); - for (const BRepGraph_WireRefId& aWireRefId : aFace.WireRefIds) + const BRepGraphInc::FaceRelations& aFaceRel = aStorage.FaceRelations(theFaceId); + for (const BRepGraph_WireRefId& aWireRefId : aFaceRel.WireRefIds) { - const BRepGraphInc::WireRef& aWireRef = theStorage.WireRef(aWireRefId); - if (aWireRef.IsRemoved || !aWireRef.WireDefId.IsValid(theStorage.NbWires())) + if (aStorage.IsRemoved(aWireRefId)) { continue; } - TopoDS_Wire aWire = aBuildWireForFace(aWireRef.WireDefId, aWireRef.LocalLocation); + const BRepGraphInc::WireRef& aWireRef = aStorage.WireRef(aWireRefId); + if (!aWireRef.ChildWireId.IsValid(aStorage.NbWires())) + { + continue; + } + TopoDS_Wire aWire = aBuildWireForFace(aWireRef.ChildWireId); aWire.Orientation(aWireRef.Orientation); - if (!aWireRef.LocalLocation.IsIdentity()) - { - aWire.Location(aWireRef.LocalLocation); - } - if (aWireRef.IsOuter) - { - if (anOuterWire.IsNull()) - { - anOuterWire = aWire; - } - continue; - } - anInnerWires.Append(aWire); - } - if (!anOuterWire.IsNull()) - { - aBB.Add(aNewFace, anOuterWire); - } - for (const TopoDS_Wire& anInnerWire : anInnerWires) - { - aBB.Add(aNewFace, anInnerWire); - } - - // Add direct INTERNAL/EXTERNAL vertex children. - for (const BRepGraph_VertexRefId& aVRefId : aFace.VertexRefIds) - { - if (!aVRefId.IsValid(theStorage.NbVertexRefs())) - { - continue; - } - const BRepGraphInc::VertexRef& aVR = theStorage.VertexRef(aVRefId); - if (aVR.IsRemoved || !aVR.VertexDefId.IsValid()) - { - continue; - } - const BRepGraphInc::VertexDef& aVtxEnt = theStorage.Vertex(aVR.VertexDefId); - BRepGraph_NodeId aVtxId = aVR.VertexDefId; - const TopoDS_Shape* aVtxCached = theCache.Seek(aVtxId); - TopoDS_Shape aVtxShape; - if (aVtxCached != nullptr) - { - aVtxShape = *aVtxCached; - } - else - { - TopoDS_Vertex aNewVtx; - aBB.MakeVertex(aNewVtx, aVtxEnt.Point, aVtxEnt.Tolerance); - theCache.Bind(aVtxId, aNewVtx); - aVtxShape = aNewVtx; - } - aVtxShape.Orientation(aVR.Orientation); - if (!aVR.LocalLocation.IsIdentity()) - { - aVtxShape.Location(aVR.LocalLocation); - } - aBB.Add(aNewFace, aVtxShape); - } - - // Restore vertex point representations now that all edges and this face are cached. - // UpdateVertex modifies TShape in-place, so cached vertex shapes are updated. - NCollection_Map aProcessedVertices(1, theCache.myTempAllocator); - for (const BRepGraph_WireRefId& aWireRefId : aFace.WireRefIds) - { - const BRepGraphInc::WireRef& aWireRef = theStorage.WireRef(aWireRefId); - if (aWireRef.IsRemoved || !aWireRef.WireDefId.IsValid(theStorage.NbWires())) - { - continue; - } - const BRepGraphInc::WireDef& aWireEnt = theStorage.Wire(aWireRef.WireDefId); - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWireEnt.CoEdgeRefIds) - { - const BRepGraphInc::CoEdgeRef& aCoEdgeRef = theStorage.CoEdgeRef(aCoEdgeRefId); - if (aCoEdgeRef.IsRemoved || !aCoEdgeRef.CoEdgeDefId.IsValid(theStorage.NbCoEdges())) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = - theStorage.CoEdge(BRepGraph_CoEdgeId(aCoEdgeRef.CoEdgeDefId)); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid(theStorage.NbEdges())) - { - continue; - } - const BRepGraphInc::EdgeDef& anEdgeEnt = theStorage.Edge(aCoEdge.EdgeDefId); - if (anEdgeEnt.StartVertexRefId.IsValid()) - { - const BRepGraph_VertexId aVertexId = - theStorage.VertexRef(anEdgeEnt.StartVertexRefId).VertexDefId; - if (aProcessedVertices.Add(aVertexId)) - { - restoreVertexPointReps(theStorage, theParams, aVertexId, theCache, aBB); - } - } - if (anEdgeEnt.EndVertexRefId.IsValid()) - { - const BRepGraph_VertexId aVertexId = - theStorage.VertexRef(anEdgeEnt.EndVertexRefId).VertexDefId; - if (aProcessedVertices.Add(aVertexId)) - { - restoreVertexPointReps(theStorage, theParams, aVertexId, theCache, aBB); - } - } - } - } - - // NaturalRestriction must be set AFTER wires are added - // (BRep_Builder::Add may reset the flag). - if (aFace.NaturalRestriction) - { - aBB.NaturalRestriction(aNewFace, true); + aBB.Add(aNewFace, aWire); } aNewFace.Orientation(TopAbs_FORWARD); + if (aSupplement != nullptr) + { + replaySupplementAttachments(aSupplement, aFaceNodeId, aNewFace); + } theCache.Bind(aFaceNodeId, aNewFace); return aNewFace; } diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.hxx index 9fbd374b8f..7ab3d22189 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reconstruct.hxx @@ -16,14 +16,11 @@ #include #include +#include #include #include -#include - -class BRepGraphInc_Storage; -class BRepGraph_LayerParam; -class BRepGraph_LayerRegularity; +class BRepGraph; //! @brief Backend reconstruction helpers over incidence-table storage. //! @@ -55,98 +52,53 @@ public: { Cache& myCache; - explicit TempScope(Cache& theCache) - : myCache(theCache) - { - if (myCache.myTempScopeDepth == 0 && !myCache.myTempAllocator.IsNull()) - myCache.myTempAllocator->Reset(false); - ++myCache.myTempScopeDepth; - } - - ~TempScope() - { - --myCache.myTempScopeDepth; - if (myCache.myTempScopeDepth == 0 && !myCache.myTempAllocator.IsNull()) - myCache.myTempAllocator->Reset(false); - } + explicit TempScope(Cache& theCache); + ~TempScope(); }; - Cache() - : myAllocator(new NCollection_IncAllocator()), - myTempAllocator(new NCollection_IncAllocator()) - { - for (int aKindIdx = 0; aKindIdx < THE_KIND_COUNT; ++aKindIdx) - { - myKinds[aKindIdx] = - NCollection_DynamicArray(THE_DEFAULT_INCREMENT, myAllocator); - } - } + Cache(); //! Seek a cached shape. Returns nullptr if not yet cached. - const TopoDS_Shape* Seek(const BRepGraph_NodeId theNode) const - { - const int aKindIdx = static_cast(theNode.NodeKind); - if (aKindIdx < 0 || aKindIdx >= THE_KIND_COUNT) - return nullptr; - const NCollection_DynamicArray& aVec = myKinds[aKindIdx]; - if (theNode.Index >= aVec.Size()) - return nullptr; - const TopoDS_Shape& aShape = aVec.Value(static_cast(theNode.Index)); - return aShape.IsNull() ? nullptr : &aShape; - } + [[nodiscard]] Standard_EXPORT const TopoDS_Shape* Seek(const BRepGraph_NodeId theNode) const; //! Bind a reconstructed shape to a node. Grows the vector as needed. - void Bind(const BRepGraph_NodeId theNode, const TopoDS_Shape& theShape) - { - const int aKindIdx = static_cast(theNode.NodeKind); - if (aKindIdx < 0 || aKindIdx >= THE_KIND_COUNT) - return; - NCollection_DynamicArray& aVec = myKinds[aKindIdx]; - aVec.SetValue(static_cast(theNode.Index), theShape); - } + Standard_EXPORT void Bind(const BRepGraph_NodeId theNode, const TopoDS_Shape& theShape); //! Check if a node is already cached. - bool IsBound(const BRepGraph_NodeId theNode) const { return Seek(theNode) != nullptr; } + [[nodiscard]] bool IsBound(const BRepGraph_NodeId theNode) const + { + return Seek(theNode) != nullptr; + } }; //! Reconstruct a TopoDS_Shape from an entity node. //! Creates a local cache internally; shared vertices/edges are not reused //! across calls. - //! @param[in] theStorage incidence storage - //! @param[in] theNode entity node id + //! @param[in] theGraph graph owning the storage and caches + //! @param[in] theNode entity node id //! @return reconstructed shape - static Standard_EXPORT TopoDS_Shape - Node(const BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theNode, - const BRepGraph_LayerParam* theParams = nullptr, - const BRepGraph_LayerRegularity* theRegularities = nullptr); + static Standard_EXPORT TopoDS_Shape Node(const BRepGraph& theGraph, + const BRepGraph_NodeId theNode); //! Reconstruct a TopoDS_Shape with a shared cache for sub-shape reuse. //! Vertices and edges already in theCache are returned directly. - //! @param[in] theStorage incidence storage - //! @param[in] theNode entity node id - //! @param[in,out] theCache shared cache for vertex/edge/face shapes + //! @param[in] theGraph graph owning the storage and caches + //! @param[in] theNode entity node id + //! @param[in,out] theCache shared cache for vertex/edge/face shapes //! @return reconstructed shape - static Standard_EXPORT TopoDS_Shape - Node(const BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theNode, - Cache& theCache, - const BRepGraph_LayerParam* theParams = nullptr, - const BRepGraph_LayerRegularity* theRegularities = nullptr); + static Standard_EXPORT TopoDS_Shape Node(const BRepGraph& theGraph, + const BRepGraph_NodeId theNode, + Cache& theCache); //! Reconstruct a face with shared edge/vertex cache for multi-face contexts. - //! @param[in] theStorage incidence storage - //! @param[in] theFaceId face entity id - //! @param[in,out] theCache shared cache for edge and vertex shapes + //! @param[in] theGraph graph owning the storage and caches + //! @param[in] theFaceId face entity id + //! @param[in,out] theCache shared cache for edge and vertex shapes //! @return reconstructed face shape - static Standard_EXPORT TopoDS_Shape - FaceWithCache(const BRepGraphInc_Storage& theStorage, - const BRepGraph_FaceId theFaceId, - Cache& theCache, - const BRepGraph_LayerParam* theParams = nullptr, - const BRepGraph_LayerRegularity* theRegularities = nullptr); + static Standard_EXPORT TopoDS_Shape FaceWithCache(const BRepGraph& theGraph, + const BRepGraph_FaceId theFaceId, + Cache& theCache); -private: BRepGraphInc_Reconstruct() = delete; }; diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reference.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reference.hxx index a4c8f1b710..0068c8cc1c 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reference.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Reference.hxx @@ -14,19 +14,19 @@ #ifndef _BRepGraphInc_Reference_HeaderFile #define _BRepGraphInc_Reference_HeaderFile +#include #include #include -#include #include //! @brief Managed reference entry structs for the incidence-table storage. //! -//! Each reference entry extends BaseRef with payload fields describing -//! how a child definition is used by its parent (orientation, location). +//! Each reference entry extends BaseRef with representation fields describing +//! how a child definition is used by its parent. //! Reference entries are stored in flat per-kind vectors in BRepGraphInc_Storage //! and support mutation tracking and soft-removal. //! Not every definition kind has a dedicated Ref kind by design: -//! - Edge usage is represented by CoEdgeRef -> CoEdgeDef (which then targets EdgeDef) +//! - CoEdge usage is stored directly on CoEdgeDef and ordered through WireRelations //! - Compound children use ChildRef (heterogeneous NodeId target) //! - Product children use OccurrenceRef (placement owned by OccurrenceDef) //! - CompSolid children use SolidRef @@ -41,9 +41,10 @@ struct BaseRef { using TypeId = BRepGraph_RefId; - BRepGraph_NodeId ParentId; //!< Parent topology node owning this reference usage - uint32_t OwnGen = 0; //!< Per-reference mutation counter - bool IsRemoved = false; //!< Soft-removal flag + //! Persistent per-kind UID counter value. + //! 0 = invalid sentinel (not yet allocated). Valid UIDs start at 1. + //! Kind is implicit from the concrete struct type (ShellRef, FaceRef, etc.). + uint32_t UID = 0; }; //! Shell reference storage entry. @@ -51,9 +52,9 @@ struct ShellRef : public BaseRef { using TypeId = BRepGraph_ShellRefId; - BRepGraph_ShellId ShellDefId; - TopAbs_Orientation Orientation = TopAbs_FORWARD; - TopLoc_Location LocalLocation; + BRepGraph_SolidId ParentSolidId; //!< Parent solid identifier + BRepGraph_ShellId ChildShellId; //!< Child shell identifier + ParityOrientation Orientation = TopAbs_FORWARD; //!< Orientation within parent }; //! Face reference storage entry. @@ -61,9 +62,9 @@ struct FaceRef : public BaseRef { using TypeId = BRepGraph_FaceRefId; - BRepGraph_FaceId FaceDefId; - TopAbs_Orientation Orientation = TopAbs_FORWARD; - TopLoc_Location LocalLocation; + BRepGraph_ShellId ParentShellId; //!< Parent shell identifier + BRepGraph_FaceId ChildFaceId; //!< Child face identifier + ParityOrientation Orientation = TopAbs_FORWARD; //!< Orientation within parent }; //! Wire reference storage entry. @@ -71,22 +72,9 @@ struct WireRef : public BaseRef { using TypeId = BRepGraph_WireRefId; - BRepGraph_WireId WireDefId; - bool IsOuter = false; - TopAbs_Orientation Orientation = TopAbs_FORWARD; - TopLoc_Location LocalLocation; -}; - -//! CoEdge reference storage entry. -//! No Orientation field: CoEdgeDef::Orientation already owns the edge-on-face sense, -//! coupled with PCurve parametrization, so duplicating orientation here would -//! create a second competing source of truth. -struct CoEdgeRef : public BaseRef -{ - using TypeId = BRepGraph_CoEdgeRefId; - - BRepGraph_CoEdgeId CoEdgeDefId; - TopLoc_Location LocalLocation; + BRepGraph_FaceId ParentFaceId; //!< Parent face identifier + BRepGraph_WireId ChildWireId; //!< Child wire identifier + ParityOrientation Orientation = TopAbs_FORWARD; //!< Orientation within parent }; //! Vertex reference storage entry. @@ -94,10 +82,9 @@ struct VertexRef : public BaseRef { using TypeId = BRepGraph_VertexRefId; - BRepGraph_VertexId VertexDefId; - TopAbs_Orientation Orientation = - TopAbs_INTERNAL; //!< INTERNAL: B-Rep vertex classification convention - TopLoc_Location LocalLocation; + BRepGraph_VertexId ChildVertexId; //!< Child vertex identifier + BRepGraph_EdgeId ParentEdgeId; //!< Edge that owns this vertex reference + ParityOrientation Orientation = TopAbs_FORWARD; //!< Orientation within parent }; //! Solid reference storage entry. @@ -105,9 +92,9 @@ struct SolidRef : public BaseRef { using TypeId = BRepGraph_SolidRefId; - BRepGraph_SolidId SolidDefId; - TopAbs_Orientation Orientation = TopAbs_FORWARD; - TopLoc_Location LocalLocation; + BRepGraph_CompSolidId ParentCompSolidId; //!< Parent compsolid identifier + BRepGraph_SolidId ChildSolidId; //!< Child solid identifier + ParityOrientation Orientation = TopAbs_FORWARD; //!< Orientation within parent }; //! Child reference storage entry. @@ -115,9 +102,10 @@ struct ChildRef : public BaseRef { using TypeId = BRepGraph_ChildRefId; - BRepGraph_NodeId ChildDefId; - TopAbs_Orientation Orientation = TopAbs_FORWARD; - TopLoc_Location LocalLocation; + BRepGraph_CompoundId ParentCompoundId; //!< Parent compound identifier + BRepGraph_NodeId ChildNodeId; //!< Child node identifier (heterogeneous) + ParityOrientation Orientation = TopAbs_FORWARD; //!< Orientation within parent + TopLoc_Location LocalLocation; //!< Location relative to parent }; //! Occurrence reference storage entry. @@ -127,7 +115,8 @@ struct OccurrenceRef : public BaseRef { using TypeId = BRepGraph_OccurrenceRefId; - BRepGraph_OccurrenceId OccurrenceDefId; + BRepGraph_ProductId ParentProductId; + BRepGraph_OccurrenceId ChildOccurrenceId; TopLoc_Location LocalLocation; //!< Placement relative to parent product }; diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Relations.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Relations.hxx new file mode 100644 index 0000000000..fbf1fc460b --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Relations.hxx @@ -0,0 +1,100 @@ +// 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. + +#ifndef _BRepGraphInc_Relations_HeaderFile +#define _BRepGraphInc_Relations_HeaderFile + +#include +#include +#include + +//! @brief Centralized topology relation representations for BRepGraph incidence storage. +//! +//! Relation structs hold ordered child-use lists and incoming incidence indexes +//! outside definition records. Definitions stay focused on intrinsic geometry +//! and flags; reusable child/parent edges live in reference records or coedge +//! use records. +namespace BRepGraphInc +{ + +//! @brief Topology relations for face definitions. +struct FaceRelations +{ + NCollection_LinearVector WireRefIds; //!< Wire references owned by this face + NCollection_LinearVector + ParentFaceRefIds; //!< Upstream face references (compound hierarchy) +}; + +//! @brief Topology relations for wire definitions. +struct WireRelations +{ + NCollection_LinearVector CoEdgeIds; //!< Coedge identifiers in this wire + NCollection_LinearVector ParentWireRefIds; //!< Upstream wire references +}; + +//! @brief Topology relations for edge definitions. +struct EdgeRelations +{ + NCollection_LinearVector CoEdgeIds; //!< Coedge identifiers using this edge +}; + +//! @brief Topology relations for shell definitions. +struct ShellRelations +{ + NCollection_LinearVector FaceRefIds; //!< Face references in this shell + NCollection_LinearVector ParentShellRefIds; //!< Upstream shell references +}; + +//! @brief Topology relations for solid definitions. +struct SolidRelations +{ + NCollection_LinearVector ShellRefIds; //!< Shell references in this solid + NCollection_LinearVector ParentSolidRefIds; //!< Upstream solid references +}; + +//! @brief Topology relations for compound definitions. +struct CompoundRelations +{ + NCollection_LinearVector ChildRefIds; //!< Child references in this compound +}; + +//! @brief Topology relations for compsolid definitions. +struct CompSolidRelations +{ + NCollection_LinearVector + SolidRefIds; //!< Solid references in this compsolid +}; + +//! @brief Topology relations for vertex definitions. +struct VertexRelations +{ + NCollection_LinearVector EdgeIds; //!< Edge identifiers sharing this vertex +}; + +//! @brief Topology relations for product definitions. +struct ProductRelations +{ + NCollection_LinearVector + OccurrenceRefIds; //!< Occurrence references under this product +}; + +//! @brief Topology relations for occurrence definitions. +struct OccurrenceRelations +{ + NCollection_LinearVector + ParentOccurrenceRefIds; //!< Upstream occurrence references +}; + +} // namespace BRepGraphInc + +#endif // _BRepGraphInc_Relations_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_RepId.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_RepId.hxx new file mode 100644 index 0000000000..d74e50d8b7 --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_RepId.hxx @@ -0,0 +1,221 @@ +// 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. + +#ifndef _BRepGraphInc_RepId_HeaderFile +#define _BRepGraphInc_RepId_HeaderFile + +#include +#include + +#include +#include +#include + +class BRepGraph; + +//! Lightweight typed index into a per-kind use-record vector inside BRepGraph. +//! +//! The pair (Kind, Index) forms a unique use-record identifier within one graph +//! instance. Default-constructed RepId has Index = UINT32_MAX (invalid). +//! +//! Use records are session-local representation slots with no public stable UID, +//! graph-level lock state, independent mutation generation, or layer callbacks. +//! They do have a soft-removed state so an owner can clear and later reuse its slot. +struct BRepGraph_RepId +{ + //! Enumeration of use-record kinds. + enum class Kind : int + { + EdgeCurve3D = 0, //!< Geom_Curve use for edges + EdgePolygon3D = 1, //!< Poly_Polygon3D use for edges + CoEdgeCurve2D = 2, //!< Geom2d_Curve use for coedges + CoEdgePolygon2D = 3, //!< Poly_Polygon2D use for coedges + CoEdgePolygonOnTri = 4, //!< Poly_PolygonOnTriangulation use for coedges + FaceSurface = 5, //!< Geom_Surface use for faces + FaceTriangulation = 6 //!< Poly_Triangulation use for faces + }; + + //! True if the kind value is one of the supported use-record kinds. + static bool IsValidKind(const Kind theKind) + { + switch (theKind) + { + case Kind::EdgeCurve3D: + case Kind::EdgePolygon3D: + case Kind::CoEdgeCurve2D: + case Kind::CoEdgePolygon2D: + case Kind::CoEdgePolygonOnTri: + case Kind::FaceSurface: + case Kind::FaceTriangulation: + return true; + } + return false; + } + + //! Compile-time typed wrapper around BRepGraph_RepId. + template + struct Typed + { + static constexpr uint32_t THE_START_INDEX = 0u; + static constexpr uint32_t THE_INVALID_INDEX = std::numeric_limits::max(); + + uint32_t Index; + + //! Default: invalid. + Typed() + : Index(THE_INVALID_INDEX) + { + } + + //! Construct from index. + explicit Typed(const uint32_t theIdx) + : Index(theIdx) + { + } + + //! First valid id in a dense sequence. + [[nodiscard]] static Typed Start() { return Typed(THE_START_INDEX); } + + //! Invalid sentinel id. + [[nodiscard]] static Typed Invalid() { return Typed(); } + + //! True if this id points to an allocated slot. + [[nodiscard]] bool IsValid() const { return Index != THE_INVALID_INDEX; } + + //! True if this id is within [0, theMaxCount). + [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const + { + return IsValid() && Index < theMaxCount; + } + + //! Implicit conversion to untyped RepId. + operator BRepGraph_RepId() const { return BRepGraph_RepId(TheKind, Index); } + + //! Return true if this use entry has been soft-removed in the given graph. + [[nodiscard]] bool IsRemoved(const BRepGraph& theGraph) const + { + return BRepGraph_RepId(*this).IsRemoved(theGraph); + } + + //! Pre-increment. + Typed& operator++() + { + Standard_ASSERT_VOID(Index != THE_INVALID_INDEX, "pre-increment on invalid use id"); + ++Index; + return *this; + } + + //! Post-increment. + Typed operator++(int) + { + Standard_ASSERT_VOID(Index != THE_INVALID_INDEX, "post-increment on invalid use id"); + Typed aPrev = *this; + ++Index; + return aPrev; + } + + bool operator==(const Typed& theOther) const { return Index == theOther.Index; } + + bool operator!=(const Typed& theOther) const { return Index != theOther.Index; } + + bool operator<(const Typed& theOther) const { return Index < theOther.Index; } + + bool operator<=(const Typed& theOther) const { return Index <= theOther.Index; } + + bool operator>(const Typed& theOther) const { return Index > theOther.Index; } + + bool operator>=(const Typed& theOther) const { return Index >= theOther.Index; } + }; + + static constexpr uint32_t THE_START_INDEX = 0u; + static constexpr uint32_t THE_INVALID_INDEX = std::numeric_limits::max(); + + Kind RepKind; + uint32_t Index; + + //! Default: invalid RepId. + BRepGraph_RepId() + : RepKind(Kind::EdgeCurve3D), + Index(THE_INVALID_INDEX) + { + } + + BRepGraph_RepId(const Kind theKind, const uint32_t theIdx) + : RepKind(theKind), + Index(theIdx) + { + } + + //! True if this id points to an allocated slot. + [[nodiscard]] bool IsValid() const { return IsValidKind(RepKind) && Index != THE_INVALID_INDEX; } + + //! True if this id is within [0, theMaxCount). + [[nodiscard]] bool IsValid(const uint32_t theMaxCount) const + { + return IsValid() && Index < theMaxCount; + } + + bool operator==(const BRepGraph_RepId& theOther) const + { + return RepKind == theOther.RepKind && Index == theOther.Index; + } + + bool operator!=(const BRepGraph_RepId& theOther) const { return !(*this == theOther); } + + bool operator<(const BRepGraph_RepId& theOther) const + { + if (RepKind != theOther.RepKind) + { + return static_cast(RepKind) < static_cast(theOther.RepKind); + } + return Index < theOther.Index; + } + + //! Return true if this use entry has been soft-removed in the given graph. + [[nodiscard]] Standard_EXPORT bool IsRemoved(const BRepGraph& theGraph) const; +}; + +// Convenience type aliases for typed RepIds. +using BRepGraph_EdgeCurve3DRepId = BRepGraph_RepId::Typed; +using BRepGraph_EdgePolygon3DRepId = BRepGraph_RepId::Typed; +using BRepGraph_CoEdgeCurve2DRepId = BRepGraph_RepId::Typed; +using BRepGraph_CoEdgePolygon2DRepId = + BRepGraph_RepId::Typed; +using BRepGraph_CoEdgePolygonOnTriRepId = + BRepGraph_RepId::Typed; +using BRepGraph_FaceSurfaceRepId = BRepGraph_RepId::Typed; +using BRepGraph_FaceTriangulationRepId = + BRepGraph_RepId::Typed; + +template <> +struct std::hash +{ + size_t operator()(const BRepGraph_RepId& theId) const noexcept + { + size_t aCombination[2]; + aCombination[0] = opencascade::hash(static_cast(theId.RepKind)); + aCombination[1] = opencascade::hash(theId.Index); + return opencascade::hashBytes(aCombination, sizeof(aCombination)); + } +}; + +template +struct std::hash> +{ + size_t operator()(const BRepGraph_RepId::Typed& theId) const noexcept + { + return std::hash{}(theId.Index); + } +}; + +#endif // _BRepGraphInc_RepId_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Representation.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Representation.hxx index 58e7b44fad..ee36b3f432 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Representation.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Representation.hxx @@ -14,8 +14,8 @@ #ifndef _BRepGraphInc_Representation_HeaderFile #define _BRepGraphInc_Representation_HeaderFile -#include - +#include +#include #include #include #include @@ -23,81 +23,81 @@ #include #include #include +#include -//! @brief Geometry and mesh representation structs for the incidence-table model. +//! @brief Geometry representation records for the BRepGraph incidence storage. //! -//! Each representation struct wraps a single piece of geometry or discretization -//! data (surface, curve, triangulation, polygon) with a typed RepId address -//! and lifecycle tracking fields. Representations are stored in flat per-kind -//! vectors in BRepGraphInc_Storage and referenced from definitions by typed RepId. +//! Curve parameter ranges live in curve-use records because the use record +//! is not reusable - the interval belongs to the owning edge/coedge curve use, +//! not to a shared geometric curve. namespace BRepGraphInc { -//! Fields shared by every representation entity. -struct BaseRep +//! 3D curve use for edges. Owned by a single edge. +struct EdgeCurve3DRep { - using TypeId = BRepGraph_RepId; + using TypeId = BRepGraph_EdgeCurve3DRepId; - uint32_t OwnGen = 0; //!< Per-rep mutation counter - bool IsRemoved = false; //!< Soft-removal flag + BRepGraph_EdgeId ParentEdgeId; //!< Owning edge identifier + occ::handle Curve; //!< 3D curve geometry + double ParamFirst = 0.0; //!< First curve parameter + double ParamLast = 0.0; //!< Last curve parameter }; -//! Surface geometry representation for faces. -struct SurfaceRep : public BaseRep +//! 3D polygon use for edges. Owned by a single edge. +struct EdgePolygon3DRep { - using TypeId = BRepGraph_SurfaceRepId; + using TypeId = BRepGraph_EdgePolygon3DRepId; - occ::handle Surface; //!< The geometric surface + BRepGraph_EdgeId ParentEdgeId; //!< Owning edge identifier + occ::handle Polygon; //!< 3D polygon geometry }; -//! 3D curve geometry representation for edges. -struct Curve3DRep : public BaseRep +//! 2D parametric curve (PCurve) use for coedges. Owned by a single coedge. +struct CoEdgeCurve2DRep { - using TypeId = BRepGraph_Curve3DRepId; + using TypeId = BRepGraph_CoEdgeCurve2DRepId; - occ::handle Curve; //!< The 3D curve geometry + BRepGraph_CoEdgeId ParentCoEdgeId; //!< Owning coedge identifier + occ::handle Curve; //!< 2D parametric curve geometry + double ParamFirst = 0.0; //!< First curve parameter + double ParamLast = 0.0; //!< Last curve parameter }; -//! 2D parametric curve (PCurve) representation for coedges. -struct Curve2DRep : public BaseRep +//! 2D polygon-on-surface use for coedges. Owned by a single coedge. +struct CoEdgePolygon2DRep { - using TypeId = BRepGraph_Curve2DRepId; + using TypeId = BRepGraph_CoEdgePolygon2DRepId; - occ::handle Curve; //!< The 2D parametric curve + BRepGraph_CoEdgeId ParentCoEdgeId; //!< Owning coedge identifier + occ::handle Polygon; //!< 2D polygon geometry }; -//! Triangulation mesh representation for faces. -struct TriangulationRep : public BaseRep +//! Polygon-on-triangulation use for coedges. Owned by a single coedge. +struct CoEdgePolygonOnTriRep { - using TypeId = BRepGraph_TriangulationRepId; + using TypeId = BRepGraph_CoEdgePolygonOnTriRepId; - occ::handle Triangulation; //!< The mesh + BRepGraph_CoEdgeId ParentCoEdgeId; //!< Owning coedge identifier + occ::handle Polygon; //!< Polygon-on-triangulation geometry }; -//! 3D polygon discretization for edges. -struct Polygon3DRep : public BaseRep +//! Surface geometry use for faces. Owned by a single face. +struct FaceSurfaceRep { - using TypeId = BRepGraph_Polygon3DRepId; + using TypeId = BRepGraph_FaceSurfaceRepId; - occ::handle Polygon; //!< The 3D polygon + BRepGraph_FaceId ParentFaceId; //!< Owning face identifier + occ::handle Surface; //!< Surface geometry }; -//! 2D polygon-on-surface discretization for coedges. -struct Polygon2DRep : public BaseRep +//! Triangulation mesh use for faces. Owned by a single face. +struct FaceTriangulationRep { - using TypeId = BRepGraph_Polygon2DRepId; + using TypeId = BRepGraph_FaceTriangulationRepId; - occ::handle Polygon; //!< The 2D polygon on surface parametric space -}; - -//! Polygon-on-triangulation for coedges. -//! Links a polygon to a specific triangulation rep (global index, not face-local). -struct PolygonOnTriRep : public BaseRep -{ - using TypeId = BRepGraph_PolygonOnTriRepId; - - occ::handle Polygon; //!< Polygon indices into triangulation - BRepGraph_TriangulationRepId TriangulationRepId; //!< Typed id into myTriangulationsRep + BRepGraph_FaceId ParentFaceId; //!< Owning face identifier + occ::handle Triangulation; //!< Triangulation mesh }; } // namespace BRepGraphInc diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx deleted file mode 100644 index 59df5e775e..0000000000 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.cxx +++ /dev/null @@ -1,2324 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - -template -BRepGraph_VertexId resolveVertexDefId( - const NCollection_DynamicArray& theVertexRefs, - const T theRefId) -{ - if (!theRefId.IsValid(static_cast(theVertexRefs.Size()))) - { - return BRepGraph_VertexId(); - } - return theVertexRefs.Value(static_cast(theRefId.Index)).VertexDefId; -} - -template -bool containsIndexInTable(const NCollection_DynamicArray>& theIdx, - const TKey theKey, - const TVal theVal) -{ - if (!theKey.IsValid(static_cast(theIdx.Size()))) - { - return false; - } - const NCollection_DynamicArray& aVec = theIdx.Value(static_cast(theKey.Index)); - for (const TVal& anElem : aVec) - { - if (anElem == theVal) - { - return true; - } - } - return false; -} - -static bool hasActiveFaceForEdgeInCoEdges( - const NCollection_DynamicArray& theCoEdgeIds, - const NCollection_DynamicArray& theCoEdges, - const BRepGraph_EdgeId theEdgeId, - const BRepGraph_FaceId theFaceId) -{ - for (const BRepGraph_CoEdgeId& aCoEdgeId : theCoEdgeIds) - { - if (!aCoEdgeId.IsValid(static_cast(theCoEdges.Size()))) - { - continue; - } - - const BRepGraphInc::CoEdgeDef& aCoEdge = theCoEdges.Value(static_cast(aCoEdgeId.Index)); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid() || !aCoEdge.FaceDefId.IsValid()) - { - continue; - } - if (aCoEdge.EdgeDefId == theEdgeId && aCoEdge.FaceDefId == theFaceId) - { - return true; - } - } - return false; -} - -} // namespace - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::Clear() -{ - myEdgeToWires.Clear(); - myEdgeToFaces.Clear(); - myEdgeToCoEdges.Clear(); - myVertexToEdges.Clear(); - myWireToFaces.Clear(); - myFaceToShells.Clear(); - myShellToSolids.Clear(); - myCompoundsOfSolid.Clear(); - myCompSolidsOfSolid.Clear(); - myCompoundsOfShell.Clear(); - myCompoundsOfFace.Clear(); - myCompoundsOfCompound.Clear(); - myCompoundsOfCompSolid.Clear(); - myCompoundsOfWire.Clear(); - myCompoundsOfEdge.Clear(); - myCompoundsOfVertex.Clear(); - myCoEdgeToWires.Clear(); - myProductToOccurrences.Clear(); - myNbIndexedCoEdges = 0; -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::Build(const BRepGraphInc_Storage& theStorage) -{ - Build(theStorage.myVertices.Entities, - theStorage.myEdges.Entities, - theStorage.myCoEdges.Entities, - theStorage.myWires.Entities, - theStorage.myFaces.Entities, - theStorage.myShells.Entities, - theStorage.mySolids.Entities, - theStorage.myCompounds.Entities, - theStorage.myCompSolids.Entities, - theStorage.myShellRefs.Refs, - theStorage.myFaceRefs.Refs, - theStorage.myWireRefs.Refs, - theStorage.myCoEdgeRefs.Refs, - theStorage.mySolidRefs.Refs, - theStorage.myChildRefs.Refs, - theStorage.myVertexRefs.Refs); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::Build( - const NCollection_DynamicArray& theVertices, - const NCollection_DynamicArray& theEdges, - const NCollection_DynamicArray& theCoEdges, - const NCollection_DynamicArray& theWires, - const NCollection_DynamicArray& theFaces, - const NCollection_DynamicArray& theShells, - const NCollection_DynamicArray& theSolids, - const NCollection_DynamicArray& theCompounds, - const NCollection_DynamicArray& theCompSolids, - const NCollection_DynamicArray& theShellRefs, - const NCollection_DynamicArray& theFaceRefs, - const NCollection_DynamicArray& theWireRefs, - const NCollection_DynamicArray& theCoEdgeRefs, - const NCollection_DynamicArray& theSolidRefs, - const NCollection_DynamicArray& theChildRefs, - const NCollection_DynamicArray& theVertexRefs) -{ - myNbIndexedCoEdges = 0; - - // Reconstruct outer index tables with allocator if set. - if (!myAllocator.IsNull()) - { - myEdgeToWires = TypedIndexTable(256, myAllocator); - myEdgeToFaces = TypedIndexTable(256, myAllocator); - myVertexToEdges = TypedIndexTable(256, myAllocator); - myWireToFaces = TypedIndexTable(256, myAllocator); - myFaceToShells = TypedIndexTable(256, myAllocator); - myShellToSolids = TypedIndexTable(256, myAllocator); - } - else - { - Clear(); - } - - // Helper: resolve a VertexRefId to the corresponding VertexDefId (BRepGraph_VertexId). - // Returns an invalid id if the ref id is invalid or out of range. - - // Scan edges for max vertex index to pre-size myVertexToEdges. - uint32_t aNewVertexCapacity = 0; - const uint32_t aNbEdges = static_cast(theEdges.Size()); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdge = theEdges.Value(static_cast(anEdgeId.Index)); - if (anEdge.IsRemoved) - { - continue; - } - const BRepGraph_VertexId aStartVtx = resolveVertexDefId(theVertexRefs, anEdge.StartVertexRefId); - const BRepGraph_VertexId anEndVtx = resolveVertexDefId(theVertexRefs, anEdge.EndVertexRefId); - if (aStartVtx.IsValid() && aStartVtx.Index + 1 > aNewVertexCapacity) - { - aNewVertexCapacity = aStartVtx.Index + 1; - } - if (anEndVtx.IsValid() && anEndVtx.Index + 1 > aNewVertexCapacity) - { - aNewVertexCapacity = anEndVtx.Index + 1; - } - } - - // Pre-size all outer vectors to their known key range. - // Pass allocator so inner vectors use IncAllocator for O(1) alloc/free. - preSize(myVertexToEdges, aNewVertexCapacity, myAllocator); - preSize(myEdgeToWires, static_cast(theEdges.Size()), myAllocator); - preSize(myEdgeToFaces, static_cast(theEdges.Size()), myAllocator); - preSize(myWireToFaces, static_cast(theWires.Size()), myAllocator); - preSize(myFaceToShells, static_cast(theFaces.Size()), myAllocator); - preSize(myShellToSolids, static_cast(theShells.Size()), myAllocator); - - // Vertex -> Edges: scan edge entities for start/end vertex indices. - // Closed edges have StartVertexRefId and EndVertexRefId resolving to the same VertexDefId, - // so skip duplicate. - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdge = theEdges.Value(static_cast(anEdgeId.Index)); - if (anEdge.IsRemoved) - { - continue; - } - const BRepGraph_VertexId aStartVtx = resolveVertexDefId(theVertexRefs, anEdge.StartVertexRefId); - const BRepGraph_VertexId anEndVtx = resolveVertexDefId(theVertexRefs, anEdge.EndVertexRefId); - if (aStartVtx.IsValid()) - { - appendDirect(myVertexToEdges, aStartVtx.Index, anEdgeId); - } - if (anEndVtx.IsValid() && anEndVtx != aStartVtx) - { - appendDirect(myVertexToEdges, anEndVtx.Index, anEdgeId); - } - } - - // Edge -> Wires: iterate wire entities and their CoEdgeRefIds for O(1) parent lookup. - const uint32_t aNbWires = static_cast(theWires.Size()); - for (BRepGraph_WireId aWireId(0); aWireId.IsValid(aNbWires); ++aWireId) - { - const BRepGraphInc::WireDef& aWire = theWires.Value(static_cast(aWireId.Index)); - if (aWire.IsRemoved) - { - continue; - } - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) - { - if (!aCoEdgeRefId.IsValid(static_cast(theCoEdgeRefs.Size()))) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef = - theCoEdgeRefs.Value(static_cast(aCoEdgeRefId.Index)); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(static_cast(theCoEdges.Size()))) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = - theCoEdges.Value(static_cast(aRef.CoEdgeDefId.Index)); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid()) - { - continue; - } - appendDirect(myEdgeToWires, aCoEdge.EdgeDefId.Index, aWireId); - } - } - - // Edge -> CoEdges: derive from CoEdge.EdgeDefId field. - preSize(myEdgeToCoEdges, static_cast(theEdges.Size()), myAllocator); - const uint32_t aNbCoEdges = static_cast(theCoEdges.Size()); - for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aNbCoEdges); ++aCoEdgeId) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = theCoEdges.Value(static_cast(aCoEdgeId.Index)); - if (aCoEdge.IsRemoved) - { - continue; - } - if (aCoEdge.EdgeDefId.IsValid()) - { - appendDirect(myEdgeToCoEdges, aCoEdge.EdgeDefId.Index, aCoEdgeId); - } - } - myNbIndexedCoEdges = static_cast(theCoEdges.Size()); - - // Edge -> Faces: derive from CoEdge.FaceDefId (replaces legacy PCurve-based derivation). - // Seam edges have two CoEdges with same FaceDefId but opposite Orientation. - // Deduplicate per edge using the edge->coedges index built above. - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - if (theEdges.Value(static_cast(anEdgeId.Index)).IsRemoved) - { - continue; - } - const NCollection_DynamicArray* aCoEdgeIdxs = - seekVec(myEdgeToCoEdges, anEdgeId.Index); - if (aCoEdgeIdxs == nullptr) - { - continue; - } - const size_t aNbCE = aCoEdgeIdxs->Size(); - - // Collect face indices from coedges (stack-allocated for small counts). - NCollection_LocalArray aFaces(aNbCE); - size_t aNbFaces = 0; - for (const auto& aCoEdgeId : *aCoEdgeIdxs) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = - theCoEdges.Value(static_cast(aCoEdgeId.Index)); - if (aCoEdge.FaceDefId.IsValid()) - { - aFaces[aNbFaces++] = aCoEdge.FaceDefId.Index; - } - } - if (aNbFaces == 0) - { - continue; - } - - std::sort(static_cast(aFaces), static_cast(aFaces) + aNbFaces); - - // Append unique sorted values. - uint32_t aPrev = UINT32_MAX; - for (size_t i = 0; i < aNbFaces; ++i) - { - if (aFaces[i] != aPrev) - { - appendDirect(myEdgeToFaces, anEdgeId.Index, BRepGraph_FaceId(aFaces[i])); - aPrev = aFaces[i]; - } - } - } - - // Wire -> Faces: iterate face entities and their WireRefIds for O(1) parent lookup. - const uint32_t aNbFaces = static_cast(theFaces.Size()); - for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) - { - const BRepGraphInc::FaceDef& aFace = theFaces.Value(static_cast(aFaceId.Index)); - if (aFace.IsRemoved) - { - continue; - } - for (const BRepGraph_WireRefId& aWireRefId : aFace.WireRefIds) - { - if (!aWireRefId.IsValid(static_cast(theWireRefs.Size()))) - { - continue; - } - const BRepGraphInc::WireRef& aRef = theWireRefs.Value(static_cast(aWireRefId.Index)); - if (aRef.IsRemoved || !aRef.WireDefId.IsValid(static_cast(theWires.Size()))) - { - continue; - } - if (theWires.Value(static_cast(aRef.WireDefId.Index)).IsRemoved) - { - continue; - } - appendDirect(myWireToFaces, aRef.WireDefId.Index, aFaceId); - } - } - - // Face -> Shells: iterate shell entities and their FaceRefIds for O(1) parent lookup. - const uint32_t aNbShells = static_cast(theShells.Size()); - for (BRepGraph_ShellId aShellId(0); aShellId.IsValid(aNbShells); ++aShellId) - { - const BRepGraphInc::ShellDef& aShell = theShells.Value(static_cast(aShellId.Index)); - if (aShell.IsRemoved) - { - continue; - } - for (const BRepGraph_FaceRefId& aFaceRefId : aShell.FaceRefIds) - { - if (!aFaceRefId.IsValid(static_cast(theFaceRefs.Size()))) - { - continue; - } - const BRepGraphInc::FaceRef& aRef = theFaceRefs.Value(static_cast(aFaceRefId.Index)); - if (aRef.IsRemoved || !aRef.FaceDefId.IsValid(static_cast(theFaces.Size()))) - { - continue; - } - if (theFaces.Value(static_cast(aRef.FaceDefId.Index)).IsRemoved) - { - continue; - } - appendDirect(myFaceToShells, aRef.FaceDefId.Index, aShellId); - } - } - - // Shell -> Solids: iterate solid entities and their ShellRefIds for O(1) parent lookup. - const uint32_t aNbSolids = static_cast(theSolids.Size()); - for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(aNbSolids); ++aSolidId) - { - const BRepGraphInc::SolidDef& aSolid = theSolids.Value(static_cast(aSolidId.Index)); - if (aSolid.IsRemoved) - { - continue; - } - for (const BRepGraph_ShellRefId& aShellRefId : aSolid.ShellRefIds) - { - if (!aShellRefId.IsValid(static_cast(theShellRefs.Size()))) - { - continue; - } - const BRepGraphInc::ShellRef& aRef = - theShellRefs.Value(static_cast(aShellRefId.Index)); - if (aRef.IsRemoved || !aRef.ShellDefId.IsValid(static_cast(theShells.Size()))) - { - continue; - } - if (theShells.Value(static_cast(aRef.ShellDefId.Index)).IsRemoved) - { - continue; - } - appendDirect(myShellToSolids, aRef.ShellDefId.Index, aSolidId); - } - } - - // Compound -> child reverse indices: iterate compound entities and their ChildRefIds. - // Covers all legal TopoDS_Compound child kinds (Solid/Shell/Face/Compound/CompSolid - // + atomic Wire/Edge/Vertex); missing any kind causes silent unindexing of legal - // compounds and later reverse-lookup returns empty. - preSize(myCompoundsOfSolid, static_cast(theSolids.Size()), myAllocator); - preSize(myCompoundsOfShell, static_cast(theShells.Size()), myAllocator); - preSize(myCompoundsOfFace, static_cast(theFaces.Size()), myAllocator); - preSize(myCompoundsOfCompound, static_cast(theCompounds.Size()), myAllocator); - preSize(myCompoundsOfCompSolid, static_cast(theCompSolids.Size()), myAllocator); - preSize(myCompoundsOfWire, static_cast(theWires.Size()), myAllocator); - preSize(myCompoundsOfEdge, static_cast(theEdges.Size()), myAllocator); - preSize(myCompoundsOfVertex, static_cast(theVertices.Size()), myAllocator); - const uint32_t aNbCompounds = static_cast(theCompounds.Size()); - for (BRepGraph_CompoundId aCompoundId(0); aCompoundId.IsValid(aNbCompounds); ++aCompoundId) - { - const BRepGraphInc::CompoundDef& aComp = - theCompounds.Value(static_cast(aCompoundId.Index)); - if (aComp.IsRemoved) - { - continue; - } - for (const BRepGraph_ChildRefId& aChildRefId : aComp.ChildRefIds) - { - const uint32_t aChildRefIdx = aChildRefId.Index; - if (aChildRefIdx >= theChildRefs.Size()) - { - continue; - } - const BRepGraphInc::ChildRef& aRef = theChildRefs.Value(static_cast(aChildRefIdx)); - if (aRef.IsRemoved || !aRef.ChildDefId.IsValid()) - { - continue; - } - if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Solid - && aRef.ChildDefId.Index < theSolids.Size()) - { - appendDirect(myCompoundsOfSolid, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Shell - && aRef.ChildDefId.Index < theShells.Size()) - { - appendDirect(myCompoundsOfShell, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Face - && aRef.ChildDefId.Index < theFaces.Size()) - { - appendDirect(myCompoundsOfFace, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Compound - && aRef.ChildDefId.Index < theCompounds.Size()) - { - appendDirect(myCompoundsOfCompound, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::CompSolid - && aRef.ChildDefId.Index < theCompSolids.Size()) - { - appendDirect(myCompoundsOfCompSolid, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Wire - && aRef.ChildDefId.Index < theWires.Size()) - { - appendDirect(myCompoundsOfWire, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Edge - && aRef.ChildDefId.Index < theEdges.Size()) - { - appendDirect(myCompoundsOfEdge, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Vertex - && aRef.ChildDefId.Index < theVertices.Size()) - { - appendDirect(myCompoundsOfVertex, aRef.ChildDefId.Index, aCompoundId); - } - } - } - - // CompSolid -> Solid reverse index: iterate comp-solid entities and their SolidRefIds. - preSize(myCompSolidsOfSolid, static_cast(theSolids.Size()), myAllocator); - const uint32_t aNbCompSolids = static_cast(theCompSolids.Size()); - for (BRepGraph_CompSolidId aCompSolidId(0); aCompSolidId.IsValid(aNbCompSolids); ++aCompSolidId) - { - const BRepGraphInc::CompSolidDef& aCS = - theCompSolids.Value(static_cast(aCompSolidId.Index)); - if (aCS.IsRemoved) - { - continue; - } - for (const BRepGraph_SolidRefId& aSolidRefId : aCS.SolidRefIds) - { - if (!aSolidRefId.IsValid(static_cast(theSolidRefs.Size()))) - { - continue; - } - const BRepGraphInc::SolidRef& aRef = - theSolidRefs.Value(static_cast(aSolidRefId.Index)); - if (aRef.IsRemoved || !aRef.SolidDefId.IsValid(static_cast(theSolids.Size()))) - { - continue; - } - if (theSolids.Value(static_cast(aRef.SolidDefId.Index)).IsRemoved) - { - continue; - } - appendDirect(myCompSolidsOfSolid, aRef.SolidDefId.Index, aCompSolidId); - } - } - - // CoEdge -> Wires: iterate wire entities and their CoEdgeRefIds for O(1) parent lookup. - preSize(myCoEdgeToWires, static_cast(theCoEdges.Size()), myAllocator); - for (BRepGraph_WireId aWireId(0); aWireId.IsValid(aNbWires); ++aWireId) - { - const BRepGraphInc::WireDef& aWire = theWires.Value(static_cast(aWireId.Index)); - if (aWire.IsRemoved) - { - continue; - } - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) - { - if (!aCoEdgeRefId.IsValid(static_cast(theCoEdgeRefs.Size()))) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef = - theCoEdgeRefs.Value(static_cast(aCoEdgeRefId.Index)); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(static_cast(theCoEdges.Size()))) - { - continue; - } - if (theCoEdges.Value(static_cast(aRef.CoEdgeDefId.Index)).IsRemoved) - { - continue; - } - appendDirect(myCoEdgeToWires, aRef.CoEdgeDefId.Index, aWireId); - } - } -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BuildDelta( - const NCollection_DynamicArray& theVertices, - const NCollection_DynamicArray& theEdges, - const NCollection_DynamicArray& theCoEdges, - const NCollection_DynamicArray& theWires, - const NCollection_DynamicArray& theFaces, - const NCollection_DynamicArray& theShells, - const NCollection_DynamicArray& theSolids, - const NCollection_DynamicArray& theCompounds, - const NCollection_DynamicArray& theCompSolids, - const NCollection_DynamicArray& theShellRefs, - const NCollection_DynamicArray& theFaceRefs, - const NCollection_DynamicArray& theWireRefs, - const NCollection_DynamicArray& theCoEdgeRefs, - const NCollection_DynamicArray& theSolidRefs, - const NCollection_DynamicArray& theChildRefs, - const NCollection_DynamicArray& theVertexRefs, - const uint32_t theOldNbEdges, - const uint32_t theOldNbWires, - const uint32_t theOldNbFaces, - const uint32_t theOldNbShells, - const uint32_t theOldNbSolids, - const uint32_t theOldNbCompounds, - const uint32_t theOldNbCompSolids, - const uint32_t theOldNbChildRefs, - const uint32_t theOldNbSolidRefs) -{ - - // Helper: resolve a VertexRefId to the corresponding VertexDefId (BRepGraph_VertexId). - // Returns an invalid id if the ref id is invalid or out of range. - - // Scan new edges for max vertex index to possibly extend myVertexToEdges. - uint32_t aNewVertexCapacity = static_cast(myVertexToEdges.Size()); - const uint32_t aNbEdges = static_cast(theEdges.Size()); - for (BRepGraph_EdgeId anEdgeId(theOldNbEdges); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdge = theEdges.Value(static_cast(anEdgeId.Index)); - if (anEdge.IsRemoved) - { - continue; - } - const BRepGraph_VertexId aStartVtx = resolveVertexDefId(theVertexRefs, anEdge.StartVertexRefId); - const BRepGraph_VertexId anEndVtx = resolveVertexDefId(theVertexRefs, anEdge.EndVertexRefId); - if (aStartVtx.IsValid() && aStartVtx.Index + 1 > aNewVertexCapacity) - { - aNewVertexCapacity = aStartVtx.Index + 1; - } - if (anEndVtx.IsValid() && anEndVtx.Index + 1 > aNewVertexCapacity) - { - aNewVertexCapacity = anEndVtx.Index + 1; - } - } - - // Extend outer vectors if needed (pre-size for new key ranges). - ensureSize(myVertexToEdges, aNewVertexCapacity, myAllocator); - ensureSize(myEdgeToWires, static_cast(theEdges.Size()), myAllocator); - ensureSize(myEdgeToFaces, static_cast(theEdges.Size()), myAllocator); - ensureSize(myEdgeToCoEdges, static_cast(theEdges.Size()), myAllocator); - ensureSize(myWireToFaces, static_cast(theWires.Size()), myAllocator); - ensureSize(myFaceToShells, static_cast(theFaces.Size()), myAllocator); - ensureSize(myShellToSolids, static_cast(theShells.Size()), myAllocator); - ensureSize(myCompoundsOfSolid, static_cast(theSolids.Size()), myAllocator); - ensureSize(myCompSolidsOfSolid, static_cast(theSolids.Size()), myAllocator); - ensureSize(myCompoundsOfShell, static_cast(theShells.Size()), myAllocator); - ensureSize(myCompoundsOfFace, static_cast(theFaces.Size()), myAllocator); - ensureSize(myCompoundsOfCompound, static_cast(theCompounds.Size()), myAllocator); - ensureSize(myCompoundsOfCompSolid, static_cast(theCompSolids.Size()), myAllocator); - ensureSize(myCompoundsOfWire, static_cast(theWires.Size()), myAllocator); - ensureSize(myCompoundsOfEdge, static_cast(theEdges.Size()), myAllocator); - ensureSize(myCompoundsOfVertex, static_cast(theVertices.Size()), myAllocator); - - // Vertex -> Edges: only new edges. - for (BRepGraph_EdgeId anEdgeId(theOldNbEdges); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdge = theEdges.Value(static_cast(anEdgeId.Index)); - if (anEdge.IsRemoved) - { - continue; - } - const BRepGraph_VertexId aStartVtx = resolveVertexDefId(theVertexRefs, anEdge.StartVertexRefId); - const BRepGraph_VertexId anEndVtx = resolveVertexDefId(theVertexRefs, anEdge.EndVertexRefId); - if (aStartVtx.IsValid()) - { - appendUnique(myVertexToEdges, aStartVtx.Index, anEdgeId); - } - if (anEndVtx.IsValid() && anEndVtx != aStartVtx) - { - appendUnique(myVertexToEdges, anEndVtx.Index, anEdgeId); - } - } - - // Edge -> Wires: iterate only new wire entities and their CoEdgeRefIds. - const uint32_t aNbWires = static_cast(theWires.Size()); - for (BRepGraph_WireId aWireId(theOldNbWires); aWireId.IsValid(aNbWires); ++aWireId) - { - const BRepGraphInc::WireDef& aWire = theWires.Value(static_cast(aWireId.Index)); - if (aWire.IsRemoved) - { - continue; - } - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) - { - if (!aCoEdgeRefId.IsValid(static_cast(theCoEdgeRefs.Size()))) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef = - theCoEdgeRefs.Value(static_cast(aCoEdgeRefId.Index)); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(static_cast(theCoEdges.Size()))) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = - theCoEdges.Value(static_cast(aRef.CoEdgeDefId.Index)); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid()) - { - continue; - } - appendUnique(myEdgeToWires, aCoEdge.EdgeDefId.Index, aWireId); - } - } - - // Edge -> CoEdges: scan only newly appended coedges (they may reference old edges). - Standard_ASSERT_RAISE( - myNbIndexedCoEdges <= theCoEdges.Size(), - "BRepGraphInc_ReverseIndex::BuildDelta: myNbIndexedCoEdges cursor out of range " - "(indicates CoEdge vector shrank between delta calls, which is unsupported)"); - const uint32_t aOldNbIndexedCoEdges = myNbIndexedCoEdges; - const uint32_t aNbCoEdges = static_cast(theCoEdges.Size()); - for (BRepGraph_CoEdgeId aCoEdgeId(aOldNbIndexedCoEdges); aCoEdgeId.IsValid(aNbCoEdges); - ++aCoEdgeId) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = theCoEdges.Value(static_cast(aCoEdgeId.Index)); - if (aCoEdge.IsRemoved) - { - continue; - } - if (aCoEdge.EdgeDefId.IsValid()) - { - appendUnique(myEdgeToCoEdges, aCoEdge.EdgeDefId.Index, aCoEdgeId); - } - } - myNbIndexedCoEdges = static_cast(theCoEdges.Size()); - - // CoEdge -> Wires: iterate only new wire entities and their CoEdgeRefIds. - ensureSize(myCoEdgeToWires, static_cast(theCoEdges.Size()), myAllocator); - for (BRepGraph_WireId aWireId(theOldNbWires); aWireId.IsValid(aNbWires); ++aWireId) - { - const BRepGraphInc::WireDef& aWire = theWires.Value(static_cast(aWireId.Index)); - if (aWire.IsRemoved) - { - continue; - } - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) - { - if (!aCoEdgeRefId.IsValid(static_cast(theCoEdgeRefs.Size()))) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef = - theCoEdgeRefs.Value(static_cast(aCoEdgeRefId.Index)); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(static_cast(theCoEdges.Size()))) - { - continue; - } - if (theCoEdges.Value(static_cast(aRef.CoEdgeDefId.Index)).IsRemoved) - { - continue; - } - appendUnique(myCoEdgeToWires, aRef.CoEdgeDefId.Index, aWireId); - } - } - - // Edge -> Faces: derive from CoEdge.FaceDefId for new edges. - for (BRepGraph_EdgeId anEdgeId(theOldNbEdges); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - if (theEdges.Value(static_cast(anEdgeId.Index)).IsRemoved) - { - continue; - } - const NCollection_DynamicArray* aCoEdgeIdxs = - seekVec(myEdgeToCoEdges, anEdgeId.Index); - if (aCoEdgeIdxs == nullptr) - { - continue; - } - const size_t aNbCE = aCoEdgeIdxs->Size(); - - NCollection_LocalArray aFaces(aNbCE); - size_t aNbFaces = 0; - for (const auto& aCoEdgeId : *aCoEdgeIdxs) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = - theCoEdges.Value(static_cast(aCoEdgeId.Index)); - if (aCoEdge.FaceDefId.IsValid()) - { - aFaces[aNbFaces++] = aCoEdge.FaceDefId.Index; - } - } - if (aNbFaces == 0) - { - continue; - } - - std::sort(static_cast(aFaces), static_cast(aFaces) + aNbFaces); - - uint32_t aPrev = UINT32_MAX; - for (size_t i = 0; i < aNbFaces; ++i) - { - if (aFaces[i] != aPrev) - { - appendDirect(myEdgeToFaces, anEdgeId.Index, BRepGraph_FaceId(aFaces[i])); - aPrev = aFaces[i]; - } - } - } - - // Wire -> Faces: iterate only new face entities and their WireRefIds. - const uint32_t aNbFaces = static_cast(theFaces.Size()); - for (BRepGraph_FaceId aFaceId(theOldNbFaces); aFaceId.IsValid(aNbFaces); ++aFaceId) - { - const BRepGraphInc::FaceDef& aFace = theFaces.Value(static_cast(aFaceId.Index)); - if (aFace.IsRemoved) - { - continue; - } - for (const BRepGraph_WireRefId& aWireRefId : aFace.WireRefIds) - { - if (!aWireRefId.IsValid(static_cast(theWireRefs.Size()))) - { - continue; - } - const BRepGraphInc::WireRef& aRef = theWireRefs.Value(static_cast(aWireRefId.Index)); - if (aRef.IsRemoved || !aRef.WireDefId.IsValid(static_cast(theWires.Size()))) - { - continue; - } - if (theWires.Value(static_cast(aRef.WireDefId.Index)).IsRemoved) - { - continue; - } - appendUnique(myWireToFaces, aRef.WireDefId.Index, aFaceId); - } - } - - // Face -> Shells: iterate only new shell entities and their FaceRefIds. - const uint32_t aNbShells = static_cast(theShells.Size()); - for (BRepGraph_ShellId aShellId(theOldNbShells); aShellId.IsValid(aNbShells); ++aShellId) - { - const BRepGraphInc::ShellDef& aShell = theShells.Value(static_cast(aShellId.Index)); - if (aShell.IsRemoved) - { - continue; - } - for (const BRepGraph_FaceRefId& aFaceRefId : aShell.FaceRefIds) - { - if (!aFaceRefId.IsValid(static_cast(theFaceRefs.Size()))) - { - continue; - } - const BRepGraphInc::FaceRef& aRef = theFaceRefs.Value(static_cast(aFaceRefId.Index)); - if (aRef.IsRemoved || !aRef.FaceDefId.IsValid(static_cast(theFaces.Size()))) - { - continue; - } - if (theFaces.Value(static_cast(aRef.FaceDefId.Index)).IsRemoved) - { - continue; - } - appendUnique(myFaceToShells, aRef.FaceDefId.Index, aShellId); - } - } - - // Shell -> Solids: iterate only new solid entities and their ShellRefIds. - const uint32_t aNbSolids = static_cast(theSolids.Size()); - for (BRepGraph_SolidId aSolidId(theOldNbSolids); aSolidId.IsValid(aNbSolids); ++aSolidId) - { - const BRepGraphInc::SolidDef& aSolid = theSolids.Value(static_cast(aSolidId.Index)); - if (aSolid.IsRemoved) - { - continue; - } - for (const BRepGraph_ShellRefId& aShellRefId : aSolid.ShellRefIds) - { - if (!aShellRefId.IsValid(static_cast(theShellRefs.Size()))) - { - continue; - } - const BRepGraphInc::ShellRef& aRef = - theShellRefs.Value(static_cast(aShellRefId.Index)); - if (aRef.IsRemoved || !aRef.ShellDefId.IsValid(static_cast(theShells.Size()))) - { - continue; - } - if (theShells.Value(static_cast(aRef.ShellDefId.Index)).IsRemoved) - { - continue; - } - appendUnique(myShellToSolids, aRef.ShellDefId.Index, aSolidId); - } - } - - uint32_t anOldNbCompounds = theOldNbCompounds; - if (anOldNbCompounds > theCompounds.Size()) - { - anOldNbCompounds = 0; - } - - // Compound -> child reverse indices: - // 1) process all refs of newly appended compound parents - // 2) process refs attached to pre-existing parents (covers appended refs on old parents) - const uint32_t aNbCompounds = static_cast(theCompounds.Size()); - for (BRepGraph_CompoundId aCompoundId(anOldNbCompounds); aCompoundId.IsValid(aNbCompounds); - ++aCompoundId) - { - const BRepGraphInc::CompoundDef& aComp = - theCompounds.Value(static_cast(aCompoundId.Index)); - if (aComp.IsRemoved) - { - continue; - } - for (const BRepGraph_ChildRefId& aChildRefId : aComp.ChildRefIds) - { - const uint32_t aChildRefIdx = aChildRefId.Index; - if (aChildRefIdx >= theChildRefs.Size()) - { - continue; - } - const BRepGraphInc::ChildRef& aRef = theChildRefs.Value(static_cast(aChildRefIdx)); - if (aRef.IsRemoved || !aRef.ChildDefId.IsValid()) - { - continue; - } - if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Solid - && aRef.ChildDefId.Index < theSolids.Size()) - { - appendUnique(myCompoundsOfSolid, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Shell - && aRef.ChildDefId.Index < theShells.Size()) - { - appendUnique(myCompoundsOfShell, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Face - && aRef.ChildDefId.Index < theFaces.Size()) - { - appendUnique(myCompoundsOfFace, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Compound - && aRef.ChildDefId.Index < theCompounds.Size()) - { - appendUnique(myCompoundsOfCompound, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::CompSolid - && aRef.ChildDefId.Index < theCompSolids.Size()) - { - appendUnique(myCompoundsOfCompSolid, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Wire - && aRef.ChildDefId.Index < theWires.Size()) - { - appendUnique(myCompoundsOfWire, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Edge - && aRef.ChildDefId.Index < theEdges.Size()) - { - appendUnique(myCompoundsOfEdge, aRef.ChildDefId.Index, aCompoundId); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Vertex - && aRef.ChildDefId.Index < theVertices.Size()) - { - appendUnique(myCompoundsOfVertex, aRef.ChildDefId.Index, aCompoundId); - } - } - } - - // Loop 2: process ChildRefs appended during this delta that point to a - // pre-existing compound parent. Iterate only [theOldNbChildRefs, NbChildRefs) - // so cost stays O(delta refs) regardless of pre-existing graph size. - uint32_t aOldNbChildRefs = theOldNbChildRefs; - if (aOldNbChildRefs > theChildRefs.Size()) - { - aOldNbChildRefs = 0; - } - const uint32_t aNbChildRefs = static_cast(theChildRefs.Size()); - for (BRepGraph_ChildRefId aChildRefId(aOldNbChildRefs); aChildRefId.IsValid(aNbChildRefs); - ++aChildRefId) - { - const BRepGraphInc::ChildRef& aRef = theChildRefs.Value(static_cast(aChildRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() || !aRef.ChildDefId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Compound) - { - continue; - } - - const uint32_t aCompoundIdx = aRef.ParentId.Index; - if (aCompoundIdx >= theCompounds.Size() || aCompoundIdx >= anOldNbCompounds) - { - continue; - } - - const BRepGraphInc::CompoundDef& aComp = theCompounds.Value(static_cast(aCompoundIdx)); - if (aComp.IsRemoved) - { - continue; - } - - if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Solid - && aRef.ChildDefId.Index < theSolids.Size()) - { - appendUnique(myCompoundsOfSolid, aRef.ChildDefId.Index, BRepGraph_CompoundId(aCompoundIdx)); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Shell - && aRef.ChildDefId.Index < theShells.Size()) - { - appendUnique(myCompoundsOfShell, aRef.ChildDefId.Index, BRepGraph_CompoundId(aCompoundIdx)); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Face - && aRef.ChildDefId.Index < theFaces.Size()) - { - appendUnique(myCompoundsOfFace, aRef.ChildDefId.Index, BRepGraph_CompoundId(aCompoundIdx)); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Compound - && aRef.ChildDefId.Index < theCompounds.Size()) - { - appendUnique(myCompoundsOfCompound, - aRef.ChildDefId.Index, - BRepGraph_CompoundId(aCompoundIdx)); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::CompSolid - && aRef.ChildDefId.Index < theCompSolids.Size()) - { - appendUnique(myCompoundsOfCompSolid, - aRef.ChildDefId.Index, - BRepGraph_CompoundId(aCompoundIdx)); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Wire - && aRef.ChildDefId.Index < theWires.Size()) - { - appendUnique(myCompoundsOfWire, aRef.ChildDefId.Index, BRepGraph_CompoundId(aCompoundIdx)); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Edge - && aRef.ChildDefId.Index < theEdges.Size()) - { - appendUnique(myCompoundsOfEdge, aRef.ChildDefId.Index, BRepGraph_CompoundId(aCompoundIdx)); - } - else if (aRef.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Vertex - && aRef.ChildDefId.Index < theVertices.Size()) - { - appendUnique(myCompoundsOfVertex, aRef.ChildDefId.Index, BRepGraph_CompoundId(aCompoundIdx)); - } - } - - uint32_t anOldNbCompSolids = theOldNbCompSolids; - if (anOldNbCompSolids > theCompSolids.Size()) - { - anOldNbCompSolids = 0; - } - - // CompSolid -> Solid reverse index: - // 1) process all refs of newly appended compsolid parents - // 2) process refs attached to pre-existing parents (covers appended refs on old parents) - const uint32_t aNbCompSolids = static_cast(theCompSolids.Size()); - for (BRepGraph_CompSolidId aCompSolidId(anOldNbCompSolids); aCompSolidId.IsValid(aNbCompSolids); - ++aCompSolidId) - { - const BRepGraphInc::CompSolidDef& aCS = - theCompSolids.Value(static_cast(aCompSolidId.Index)); - if (aCS.IsRemoved) - { - continue; - } - for (const BRepGraph_SolidRefId& aSolidRefId : aCS.SolidRefIds) - { - if (!aSolidRefId.IsValid(static_cast(theSolidRefs.Size()))) - { - continue; - } - const BRepGraphInc::SolidRef& aRef = - theSolidRefs.Value(static_cast(aSolidRefId.Index)); - if (aRef.IsRemoved || !aRef.SolidDefId.IsValid(static_cast(theSolids.Size()))) - { - continue; - } - if (theSolids.Value(static_cast(aRef.SolidDefId.Index)).IsRemoved) - { - continue; - } - appendUnique(myCompSolidsOfSolid, aRef.SolidDefId.Index, aCompSolidId); - } - } - - // Loop 2: process SolidRefs appended during this delta that target a - // pre-existing compsolid parent. Iterate only [theOldNbSolidRefs, NbSolidRefs). - uint32_t aOldNbSolidRefs = theOldNbSolidRefs; - if (aOldNbSolidRefs > theSolidRefs.Size()) - { - aOldNbSolidRefs = 0; - } - const uint32_t aNbSolidRefs = static_cast(theSolidRefs.Size()); - for (BRepGraph_SolidRefId aSolidRefId(aOldNbSolidRefs); aSolidRefId.IsValid(aNbSolidRefs); - ++aSolidRefId) - { - const BRepGraphInc::SolidRef& aRef = theSolidRefs.Value(static_cast(aSolidRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() || !aRef.SolidDefId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::CompSolid) - { - continue; - } - - const uint32_t aCompSolidIdx = aRef.ParentId.Index; - if (aCompSolidIdx >= theCompSolids.Size() || aCompSolidIdx >= anOldNbCompSolids - || !aRef.SolidDefId.IsValid(static_cast(theSolids.Size()))) - { - continue; - } - - const BRepGraphInc::CompSolidDef& aCS = theCompSolids.Value(static_cast(aCompSolidIdx)); - if (aCS.IsRemoved || theSolids.Value(static_cast(aRef.SolidDefId.Index)).IsRemoved) - { - continue; - } - - appendUnique(myCompSolidsOfSolid, aRef.SolidDefId.Index, BRepGraph_CompSolidId(aCompSolidIdx)); - } -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindEdgeToWire(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_WireId theWireId) -{ - appendUnique(myEdgeToWires, theEdgeId.Index, theWireId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindCoEdgeToWire(const BRepGraph_CoEdgeId theCoEdgeId, - const BRepGraph_WireId theWireId) -{ - appendUnique(myCoEdgeToWires, theCoEdgeId.Index, theWireId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindEdgeFromWire(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_WireId theWireId) -{ - eraseSwapLast(myEdgeToWires, theEdgeId.Index, theWireId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindCoEdgeFromWire(const BRepGraph_CoEdgeId theCoEdgeId, - const BRepGraph_WireId theWireId) -{ - eraseSwapLast(myCoEdgeToWires, theCoEdgeId.Index, theWireId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::ReplaceEdgeInWireMap(const BRepGraph_EdgeId theOldEdgeId, - const BRepGraph_EdgeId theNewEdgeId, - const BRepGraph_WireId theWireId) -{ - UnbindEdgeFromWire(theOldEdgeId, theWireId); - BindEdgeToWire(theNewEdgeId, theWireId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindVertexToEdge(const BRepGraph_VertexId theVertexId, - const BRepGraph_EdgeId theEdgeId) -{ - appendUnique(myVertexToEdges, theVertexId.Index, theEdgeId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindVertexFromEdge(const BRepGraph_VertexId theVertexId, - const BRepGraph_EdgeId theEdgeId) -{ - eraseSwapLast(myVertexToEdges, theVertexId.Index, theEdgeId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindEdgeToCoEdge(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_CoEdgeId theCoEdgeId) -{ - appendUnique(myEdgeToCoEdges, theEdgeId.Index, theCoEdgeId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindEdgeFromCoEdge(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_CoEdgeId theCoEdgeId) -{ - eraseSwapLast(myEdgeToCoEdges, theEdgeId.Index, theCoEdgeId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindEdgeToFace(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_FaceId theFaceId) -{ - appendUnique(myEdgeToFaces, theEdgeId.Index, theFaceId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindEdgeFromFace(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_FaceId theFaceId) -{ - eraseSwapLast(myEdgeToFaces, theEdgeId.Index, theFaceId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindWireToFace(const BRepGraph_WireId theWireId, - const BRepGraph_FaceId theFaceId) -{ - appendUnique(myWireToFaces, theWireId.Index, theFaceId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindWireFromFace(const BRepGraph_WireId theWireId, - const BRepGraph_FaceId theFaceId) -{ - eraseSwapLast(myWireToFaces, theWireId.Index, theFaceId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindFaceToShell(const BRepGraph_FaceId theFaceId, - const BRepGraph_ShellId theShellId) -{ - appendUnique(myFaceToShells, theFaceId.Index, theShellId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindFaceFromShell(const BRepGraph_FaceId theFaceId, - const BRepGraph_ShellId theShellId) -{ - eraseSwapLast(myFaceToShells, theFaceId.Index, theShellId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindShellToSolid(const BRepGraph_ShellId theShellId, - const BRepGraph_SolidId theSolidId) -{ - appendUnique(myShellToSolids, theShellId.Index, theSolidId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindShellFromSolid(const BRepGraph_ShellId theShellId, - const BRepGraph_SolidId theSolidId) -{ - eraseSwapLast(myShellToSolids, theShellId.Index, theSolidId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindSolidToCompSolid(const BRepGraph_SolidId theSolidId, - const BRepGraph_CompSolidId theCompSolidId) -{ - appendUnique(myCompSolidsOfSolid, theSolidId.Index, theCompSolidId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindSolidFromCompSolid(const BRepGraph_SolidId theSolidId, - const BRepGraph_CompSolidId theCompSolidId) -{ - eraseSwapLast(myCompSolidsOfSolid, theSolidId.Index, theCompSolidId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindCompoundChild(const BRepGraph_NodeId theChildDefId, - const BRepGraph_CompoundId theCompoundId) -{ - switch (theChildDefId.NodeKind) - { - case BRepGraph_NodeId::Kind::Solid: - appendUnique(myCompoundsOfSolid, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Shell: - appendUnique(myCompoundsOfShell, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Face: - appendUnique(myCompoundsOfFace, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Compound: - appendUnique(myCompoundsOfCompound, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::CompSolid: - appendUnique(myCompoundsOfCompSolid, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Wire: - appendUnique(myCompoundsOfWire, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Edge: - appendUnique(myCompoundsOfEdge, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Vertex: - appendUnique(myCompoundsOfVertex, theChildDefId.Index, theCompoundId); - break; - default: - break; - } -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindCompoundChild(const BRepGraph_NodeId theChildDefId, - const BRepGraph_CompoundId theCompoundId) -{ - switch (theChildDefId.NodeKind) - { - case BRepGraph_NodeId::Kind::Solid: - eraseSwapLast(myCompoundsOfSolid, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Shell: - eraseSwapLast(myCompoundsOfShell, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Face: - eraseSwapLast(myCompoundsOfFace, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Compound: - eraseSwapLast(myCompoundsOfCompound, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::CompSolid: - eraseSwapLast(myCompoundsOfCompSolid, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Wire: - eraseSwapLast(myCompoundsOfWire, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Edge: - eraseSwapLast(myCompoundsOfEdge, theChildDefId.Index, theCompoundId); - break; - case BRepGraph_NodeId::Kind::Vertex: - eraseSwapLast(myCompoundsOfVertex, theChildDefId.Index, theCompoundId); - break; - default: - break; - } -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BindProductOccurrence(const BRepGraph_OccurrenceId theOccurrenceId, - const BRepGraph_ProductId theProductId) -{ - appendUnique(myProductToOccurrences, theProductId.Index, theOccurrenceId); -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::UnbindProductOccurrence( - const BRepGraph_OccurrenceId theOccurrenceId, - const BRepGraph_ProductId theProductId) -{ - eraseSwapLast(myProductToOccurrences, theProductId.Index, theOccurrenceId); -} - -//================================================================================================= - -bool BRepGraphInc_ReverseIndex::Validate( - const NCollection_DynamicArray& theVertices, - const NCollection_DynamicArray& theEdges, - const NCollection_DynamicArray& theCoEdges, - const NCollection_DynamicArray& theWires, - const NCollection_DynamicArray& theFaces, - const NCollection_DynamicArray& theShells, - const NCollection_DynamicArray& theSolids, - const NCollection_DynamicArray& theCompounds, - const NCollection_DynamicArray& theCompSolids, - const NCollection_DynamicArray& theShellRefs, - const NCollection_DynamicArray& theFaceRefs, - const NCollection_DynamicArray& theWireRefs, - const NCollection_DynamicArray& theCoEdgeRefs, - const NCollection_DynamicArray& theSolidRefs, - const NCollection_DynamicArray& theChildRefs, - const NCollection_DynamicArray& theVertexRefs) const -{ - auto hasActiveWireUsageOfEdge = [&](const BRepGraph_WireId theWireId, - const BRepGraph_EdgeId theEdgeId) -> bool { - if (!theWireId.IsValid(static_cast(theWires.Size())) - || !theEdgeId.IsValid(static_cast(theEdges.Size()))) - { - return false; - } - const BRepGraphInc::WireDef& aWire = theWires.Value(static_cast(theWireId.Index)); - if (aWire.IsRemoved) - { - return false; - } - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aWire.CoEdgeRefIds) - { - if (!aCoEdgeRefId.IsValid(static_cast(theCoEdgeRefs.Size()))) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef = - theCoEdgeRefs.Value(static_cast(aCoEdgeRefId.Index)); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid()) - { - continue; - } - if (BRepGraph_WireId(aRef.ParentId) != theWireId) - { - continue; - } - if (!aRef.CoEdgeDefId.IsValid(static_cast(theCoEdges.Size()))) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = - theCoEdges.Value(static_cast(aRef.CoEdgeDefId.Index)); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid()) - { - continue; - } - if (aCoEdge.EdgeDefId == theEdgeId) - { - return true; - } - } - return false; - }; - - auto hasActiveFaceRefForWire = [&](const BRepGraph_WireId theWireId, - const BRepGraph_FaceId theFaceId) -> bool { - if (!theWireId.IsValid(static_cast(theWires.Size())) - || !theFaceId.IsValid(static_cast(theFaces.Size()))) - { - return false; - } - const BRepGraphInc::FaceDef& aFace = theFaces.Value(static_cast(theFaceId.Index)); - if (aFace.IsRemoved) - { - return false; - } - for (const BRepGraph_WireRefId& aWireRefId : aFace.WireRefIds) - { - if (!aWireRefId.IsValid(static_cast(theWireRefs.Size()))) - { - continue; - } - const BRepGraphInc::WireRef& aRef = theWireRefs.Value(static_cast(aWireRefId.Index)); - if (aRef.IsRemoved || !aRef.WireDefId.IsValid()) - { - continue; - } - if (BRepGraph_FaceId(aRef.ParentId) != theFaceId) - { - continue; - } - if (aRef.WireDefId == theWireId) - { - return true; - } - } - return false; - }; - - auto hasActiveFaceForEdge = [&](const BRepGraph_EdgeId theEdgeId, - const BRepGraph_FaceId theFaceId) -> bool { - if (!theEdgeId.IsValid(static_cast(theEdges.Size())) - || !theFaceId.IsValid(static_cast(theFaces.Size()))) - { - return false; - } - const BRepGraphInc::EdgeDef& anEdge = theEdges.Value(static_cast(theEdgeId.Index)); - if (anEdge.IsRemoved) - { - return false; - } - - const NCollection_DynamicArray* aCoEdgeIds = - seekVec(myEdgeToCoEdges, theEdgeId.Index); - if (aCoEdgeIds == nullptr) - { - return false; - } - - return hasActiveFaceForEdgeInCoEdges(*aCoEdgeIds, theCoEdges, theEdgeId, theFaceId); - }; - - auto hasActiveFaceRef = [&](const BRepGraph_ShellId theShellId, - const BRepGraph_FaceId theFaceId) -> bool { - if (!theShellId.IsValid(static_cast(theShells.Size())) - || !theFaceId.IsValid(static_cast(theFaces.Size()))) - { - return false; - } - const BRepGraphInc::ShellDef& aShell = theShells.Value(static_cast(theShellId.Index)); - if (aShell.IsRemoved) - { - return false; - } - for (const BRepGraph_FaceRefId& aFaceRefId : aShell.FaceRefIds) - { - if (!aFaceRefId.IsValid(static_cast(theFaceRefs.Size()))) - { - continue; - } - const BRepGraphInc::FaceRef& aRef = theFaceRefs.Value(static_cast(aFaceRefId.Index)); - if (aRef.IsRemoved || !aRef.FaceDefId.IsValid()) - { - continue; - } - if (BRepGraph_ShellId(aRef.ParentId) != theShellId) - { - continue; - } - if (aRef.FaceDefId == theFaceId) - { - return true; - } - } - return false; - }; - - auto hasActiveShellRef = [&](const BRepGraph_SolidId theSolidId, - const BRepGraph_ShellId theShellId) -> bool { - if (!theSolidId.IsValid(static_cast(theSolids.Size())) - || !theShellId.IsValid(static_cast(theShells.Size()))) - { - return false; - } - const BRepGraphInc::SolidDef& aSolid = theSolids.Value(static_cast(theSolidId.Index)); - if (aSolid.IsRemoved) - { - return false; - } - for (const BRepGraph_ShellRefId& aShellRefId : aSolid.ShellRefIds) - { - if (!aShellRefId.IsValid(static_cast(theShellRefs.Size()))) - { - continue; - } - const BRepGraphInc::ShellRef& aRef = - theShellRefs.Value(static_cast(aShellRefId.Index)); - if (aRef.IsRemoved || !aRef.ShellDefId.IsValid()) - { - continue; - } - if (BRepGraph_SolidId(aRef.ParentId) != theSolidId) - { - continue; - } - if (aRef.ShellDefId == theShellId) - { - return true; - } - } - return false; - }; - - auto isActiveChildNode = [&](const BRepGraph_NodeId theChildId) -> bool { - if (!theChildId.IsValid()) - { - return false; - } - - switch (theChildId.NodeKind) - { - case BRepGraph_NodeId::Kind::Solid: - return theChildId.Index < theSolids.Size() - && !theSolids.Value(static_cast(theChildId.Index)).IsRemoved; - case BRepGraph_NodeId::Kind::Shell: - return theChildId.Index < theShells.Size() - && !theShells.Value(static_cast(theChildId.Index)).IsRemoved; - case BRepGraph_NodeId::Kind::Face: - return theChildId.Index < theFaces.Size() - && !theFaces.Value(static_cast(theChildId.Index)).IsRemoved; - case BRepGraph_NodeId::Kind::Compound: - return theChildId.Index < theCompounds.Size() - && !theCompounds.Value(static_cast(theChildId.Index)).IsRemoved; - case BRepGraph_NodeId::Kind::CompSolid: - return theChildId.Index < theCompSolids.Size() - && !theCompSolids.Value(static_cast(theChildId.Index)).IsRemoved; - case BRepGraph_NodeId::Kind::Wire: - return theChildId.Index < theWires.Size() - && !theWires.Value(static_cast(theChildId.Index)).IsRemoved; - case BRepGraph_NodeId::Kind::Edge: - return theChildId.Index < theEdges.Size() - && !theEdges.Value(static_cast(theChildId.Index)).IsRemoved; - case BRepGraph_NodeId::Kind::Vertex: - return theChildId.Index < theVertices.Size() - && !theVertices.Value(static_cast(theChildId.Index)).IsRemoved; - default: - return false; - } - }; - - auto hasActiveCompoundChildRef = [&](const BRepGraph_CompoundId theCompoundId, - const BRepGraph_NodeId theChildId) -> bool { - if (!theCompoundId.IsValid(static_cast(theCompounds.Size())) - || !isActiveChildNode(theChildId)) - { - return false; - } - - const BRepGraphInc::CompoundDef& aCompound = - theCompounds.Value(static_cast(theCompoundId.Index)); - if (aCompound.IsRemoved) - { - return false; - } - - for (const BRepGraph_ChildRefId& aChildRefId : aCompound.ChildRefIds) - { - if (!aChildRefId.IsValid(static_cast(theChildRefs.Size()))) - { - continue; - } - const BRepGraphInc::ChildRef& aRef = - theChildRefs.Value(static_cast(aChildRefId.Index)); - if (aRef.IsRemoved || !aRef.ChildDefId.IsValid()) - { - continue; - } - if (BRepGraph_CompoundId(aRef.ParentId) != theCompoundId) - { - continue; - } - if (aRef.ChildDefId == theChildId) - { - return true; - } - } - return false; - }; - - auto hasActiveCompSolidRef = [&](const BRepGraph_CompSolidId theCompSolidId, - const BRepGraph_SolidId theSolidId) -> bool { - if (!theCompSolidId.IsValid(static_cast(theCompSolids.Size())) - || !theSolidId.IsValid(static_cast(theSolids.Size()))) - { - return false; - } - - const BRepGraphInc::CompSolidDef& aCompSolid = - theCompSolids.Value(static_cast(theCompSolidId.Index)); - if (aCompSolid.IsRemoved || theSolids.Value(static_cast(theSolidId.Index)).IsRemoved) - { - return false; - } - - for (const BRepGraph_SolidRefId& aSolidRefId : aCompSolid.SolidRefIds) - { - if (!aSolidRefId.IsValid(static_cast(theSolidRefs.Size()))) - { - continue; - } - const BRepGraphInc::SolidRef& aRef = - theSolidRefs.Value(static_cast(aSolidRefId.Index)); - if (aRef.IsRemoved || !aRef.SolidDefId.IsValid()) - { - continue; - } - if (BRepGraph_CompSolidId(aRef.ParentId) != theCompSolidId) - { - continue; - } - if (aRef.SolidDefId == theSolidId) - { - return true; - } - } - return false; - }; - - // Check: for each coedge ref entry, edge->wire reverse entry must exist. - const uint32_t aNbCoEdgeRefs = static_cast(theCoEdgeRefs.Size()); - for (BRepGraph_CoEdgeRefId aCoEdgeRefId(0); aCoEdgeRefId.IsValid(aNbCoEdgeRefs); ++aCoEdgeRefId) - { - const BRepGraphInc::CoEdgeRef& aRef = - theCoEdgeRefs.Value(static_cast(aCoEdgeRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Wire || !aRef.CoEdgeDefId.IsValid()) - { - continue; - } - if (aRef.ParentId.Index >= theWires.Size() || aRef.CoEdgeDefId.Index >= theCoEdges.Size()) - { - return false; - } - const BRepGraphInc::WireDef& aWire = theWires.Value(static_cast(aRef.ParentId.Index)); - if (aWire.IsRemoved) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = - theCoEdges.Value(static_cast(aRef.CoEdgeDefId.Index)); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid()) - { - return false; - } - if (!containsIndexInTable(myEdgeToWires, aCoEdge.EdgeDefId, BRepGraph_WireId(aRef.ParentId))) - { - return false; - } - } - - // Check: for each active coedge ref entry, coedge->wire reverse entry must exist. - for (BRepGraph_CoEdgeRefId aCoEdgeRefId(0); aCoEdgeRefId.IsValid(aNbCoEdgeRefs); ++aCoEdgeRefId) - { - const BRepGraphInc::CoEdgeRef& aRef = - theCoEdgeRefs.Value(static_cast(aCoEdgeRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Wire || !aRef.CoEdgeDefId.IsValid()) - { - continue; - } - if (aRef.ParentId.Index >= theWires.Size() || aRef.CoEdgeDefId.Index >= theCoEdges.Size()) - { - return false; - } - const BRepGraphInc::WireDef& aWire = theWires.Value(static_cast(aRef.ParentId.Index)); - if (aWire.IsRemoved) - { - continue; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = - theCoEdges.Value(static_cast(aRef.CoEdgeDefId.Index)); - if (aCoEdge.IsRemoved) - { - continue; - } - if (!containsIndexInTable(myCoEdgeToWires, aRef.CoEdgeDefId, BRepGraph_WireId(aRef.ParentId))) - { - return false; - } - } - - // Check: for each edge's start/end vertex, vertex->edge reverse entry must exist. - const uint32_t aNbEdges = static_cast(theEdges.Size()); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdge = theEdges.Value(static_cast(anEdgeId.Index)); - if (anEdge.IsRemoved) - { - continue; - } - const BRepGraph_VertexId aStartVtx = resolveVertexDefId(theVertexRefs, anEdge.StartVertexRefId); - const BRepGraph_VertexId anEndVtx = resolveVertexDefId(theVertexRefs, anEdge.EndVertexRefId); - if (aStartVtx.IsValid()) - { - if (!containsIndexInTable(myVertexToEdges, aStartVtx, anEdgeId)) - { - return false; - } - } - if (anEndVtx.IsValid() && anEndVtx != aStartVtx) - { - if (!containsIndexInTable(myVertexToEdges, anEndVtx, anEdgeId)) - { - return false; - } - } - } - - // Check: for each edge's CoEdges with valid FaceDefId, edge->face reverse entry must exist. - const uint32_t aNbCoEdges = static_cast(theCoEdges.Size()); - for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aNbCoEdges); ++aCoEdgeId) - { - const BRepGraphInc::CoEdgeDef& aCoEdge = theCoEdges.Value(static_cast(aCoEdgeId.Index)); - if (aCoEdge.IsRemoved || !aCoEdge.FaceDefId.IsValid()) - { - continue; - } - if (!containsIndexInTable(myEdgeToFaces, aCoEdge.EdgeDefId, aCoEdge.FaceDefId)) - { - return false; - } - } - - // Check: for each wire ref entry, wire->face reverse entry must exist. - const uint32_t aNbWireRefs = static_cast(theWireRefs.Size()); - for (BRepGraph_WireRefId aWireRefId(0); aWireRefId.IsValid(aNbWireRefs); ++aWireRefId) - { - const BRepGraphInc::WireRef& aRef = theWireRefs.Value(static_cast(aWireRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Face || !aRef.WireDefId.IsValid()) - { - continue; - } - if (aRef.ParentId.Index >= theFaces.Size() || aRef.WireDefId.Index >= theWires.Size()) - { - return false; - } - const BRepGraphInc::FaceDef& aFace = theFaces.Value(static_cast(aRef.ParentId.Index)); - if (aFace.IsRemoved || theWires.Value(static_cast(aRef.WireDefId.Index)).IsRemoved) - { - continue; - } - if (!containsIndexInTable(myWireToFaces, aRef.WireDefId, BRepGraph_FaceId(aRef.ParentId))) - { - return false; - } - } - - // Check: for each face ref entry, face->shell reverse entry must exist. - const uint32_t aNbFaceRefs = static_cast(theFaceRefs.Size()); - for (BRepGraph_FaceRefId aFaceRefId(0); aFaceRefId.IsValid(aNbFaceRefs); ++aFaceRefId) - { - const BRepGraphInc::FaceRef& aRef = theFaceRefs.Value(static_cast(aFaceRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Shell || !aRef.FaceDefId.IsValid()) - { - continue; - } - if (aRef.ParentId.Index >= theShells.Size() || aRef.FaceDefId.Index >= theFaces.Size()) - { - return false; - } - const BRepGraphInc::ShellDef& aShell = - theShells.Value(static_cast(aRef.ParentId.Index)); - if (aShell.IsRemoved || theFaces.Value(static_cast(aRef.FaceDefId.Index)).IsRemoved) - { - continue; - } - if (!containsIndexInTable(myFaceToShells, aRef.FaceDefId, BRepGraph_ShellId(aRef.ParentId))) - { - return false; - } - } - - // Check: for each shell ref entry, shell->solid reverse entry must exist. - const uint32_t aNbShellRefs = static_cast(theShellRefs.Size()); - for (BRepGraph_ShellRefId aShellRefId(0); aShellRefId.IsValid(aNbShellRefs); ++aShellRefId) - { - const BRepGraphInc::ShellRef& aRef = theShellRefs.Value(static_cast(aShellRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Solid || !aRef.ShellDefId.IsValid()) - { - continue; - } - if (aRef.ParentId.Index >= theSolids.Size() || aRef.ShellDefId.Index >= theShells.Size()) - { - return false; - } - const BRepGraphInc::SolidDef& aSolid = - theSolids.Value(static_cast(aRef.ParentId.Index)); - if (aSolid.IsRemoved || theShells.Value(static_cast(aRef.ShellDefId.Index)).IsRemoved) - { - continue; - } - if (!containsIndexInTable(myShellToSolids, aRef.ShellDefId, BRepGraph_SolidId(aRef.ParentId))) - { - return false; - } - } - - // Check: for each solid ref entry, solid->compsolid reverse entry must exist. - const uint32_t aNbSolidRefs = static_cast(theSolidRefs.Size()); - for (BRepGraph_SolidRefId aSolidRefId(0); aSolidRefId.IsValid(aNbSolidRefs); ++aSolidRefId) - { - const BRepGraphInc::SolidRef& aRef = theSolidRefs.Value(static_cast(aSolidRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::CompSolid - || !aRef.SolidDefId.IsValid()) - { - continue; - } - if (aRef.ParentId.Index >= theCompSolids.Size() || aRef.SolidDefId.Index >= theSolids.Size()) - { - return false; - } - const BRepGraphInc::CompSolidDef& aCompSolid = - theCompSolids.Value(static_cast(aRef.ParentId.Index)); - if (aCompSolid.IsRemoved - || theSolids.Value(static_cast(aRef.SolidDefId.Index)).IsRemoved) - { - continue; - } - if (!containsIndexInTable(myCompSolidsOfSolid, - aRef.SolidDefId, - BRepGraph_CompSolidId(aRef.ParentId))) - { - return false; - } - } - - // Check: for each compound child ref entry, child->compound reverse entry must exist. - const uint32_t aNbChildRefs = static_cast(theChildRefs.Size()); - for (BRepGraph_ChildRefId aChildRefId(0); aChildRefId.IsValid(aNbChildRefs); ++aChildRefId) - { - const BRepGraphInc::ChildRef& aRef = theChildRefs.Value(static_cast(aChildRefId.Index)); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Compound || !aRef.ChildDefId.IsValid()) - { - continue; - } - if (aRef.ParentId.Index >= theCompounds.Size()) - { - return false; - } - const BRepGraphInc::CompoundDef& aCompound = - theCompounds.Value(static_cast(aRef.ParentId.Index)); - if (aCompound.IsRemoved || !isActiveChildNode(aRef.ChildDefId)) - { - continue; - } - - switch (aRef.ChildDefId.NodeKind) - { - case BRepGraph_NodeId::Kind::Solid: - if (!containsIndexInTable(myCompoundsOfSolid, - BRepGraph_SolidId(aRef.ChildDefId), - BRepGraph_CompoundId(aRef.ParentId))) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Shell: - if (!containsIndexInTable(myCompoundsOfShell, - BRepGraph_ShellId(aRef.ChildDefId), - BRepGraph_CompoundId(aRef.ParentId))) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Face: - if (!containsIndexInTable(myCompoundsOfFace, - BRepGraph_FaceId(aRef.ChildDefId), - BRepGraph_CompoundId(aRef.ParentId))) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Compound: - if (!containsIndexInTable(myCompoundsOfCompound, - BRepGraph_CompoundId(aRef.ChildDefId), - BRepGraph_CompoundId(aRef.ParentId))) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::CompSolid: - if (!containsIndexInTable(myCompoundsOfCompSolid, - BRepGraph_CompSolidId(aRef.ChildDefId), - BRepGraph_CompoundId(aRef.ParentId))) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Wire: - if (!containsIndexInTable(myCompoundsOfWire, - BRepGraph_WireId(aRef.ChildDefId), - BRepGraph_CompoundId(aRef.ParentId))) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Edge: - if (!containsIndexInTable(myCompoundsOfEdge, - BRepGraph_EdgeId(aRef.ChildDefId), - BRepGraph_CompoundId(aRef.ParentId))) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Vertex: - if (!containsIndexInTable(myCompoundsOfVertex, - BRepGraph_VertexId(aRef.ChildDefId), - BRepGraph_CompoundId(aRef.ParentId))) - { - return false; - } - break; - default: - return false; - } - } - - // Check reverse tables for stale/extra entries not backed by active forward refs. - const uint32_t aNbEdgeToWires = static_cast(myEdgeToWires.Size()); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdgeToWires); ++anEdgeId) - { - const NCollection_DynamicArray& aWires = WiresOfEdgeRef(anEdgeId); - for (const BRepGraph_WireId& aWireId2 : aWires) - { - if (!aWireId2.IsValid(static_cast(theWires.Size()))) - { - return false; - } - if (!hasActiveWireUsageOfEdge(aWireId2, anEdgeId)) - { - return false; - } - } - } - - const uint32_t aNbEdgeToCoEdges = static_cast(myEdgeToCoEdges.Size()); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdgeToCoEdges); ++anEdgeId) - { - const NCollection_DynamicArray& aCoEdges = CoEdgesOfEdgeRef(anEdgeId); - for (const BRepGraph_CoEdgeId& aCoEdgeId2 : aCoEdges) - { - if (!aCoEdgeId2.IsValid(static_cast(theCoEdges.Size()))) - { - return false; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = - theCoEdges.Value(static_cast(aCoEdgeId2.Index)); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid() || aCoEdge.EdgeDefId != anEdgeId) - { - return false; - } - } - } - - const uint32_t aNbWireToFaces = static_cast(myWireToFaces.Size()); - for (BRepGraph_WireId aWireId(0); aWireId.IsValid(aNbWireToFaces); ++aWireId) - { - const NCollection_DynamicArray& aFaces = FacesOfWireRef(aWireId); - for (const BRepGraph_FaceId& aFaceId2 : aFaces) - { - if (!aFaceId2.IsValid(static_cast(theFaces.Size()))) - { - return false; - } - if (!hasActiveFaceRefForWire(aWireId, aFaceId2)) - { - return false; - } - } - } - - const uint32_t aNbEdgeToFaces = static_cast(myEdgeToFaces.Size()); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdgeToFaces); ++anEdgeId) - { - const NCollection_DynamicArray* aFaces = - seekVec(myEdgeToFaces, anEdgeId.Index); - if (aFaces == nullptr) - { - continue; - } - for (const BRepGraph_FaceId& aFaceId2 : *aFaces) - { - if (!aFaceId2.IsValid(static_cast(theFaces.Size()))) - { - return false; - } - if (!hasActiveFaceForEdge(anEdgeId, aFaceId2)) - { - return false; - } - } - } - - const uint32_t aNbFaceToShells = static_cast(myFaceToShells.Size()); - for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaceToShells); ++aFaceId) - { - const NCollection_DynamicArray* aShellsVec = - seekVec(myFaceToShells, aFaceId.Index); - if (aShellsVec == nullptr) - { - continue; - } - for (const BRepGraph_ShellId& aShellId2 : *aShellsVec) - { - if (!aShellId2.IsValid(static_cast(theShells.Size()))) - { - return false; - } - if (!hasActiveFaceRef(aShellId2, aFaceId)) - { - return false; - } - } - } - - const uint32_t aNbShellToSolids = static_cast(myShellToSolids.Size()); - for (BRepGraph_ShellId aShellId(0); aShellId.IsValid(aNbShellToSolids); ++aShellId) - { - const NCollection_DynamicArray* aSolidsVec = - seekVec(myShellToSolids, aShellId.Index); - if (aSolidsVec == nullptr) - { - continue; - } - for (const BRepGraph_SolidId& aSolidId2 : *aSolidsVec) - { - if (!aSolidId2.IsValid(static_cast(theSolids.Size()))) - { - return false; - } - if (!hasActiveShellRef(aSolidId2, aShellId)) - { - return false; - } - } - } - - const uint32_t aNbCompoundsOfSolid = static_cast(myCompoundsOfSolid.Size()); - for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(aNbCompoundsOfSolid); ++aSolidId) - { - const NCollection_DynamicArray* aCompoundsVec = - seekVec(myCompoundsOfSolid, aSolidId.Index); - if (aCompoundsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompoundId& aCompoundId : *aCompoundsVec) - { - if (!aCompoundId.IsValid(static_cast(theCompounds.Size()))) - { - return false; - } - if (!hasActiveCompoundChildRef(aCompoundId, aSolidId)) - { - return false; - } - } - } - - const uint32_t aNbCompSolidsOfSolid = static_cast(myCompSolidsOfSolid.Size()); - for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(aNbCompSolidsOfSolid); ++aSolidId) - { - const NCollection_DynamicArray* aCompSolidsVec = - seekVec(myCompSolidsOfSolid, aSolidId.Index); - if (aCompSolidsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompSolidId& aCompSolidId : *aCompSolidsVec) - { - if (!aCompSolidId.IsValid(static_cast(theCompSolids.Size()))) - { - return false; - } - if (!hasActiveCompSolidRef(aCompSolidId, aSolidId)) - { - return false; - } - } - } - - const uint32_t aNbCompoundsOfShell = static_cast(myCompoundsOfShell.Size()); - for (BRepGraph_ShellId aShellId(0); aShellId.IsValid(aNbCompoundsOfShell); ++aShellId) - { - const NCollection_DynamicArray* aCompoundsVec = - seekVec(myCompoundsOfShell, aShellId.Index); - if (aCompoundsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompoundId& aCompoundId : *aCompoundsVec) - { - if (!aCompoundId.IsValid(static_cast(theCompounds.Size()))) - { - return false; - } - if (!hasActiveCompoundChildRef(aCompoundId, aShellId)) - { - return false; - } - } - } - - const uint32_t aNbCompoundsOfFace = static_cast(myCompoundsOfFace.Size()); - for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbCompoundsOfFace); ++aFaceId) - { - const NCollection_DynamicArray* aCompoundsVec = - seekVec(myCompoundsOfFace, aFaceId.Index); - if (aCompoundsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompoundId& aCompoundId : *aCompoundsVec) - { - if (!aCompoundId.IsValid(static_cast(theCompounds.Size()))) - { - return false; - } - if (!hasActiveCompoundChildRef(aCompoundId, aFaceId)) - { - return false; - } - } - } - - const uint32_t aNbCompoundsOfCompound = static_cast(myCompoundsOfCompound.Size()); - for (BRepGraph_CompoundId aCompoundId(0); aCompoundId.IsValid(aNbCompoundsOfCompound); - ++aCompoundId) - { - const NCollection_DynamicArray* aCompoundsVec = - seekVec(myCompoundsOfCompound, aCompoundId.Index); - if (aCompoundsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompoundId& aParentCompoundId : *aCompoundsVec) - { - if (!aParentCompoundId.IsValid(static_cast(theCompounds.Size()))) - { - return false; - } - if (!hasActiveCompoundChildRef(aParentCompoundId, aCompoundId)) - { - return false; - } - } - } - - const uint32_t aNbCompoundsOfCompSolid = static_cast(myCompoundsOfCompSolid.Size()); - for (BRepGraph_CompSolidId aCompSolidId(0); aCompSolidId.IsValid(aNbCompoundsOfCompSolid); - ++aCompSolidId) - { - const NCollection_DynamicArray* aCompoundsVec = - seekVec(myCompoundsOfCompSolid, aCompSolidId.Index); - if (aCompoundsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompoundId& aCompoundId : *aCompoundsVec) - { - if (!aCompoundId.IsValid(static_cast(theCompounds.Size()))) - { - return false; - } - if (!hasActiveCompoundChildRef(aCompoundId, aCompSolidId)) - { - return false; - } - } - } - - // Check reverse: myCompoundsOfWire - each entry must be backed by an active compound ChildRef. - const uint32_t aNbCompoundsOfWire = static_cast(myCompoundsOfWire.Size()); - for (BRepGraph_WireId aWireId(0); aWireId.IsValid(aNbCompoundsOfWire); ++aWireId) - { - const NCollection_DynamicArray* aCompoundsVec = - seekVec(myCompoundsOfWire, aWireId.Index); - if (aCompoundsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompoundId& aCompoundId : *aCompoundsVec) - { - if (!aCompoundId.IsValid(static_cast(theCompounds.Size()))) - { - return false; - } - if (!hasActiveCompoundChildRef(aCompoundId, aWireId)) - { - return false; - } - } - } - - // Check reverse: myCompoundsOfEdge - each entry must be backed by an active compound ChildRef. - const uint32_t aNbCompoundsOfEdge = static_cast(myCompoundsOfEdge.Size()); - for (BRepGraph_EdgeId anEdgeId2(0); anEdgeId2.IsValid(aNbCompoundsOfEdge); ++anEdgeId2) - { - const NCollection_DynamicArray* aCompoundsVec = - seekVec(myCompoundsOfEdge, anEdgeId2.Index); - if (aCompoundsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompoundId& aCompoundId : *aCompoundsVec) - { - if (!aCompoundId.IsValid(static_cast(theCompounds.Size()))) - { - return false; - } - if (!hasActiveCompoundChildRef(aCompoundId, anEdgeId2)) - { - return false; - } - } - } - - // Check reverse: myCompoundsOfVertex - each entry must be backed by an active compound ChildRef. - const uint32_t aNbCompoundsOfVertex = static_cast(myCompoundsOfVertex.Size()); - for (BRepGraph_VertexId aVertexId(0); aVertexId.IsValid(aNbCompoundsOfVertex); ++aVertexId) - { - const NCollection_DynamicArray* aCompoundsVec = - seekVec(myCompoundsOfVertex, aVertexId.Index); - if (aCompoundsVec == nullptr) - { - continue; - } - for (const BRepGraph_CompoundId& aCompoundId : *aCompoundsVec) - { - if (!aCompoundId.IsValid(static_cast(theCompounds.Size()))) - { - return false; - } - if (!hasActiveCompoundChildRef(aCompoundId, aVertexId)) - { - return false; - } - } - } - - // Check reverse: myCoEdgeToWires - each entry must be backed by an active CoEdgeRef in that wire. - const uint32_t aNbCoEdgeToWires = static_cast(myCoEdgeToWires.Size()); - for (BRepGraph_CoEdgeId aCoEdgeId2(0); aCoEdgeId2.IsValid(aNbCoEdgeToWires); ++aCoEdgeId2) - { - const NCollection_DynamicArray* aWiresVec = - seekVec(myCoEdgeToWires, aCoEdgeId2.Index); - if (aWiresVec == nullptr) - { - continue; - } - for (const BRepGraph_WireId& aWireId2 : *aWiresVec) - { - if (aWireId2.Index >= theWires.Size()) - { - return false; - } - const BRepGraphInc::WireDef& aWire = theWires.Value(static_cast(aWireId2.Index)); - if (aWire.IsRemoved) - { - // Stale entry: wire was removed but myCoEdgeToWires was not updated. - return false; - } - bool aFound = false; - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId2 : aWire.CoEdgeRefIds) - { - if (aCoEdgeRefId2.Index >= theCoEdgeRefs.Size()) - { - continue; - } - const BRepGraphInc::CoEdgeRef& aRef2 = - theCoEdgeRefs.Value(static_cast(aCoEdgeRefId2.Index)); - if (aRef2.IsRemoved || !aRef2.CoEdgeDefId.IsValid()) - { - continue; - } - if (aRef2.CoEdgeDefId.Index == aCoEdgeId2.Index) - { - aFound = true; - break; - } - } - if (!aFound) - { - return false; - } - } - } - - return true; -} - -//================================================================================================= - -void BRepGraphInc_ReverseIndex::BuildProductOccurrences( - const NCollection_DynamicArray& theOccurrences, - const uint32_t theNbProducts) -{ - myProductToOccurrences.Clear(); - preSize(myProductToOccurrences, theNbProducts, myAllocator); - - BRepGraph_OccurrenceId anOccurrenceId(0); - for (const BRepGraphInc::OccurrenceDef& anOcc : theOccurrences) - { - if (!anOcc.IsRemoved && anOcc.ChildDefId.IsValid() - && anOcc.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) - { - appendDirect(myProductToOccurrences, anOcc.ChildDefId.Index, anOccurrenceId); - } - ++anOccurrenceId; - } -} diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.hxx deleted file mode 100644 index 95379e9efe..0000000000 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_ReverseIndex.hxx +++ /dev/null @@ -1,618 +0,0 @@ -// 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. - -#ifndef _BRepGraphInc_ReverseIndex_HeaderFile -#define _BRepGraphInc_ReverseIndex_HeaderFile - -#include -#include -#include -#include - -class BRepGraphInc_Storage; - -namespace BRepGraphInc -{ -struct VertexDef; -struct EdgeDef; -struct CoEdgeDef; -struct WireDef; -struct FaceDef; -struct ShellDef; -struct SolidDef; -struct CompoundDef; -struct CompSolidDef; -struct ProductDef; -struct OccurrenceDef; -struct ShellRef; -struct FaceRef; -struct WireRef; -struct CoEdgeRef; -struct SolidRef; -struct ChildRef; -struct VertexRef; -} // namespace BRepGraphInc - -//! @brief Backend reverse incidence indices for O(1) upward navigation. -//! -//! Built from entity and reference-entry tables after population. -//! Full ReverseIndex::Build() is used for initial construction, while builder-side -//! mutations maintain the index incrementally through targeted bind/unbind -//! operations and ReverseIndex::BuildDelta() for append workflows. -//! -//! ## Two query tiers -//! Pointer-returning methods (e.g. WiresOfEdge() -> nullptr for empty) serve -//! performance-critical backend code that avoids static-empty-vector overhead. -//! Safe-reference methods (e.g. WiresOfEdgeRef() -> static empty vector) serve -//! the public facade (TopoView delegates to Ref variants). -class BRepGraphInc_ReverseIndex -{ -public: - DEFINE_STANDARD_ALLOC - - //! Set allocator for internal index tables. - void SetAllocator(const occ::handle& theAlloc) - { - myAllocator = theAlloc; - } - - //! Clear all indices. - Standard_EXPORT void Clear(); - - //! Rebuild all reverse indices from storage tables. - //! Thin wrapper over the explicit-table overload retained for compatibility. - Standard_EXPORT void Build(const BRepGraphInc_Storage& theStorage); - - //! Rebuild all reverse indices from the entity and reference-entry tables. - //! Edge-to-face index is derived from CoEdge.FaceDefId links. - //! @pre SetAllocator() must have been called (uses myAllocator for inner vectors). - //! @param[in] theEdges edge entity vector (for vertex-to-edge, edge-to-face) - //! @param[in] theCoEdges coedge entity vector (for edge-to-coedge and edge-to-face) - //! @param[in] theWires wire entity vector (parent validation for coedge refs) - //! @param[in] theFaces face entity vector (parent validation for wire refs) - //! @param[in] theShells shell entity vector (parent validation for face refs) - //! @param[in] theSolids solid entity vector (parent validation for shell refs) - //! @param[in] theCompounds compound entity vector (parent validation for child refs) - //! @param[in] theCompSolids compsolid entity vector (parent validation for solid refs) - //! @param[in] theShellRefs shell ref-entry table (solid -> shell reverse) - //! @param[in] theFaceRefs face ref-entry table (shell -> face reverse) - //! @param[in] theWireRefs wire ref-entry table (face -> wire reverse) - //! @param[in] theCoEdgeRefs coedge ref-entry table (wire -> coedge/edge reverse) - //! @param[in] theSolidRefs solid ref-entry table (compsolid -> solid reverse) - //! @param[in] theChildRefs child ref-entry table (compound child reverse) - //! @param[in] theVertexRefs vertex ref-entry table (edge vertex resolution) - Standard_EXPORT void Build( - const NCollection_DynamicArray& theVertices, - const NCollection_DynamicArray& theEdges, - const NCollection_DynamicArray& theCoEdges, - const NCollection_DynamicArray& theWires, - const NCollection_DynamicArray& theFaces, - const NCollection_DynamicArray& theShells, - const NCollection_DynamicArray& theSolids, - const NCollection_DynamicArray& theCompounds, - const NCollection_DynamicArray& theCompSolids, - const NCollection_DynamicArray& theShellRefs, - const NCollection_DynamicArray& theFaceRefs, - const NCollection_DynamicArray& theWireRefs, - const NCollection_DynamicArray& theCoEdgeRefs, - const NCollection_DynamicArray& theSolidRefs, - const NCollection_DynamicArray& theChildRefs, - const NCollection_DynamicArray& theVertexRefs); - - //! Incrementally update reverse indices for entities/ref-parents appended after a previous - //! ReverseIndex::Build(). Only processes entities from the old counts to the current vector - //! lengths and appended reference entries. - //! @param[in] theOldNbEdges edge count before the append operation - //! @param[in] theOldNbWires wire count before the append operation - //! @param[in] theOldNbFaces face count before the append operation - //! @param[in] theOldNbShells shell count before the append operation - //! @param[in] theOldNbSolids solid count before the append operation - //! @param[in] theOldNbCompounds compound count before the append operation - //! @param[in] theOldNbCompSolids compsolid count before the append operation - //! @param[in] theOldNbChildRefs ChildRef count before the append operation - //! @param[in] theOldNbSolidRefs SolidRef count before the append operation - Standard_EXPORT void BuildDelta( - const NCollection_DynamicArray& theVertices, - const NCollection_DynamicArray& theEdges, - const NCollection_DynamicArray& theCoEdges, - const NCollection_DynamicArray& theWires, - const NCollection_DynamicArray& theFaces, - const NCollection_DynamicArray& theShells, - const NCollection_DynamicArray& theSolids, - const NCollection_DynamicArray& theCompounds, - const NCollection_DynamicArray& theCompSolids, - const NCollection_DynamicArray& theShellRefs, - const NCollection_DynamicArray& theFaceRefs, - const NCollection_DynamicArray& theWireRefs, - const NCollection_DynamicArray& theCoEdgeRefs, - const NCollection_DynamicArray& theSolidRefs, - const NCollection_DynamicArray& theChildRefs, - const NCollection_DynamicArray& theVertexRefs, - const uint32_t theOldNbEdges, - const uint32_t theOldNbWires, - const uint32_t theOldNbFaces, - const uint32_t theOldNbShells, - const uint32_t theOldNbSolids, - const uint32_t theOldNbCompounds, - const uint32_t theOldNbCompSolids, - const uint32_t theOldNbChildRefs, - const uint32_t theOldNbSolidRefs); - - //! Build product-to-occurrences reverse index. - //! @param[in] theOccurrences occurrence entity vector - //! @param[in] theNbProducts total number of products (for pre-sizing) - Standard_EXPORT void BuildProductOccurrences( - const NCollection_DynamicArray& theOccurrences, - const uint32_t theNbProducts); - - //! Return wire indices containing the given edge. - [[nodiscard]] const NCollection_DynamicArray* WiresOfEdge( - const BRepGraph_EdgeId theEdgeId) const - { - return seekVec(myEdgeToWires, theEdgeId.Index); - } - - //! Return face indices containing the given edge (derived from CoEdge.FaceDefId links). - [[nodiscard]] const NCollection_DynamicArray* FacesOfEdge( - const BRepGraph_EdgeId theEdgeId) const - { - return seekVec(myEdgeToFaces, theEdgeId.Index); - } - - //! Return coedge indices referencing the given edge. - [[nodiscard]] const NCollection_DynamicArray* CoEdgesOfEdge( - const BRepGraph_EdgeId theEdgeId) const - { - return seekVec(myEdgeToCoEdges, theEdgeId.Index); - } - - //! Return the number of faces incident to an edge - O(1). - //! Derived directly from the edge-to-faces adjacency vector to keep a single source of truth. - [[nodiscard]] uint32_t NbFacesOfEdge(const BRepGraph_EdgeId theEdgeId) const - { - const NCollection_DynamicArray* aFaces = - seekVec(myEdgeToFaces, theEdgeId.Index); - return aFaces != nullptr ? static_cast(aFaces->Size()) : 0u; - } - - //! Return edge indices incident to the given vertex. - [[nodiscard]] const NCollection_DynamicArray* EdgesOfVertex( - const BRepGraph_VertexId theVertexId) const - { - return seekVec(myVertexToEdges, theVertexId.Index); - } - - //! Return face indices containing the given wire. - [[nodiscard]] const NCollection_DynamicArray* FacesOfWire( - const BRepGraph_WireId theWireId) const - { - return seekVec(myWireToFaces, theWireId.Index); - } - - //! Return shell indices containing the given face. - [[nodiscard]] const NCollection_DynamicArray* ShellsOfFace( - const BRepGraph_FaceId theFaceId) const - { - return seekVec(myFaceToShells, theFaceId.Index); - } - - //! Return solid indices containing the given shell. - [[nodiscard]] const NCollection_DynamicArray* SolidsOfShell( - const BRepGraph_ShellId theShellId) const - { - return seekVec(myShellToSolids, theShellId.Index); - } - - //! Return compound indices containing the given solid as a NodeInstance. - [[nodiscard]] const NCollection_DynamicArray* CompoundsOfSolid( - const BRepGraph_SolidId theSolidId) const - { - return seekVec(myCompoundsOfSolid, theSolidId.Index); - } - - //! Return compsolid indices containing the given solid as a SolidInstance. - [[nodiscard]] const NCollection_DynamicArray* CompSolidsOfSolid( - const BRepGraph_SolidId theSolidId) const - { - return seekVec(myCompSolidsOfSolid, theSolidId.Index); - } - - //! Return compound indices containing the given shell as a NodeInstance. - [[nodiscard]] const NCollection_DynamicArray* CompoundsOfShell( - const BRepGraph_ShellId theShellId) const - { - return seekVec(myCompoundsOfShell, theShellId.Index); - } - - //! Return compound indices containing the given face as a NodeInstance. - [[nodiscard]] const NCollection_DynamicArray* CompoundsOfFace( - const BRepGraph_FaceId theFaceId) const - { - return seekVec(myCompoundsOfFace, theFaceId.Index); - } - - //! Return compound indices containing the given compound as a NodeInstance. - [[nodiscard]] const NCollection_DynamicArray* CompoundsOfCompound( - const BRepGraph_CompoundId theCompoundId) const - { - return seekVec(myCompoundsOfCompound, theCompoundId.Index); - } - - //! Return compound indices containing the given compsolid as a NodeInstance. - [[nodiscard]] const NCollection_DynamicArray* CompoundsOfCompSolid( - const BRepGraph_CompSolidId theCompSolidId) const - { - return seekVec(myCompoundsOfCompSolid, theCompSolidId.Index); - } - - //! Return compound indices containing the given wire as a NodeInstance. - //! OCCT `TopoDS_Compound` can legally hold atomic topology (wire / edge / - //! vertex); these reverse maps round-trip that case. - [[nodiscard]] const NCollection_DynamicArray* CompoundsOfWire( - const BRepGraph_WireId theWireId) const - { - return seekVec(myCompoundsOfWire, theWireId.Index); - } - - //! Return compound indices containing the given edge as a NodeInstance. - [[nodiscard]] const NCollection_DynamicArray* CompoundsOfEdge( - const BRepGraph_EdgeId theEdgeId) const - { - return seekVec(myCompoundsOfEdge, theEdgeId.Index); - } - - //! Return compound indices containing the given vertex as a NodeInstance. - [[nodiscard]] const NCollection_DynamicArray* CompoundsOfVertex( - const BRepGraph_VertexId theVertexId) const - { - return seekVec(myCompoundsOfVertex, theVertexId.Index); - } - - //! Return wire indices containing the given coedge. - [[nodiscard]] const NCollection_DynamicArray* WiresOfCoEdge( - const BRepGraph_CoEdgeId theCoEdgeId) const - { - return seekVec(myCoEdgeToWires, theCoEdgeId.Index); - } - - //! Return occurrence indices that reference the given product. - [[nodiscard]] const NCollection_DynamicArray* OccurrencesOfProduct( - const BRepGraph_ProductId theProductId) const - { - return seekVec(myProductToOccurrences, theProductId.Index); - } - - // --- Safe reference accessors (return empty vector instead of nullptr) --- - - //! Return wire indices containing the given edge (safe reference, never null). - [[nodiscard]] const NCollection_DynamicArray& WiresOfEdgeRef( - const BRepGraph_EdgeId theEdgeId) const - { - return seekRef(myEdgeToWires, theEdgeId.Index); - } - - //! Return face indices containing the given edge (safe reference, never null). - [[nodiscard]] const NCollection_DynamicArray& FacesOfEdgeRef( - const BRepGraph_EdgeId theEdgeId) const - { - return seekRef(myEdgeToFaces, theEdgeId.Index); - } - - //! Return coedge indices referencing the given edge (safe reference, never null). - [[nodiscard]] const NCollection_DynamicArray& CoEdgesOfEdgeRef( - const BRepGraph_EdgeId theEdgeId) const - { - return seekRef(myEdgeToCoEdges, theEdgeId.Index); - } - - //! Return face indices containing the given wire (safe reference, never null). - [[nodiscard]] const NCollection_DynamicArray& FacesOfWireRef( - const BRepGraph_WireId theWireId) const - { - return seekRef(myWireToFaces, theWireId.Index); - } - - //! Return edge indices incident to the given vertex (safe reference, never null). - [[nodiscard]] const NCollection_DynamicArray& EdgesOfVertexRef( - const BRepGraph_VertexId theVertexId) const - { - return seekRef(myVertexToEdges, theVertexId.Index); - } - - //! Return shell indices containing the given face (safe reference, never null). - [[nodiscard]] const NCollection_DynamicArray& ShellsOfFaceRef( - const BRepGraph_FaceId theFaceId) const - { - return seekRef(myFaceToShells, theFaceId.Index); - } - - //! Return solid indices containing the given shell (safe reference, never null). - [[nodiscard]] const NCollection_DynamicArray& SolidsOfShellRef( - const BRepGraph_ShellId theShellId) const - { - return seekRef(myShellToSolids, theShellId.Index); - } - - //! Verify reverse index consistency against forward entity/reference-entry tables. - //! For each forward ref (e.g., wire->edge), checks that the corresponding - //! reverse entry exists (edge->wire). Intended for debug validation. - //! @return true if all forward refs have matching reverse entries - Standard_EXPORT bool Validate( - const NCollection_DynamicArray& theVertices, - const NCollection_DynamicArray& theEdges, - const NCollection_DynamicArray& theCoEdges, - const NCollection_DynamicArray& theWires, - const NCollection_DynamicArray& theFaces, - const NCollection_DynamicArray& theShells, - const NCollection_DynamicArray& theSolids, - const NCollection_DynamicArray& theCompounds, - const NCollection_DynamicArray& theCompSolids, - const NCollection_DynamicArray& theShellRefs, - const NCollection_DynamicArray& theFaceRefs, - const NCollection_DynamicArray& theWireRefs, - const NCollection_DynamicArray& theCoEdgeRefs, - const NCollection_DynamicArray& theSolidRefs, - const NCollection_DynamicArray& theChildRefs, - const NCollection_DynamicArray& theVertexRefs) const; - - // --- Incremental mutation --- - - //! Register an edge as belonging to a wire (O(1) amortized). - Standard_EXPORT void BindEdgeToWire(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_WireId theWireId); - - //! Remove a wire from the edge-to-wire index for a given edge. - Standard_EXPORT void UnbindEdgeFromWire(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_WireId theWireId); - - //! Replace an edge in the edge-to-wire index for a specific wire. - Standard_EXPORT void ReplaceEdgeInWireMap(const BRepGraph_EdgeId theOldEdgeId, - const BRepGraph_EdgeId theNewEdgeId, - const BRepGraph_WireId theWireId); - - //! Register a vertex as incident to an edge (O(1) amortized, deduplicates). - Standard_EXPORT void BindVertexToEdge(const BRepGraph_VertexId theVertexId, - const BRepGraph_EdgeId theEdgeId); - - //! Remove an edge from the vertex-to-edge index for a given vertex. - Standard_EXPORT void UnbindVertexFromEdge(const BRepGraph_VertexId theVertexId, - const BRepGraph_EdgeId theEdgeId); - - //! Register a coedge as referencing an edge (O(1) amortized). - Standard_EXPORT void BindEdgeToCoEdge(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_CoEdgeId theCoEdgeId); - - //! Remove a coedge from the edge-to-coedge index for a given edge. - Standard_EXPORT void UnbindEdgeFromCoEdge(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_CoEdgeId theCoEdgeId); - - //! Register a coedge as belonging to a wire (O(1) amortized). - Standard_EXPORT void BindCoEdgeToWire(const BRepGraph_CoEdgeId theCoEdgeId, - const BRepGraph_WireId theWireId); - - //! Remove a wire from the coedge-to-wire index for a given coedge. - Standard_EXPORT void UnbindCoEdgeFromWire(const BRepGraph_CoEdgeId theCoEdgeId, - const BRepGraph_WireId theWireId); - - //! Register an edge as belonging to a face (O(1) amortized, deduplicates). - Standard_EXPORT void BindEdgeToFace(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_FaceId theFaceId); - - //! Remove a face from the edge-to-face index for a given edge. - Standard_EXPORT void UnbindEdgeFromFace(const BRepGraph_EdgeId theEdgeId, - const BRepGraph_FaceId theFaceId); - - //! Register a wire as belonging to a face (O(1) amortized, deduplicates). - Standard_EXPORT void BindWireToFace(const BRepGraph_WireId theWireId, - const BRepGraph_FaceId theFaceId); - - //! Remove a face from the wire-to-face index for a given wire. - Standard_EXPORT void UnbindWireFromFace(const BRepGraph_WireId theWireId, - const BRepGraph_FaceId theFaceId); - - //! Register a face as belonging to a shell (O(1) amortized, deduplicates). - Standard_EXPORT void BindFaceToShell(const BRepGraph_FaceId theFaceId, - const BRepGraph_ShellId theShellId); - - //! Remove a shell from the face-to-shell index for a given face. - Standard_EXPORT void UnbindFaceFromShell(const BRepGraph_FaceId theFaceId, - const BRepGraph_ShellId theShellId); - - //! Register a shell as belonging to a solid (O(1) amortized, deduplicates). - Standard_EXPORT void BindShellToSolid(const BRepGraph_ShellId theShellId, - const BRepGraph_SolidId theSolidId); - - //! Remove a solid from the shell-to-solid index for a given shell. - Standard_EXPORT void UnbindShellFromSolid(const BRepGraph_ShellId theShellId, - const BRepGraph_SolidId theSolidId); - - //! Register a solid as belonging to a compsolid (O(1) amortized, deduplicates). - Standard_EXPORT void BindSolidToCompSolid(const BRepGraph_SolidId theSolidId, - const BRepGraph_CompSolidId theCompSolidId); - - //! Remove a compsolid from the solid-to-compsolid index for a given solid. - Standard_EXPORT void UnbindSolidFromCompSolid(const BRepGraph_SolidId theSolidId, - const BRepGraph_CompSolidId theCompSolidId); - - //! Register a child node as belonging to a compound (dispatched on NodeKind). - //! Routes to the appropriate per-kind compound reverse map. No-op for unsupported kinds. - Standard_EXPORT void BindCompoundChild(const BRepGraph_NodeId theChildDefId, - const BRepGraph_CompoundId theCompoundId); - - //! Remove a compound from the per-kind compound reverse map for a given child node. - Standard_EXPORT void UnbindCompoundChild(const BRepGraph_NodeId theChildDefId, - const BRepGraph_CompoundId theCompoundId); - - //! Register an occurrence as referencing a product (O(1) amortized, deduplicates). - Standard_EXPORT void BindProductOccurrence(const BRepGraph_OccurrenceId theOccurrenceId, - const BRepGraph_ProductId theProductId); - - //! Remove an occurrence from the product-to-occurrences index for a given product. - Standard_EXPORT void UnbindProductOccurrence(const BRepGraph_OccurrenceId theOccurrenceId, - const BRepGraph_ProductId theProductId); - -private: - //! Dense vector type: outer index = entity key, inner vector = typed adjacency list. - template - using TypedIndexTable = NCollection_DynamicArray>; - - //! Bounds-checked lookup returning nullptr for out-of-range or empty slots. - template - static const NCollection_DynamicArray* seekVec(const TypedIndexTable& theIdx, - const uint32_t theKey) - { - if (theKey >= theIdx.Size()) - return nullptr; - const NCollection_DynamicArray& aVec = theIdx.Value(static_cast(theKey)); - return aVec.IsEmpty() ? nullptr : &aVec; - } - - //! Bounds-checked lookup returning a const reference (empty vector for missing keys). - template - static const NCollection_DynamicArray& seekRef(const TypedIndexTable& theIdx, - const uint32_t theKey) - { - const NCollection_DynamicArray* aPtr = seekVec(theIdx, theKey); - if (aPtr != nullptr) - return *aPtr; - static const NCollection_DynamicArray THE_EMPTY; - return THE_EMPTY; - } - - //! Ensure theIdx has at least theSize slots (pre-sizing with empty vectors). - //! If theAlloc is non-null, inner vectors are constructed with it. - template - static void ensureSize(TypedIndexTable& theIdx, - const uint32_t theSize, - const occ::handle& theAlloc = - occ::handle()) - { - if (theSize <= theIdx.Size()) - return; - - if (!theAlloc.IsNull()) - { - for (size_t i = theIdx.Size(), aNb = static_cast(theSize); i < aNb; ++i) - { - theIdx.Append(NCollection_DynamicArray(16, theAlloc)); - } - } - else - { - for (size_t i = theIdx.Size(), aNb = static_cast(theSize); i < aNb; ++i) - { - theIdx.Appended(); - } - } - } - - //! Ensure theVec has at least theSize elements. - //! New elements are default-constructed (zero for scalar types). - template - static void ensureSize(NCollection_DynamicArray& theVec, const uint32_t theSize) - { - while (theVec.Size() < static_cast(theSize)) - { - theVec.Appended(); - } - } - - //! Resize theIdx exactly to theSize slots (clears previous content first). - template - static void preSize(TypedIndexTable& theIdx, - const uint32_t theSize, - const occ::handle& theAlloc = - occ::handle()) - { - theIdx.Clear(); - ensureSize(theIdx, theSize, theAlloc); - } - - //! Add theVal to the vector at theKey, creating if needed. Skips duplicates. - template - static void appendUnique(TypedIndexTable& theIdx, const uint32_t theKey, const T theVal) - { - if (theKey >= theIdx.Size()) - ensureSize(theIdx, theKey + 1u); - - NCollection_DynamicArray& aVec = theIdx.ChangeValue(static_cast(theKey)); - for (const T& anElem : aVec) - { - if (anElem == theVal) - return; - } - aVec.Append(theVal); - } - - //! Add theVal to the vector at theKey unconditionally (no duplicate check). - //! Used during ReverseIndex::Build() where freshly-cleared indices guarantee no duplicates. - template - static void appendDirect(TypedIndexTable& theIdx, const uint32_t theKey, const T theVal) - { - if (theKey >= theIdx.Size()) - ensureSize(theIdx, theKey + 1u); - - theIdx.ChangeValue(static_cast(theKey)).Append(theVal); - } - - //! Remove first occurrence of theVal from the vector at theKey via swap-with-last + erase-last. - //! No-op if theKey is out of range or theVal is absent. O(N) lookup, O(1) removal. - template - static void eraseSwapLast(TypedIndexTable& theIdx, const uint32_t theKey, const T theVal) - { - if (theKey >= theIdx.Size()) - return; - NCollection_DynamicArray& aVec = theIdx.ChangeValue(static_cast(theKey)); - const size_t aNb = aVec.Size(); - for (size_t i = 0; i < aNb; ++i) - { - if (aVec.Value(i) == theVal) - { - if (i + 1u < aNb) - aVec.ChangeValue(i) = aVec.Value(aNb - 1u); - aVec.EraseLast(); - return; - } - } - } - - occ::handle myAllocator; - - TypedIndexTable myEdgeToWires; - TypedIndexTable myEdgeToFaces; - TypedIndexTable myEdgeToCoEdges; - TypedIndexTable myVertexToEdges; - TypedIndexTable myWireToFaces; - TypedIndexTable myFaceToShells; - TypedIndexTable myShellToSolids; - TypedIndexTable myProductToOccurrences; - - TypedIndexTable myCompoundsOfSolid; //!< Solid -> parent Compound indices. - TypedIndexTable - myCompSolidsOfSolid; //!< Solid -> parent CompSolid indices. - TypedIndexTable myCompoundsOfShell; //!< Shell -> parent Compound indices. - TypedIndexTable myCompoundsOfFace; //!< Face -> parent Compound indices. - TypedIndexTable - myCompoundsOfCompound; //!< Compound -> parent Compound indices. - TypedIndexTable - myCompoundsOfCompSolid; //!< CompSolid -> parent Compound indices. - TypedIndexTable myCompoundsOfWire; //!< Wire -> parent Compound indices. - TypedIndexTable myCompoundsOfEdge; //!< Edge -> parent Compound indices. - TypedIndexTable myCompoundsOfVertex; //!< Vertex -> parent Compound indices. - TypedIndexTable myCoEdgeToWires; //!< CoEdge -> parent Wire indices. - - uint32_t myNbIndexedCoEdges = - 0; //!< Number of coedges indexed by ReverseIndex::Build()/BuildDelta(). -}; - -#endif // _BRepGraphInc_ReverseIndex_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx index d877a23342..858e426592 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.cxx @@ -13,8 +13,14 @@ #include +#include + +#include +#include #include +#include "BRepGraphInc_WireOrder.pxx" + namespace { @@ -47,204 +53,2578 @@ typename StoreT::ValueType* changeFindInStore(StoreT& theStore, const TypeIdT th return theId.IsValid(theStore.Nb()) ? &theStore.Change(theId) : nullptr; } +template +void prepareDynamicArray(NCollection_DynamicArray& theArray, const uint32_t theCount) +{ + if (theCount == 0) + { + theArray.Clear(true); + return; + } + theArray.SetValue(static_cast(theCount - 1), T()); +} + +template +const NCollection_LinearVector& emptyLinearVector() +{ + static const NCollection_LinearVector THE_EMPTY_VECTOR; + return THE_EMPTY_VECTOR; +} + +template +bool containsRelationId(const NCollection_LinearVector& theIds, const IdT theId) +{ + for (const IdT& anId : theIds) + { + if (anId == theId) + { + return true; + } + } + return false; +} + +template +void appendUniqueRelationId(NCollection_LinearVector& theIds, const IdT theId) +{ + if (!containsRelationId(theIds, theId)) + { + theIds.Append(theId); + } +} + +template +bool eraseRelationId(NCollection_LinearVector& theIds, const IdT theId) +{ + for (size_t anIndex = 0; anIndex < theIds.Size(); ++anIndex) + { + if (theIds.Value(anIndex) == theId) + { + theIds.Erase(anIndex); + return true; + } + } + return false; +} + +template +void prepareRelationTable(NCollection_DynamicArray& theTable, const uint32_t theCount) +{ + if (theCount == 0) + { + theTable.Clear(true); + return; + } + if (theTable.Size() < theCount) + { + theTable.SetValue(static_cast(theCount - 1), RelationT()); + } +} + +bool wireHasFaceUse(const BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theWire, + const BRepGraph_FaceId theFace) +{ + if (!theWire.IsValid(theStorage.NbWires()) || !theFace.IsValid(theStorage.NbFaces())) + { + return false; + } + for (const BRepGraph_WireRefId& aWireRefId : theStorage.WireRelations(theWire).ParentWireRefIds) + { + if (aWireRefId.IsValid(theStorage.NbWireRefs()) && !theStorage.IsRemoved(aWireRefId) + && theStorage.WireRef(aWireRefId).ParentFaceId == theFace) + { + return true; + } + } + return false; +} + +void clearWireFaceContext(BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theWire, + const BRepGraph_FaceId theFace) +{ + if (!theWire.IsValid(theStorage.NbWires()) || !theFace.IsValid(theStorage.NbFaces())) + { + return; + } + + for (const BRepGraph_CoEdgeId& aCoEdgeId : theStorage.WireRelations(theWire).CoEdgeIds) + { + if (!aCoEdgeId.IsValid(theStorage.NbCoEdges()) || theStorage.IsRemoved(aCoEdgeId) + || theStorage.CoEdge(aCoEdgeId).FaceId != theFace) + { + continue; + } + + BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.ChangeCoEdge(aCoEdgeId); + if (aCoEdge.Curve2DRepId.IsValid()) + { + theStorage.MarkRemoved(aCoEdge.Curve2DRepId); + aCoEdge.Curve2DRepId = BRepGraph_CoEdgeCurve2DRepId(); + } + if (aCoEdge.Polygon2DRepId.IsValid()) + { + theStorage.MarkRemoved(aCoEdge.Polygon2DRepId); + aCoEdge.Polygon2DRepId = BRepGraph_CoEdgePolygon2DRepId(); + } + if (aCoEdge.PolygonOnTriRepId.IsValid()) + { + theStorage.MarkRemoved(aCoEdge.PolygonOnTriRepId); + aCoEdge.PolygonOnTriRepId = BRepGraph_CoEdgePolygonOnTriRepId(); + } + aCoEdge.FaceId = BRepGraph_FaceId(); + } +} + +template +void pruneRelationIds(NCollection_LinearVector& theIds, const KeepT& theKeep) +{ + for (size_t anIndex = 0; anIndex < theIds.Size();) + { + if (!theKeep(theIds.Value(anIndex))) + { + theIds.Erase(anIndex); + continue; + } + ++anIndex; + } +} + +template +void assignRelationIds(NCollection_LinearVector& theTarget, + const NCollection_Array1& theSource) +{ + theTarget.Clear(false); + theTarget.Reserve(theSource.Size()); + for (const IdT& anId : theSource) + { + theTarget.Append(anId); + } +} + +template +void prepareDefStore(DefStoreT& theStore, + const uint32_t theCount, + const occ::handle&) +{ + prepareDynamicArray(theStore.Entities, theCount); + theStore.RemovedFlags.Resize(theCount); + theStore.OwnedFlags.Resize(theCount); + theStore.GuardFlags.Resize(theCount); + theStore.HasCompoundParentFlags.Resize(theCount); + theStore.HasOccurrenceParentFlags.Resize(theCount); + theStore.NbActive = theCount; +} + +template +void prepareRefStore(RefStoreT& theStore, const uint32_t theCount) +{ + prepareDynamicArray(theStore.Refs, theCount); + theStore.RemovedFlags.Resize(theCount); + theStore.OwnedFlags.Resize(theCount); + theStore.GuardFlags.Resize(theCount); + theStore.NbActive = theCount; +} + +template +void prepareRepStore(RepStoreT& theStore, const uint32_t theCount) +{ + prepareDynamicArray(theStore.Uses, theCount); + theStore.RemovedFlags.Resize(theCount); + theStore.NbActive = theCount; +} + +template +void recountActiveStore(StoreT& theStore, TypeIdT) +{ + theStore.NbActive = 0; + for (TypeIdT anId = TypeIdT::Start(); anId.IsValid(theStore.Nb()); ++anId) + { + if (!theStore.RemovedFlags.Test(anId.Index)) + { + ++theStore.NbActive; + } + } +} + +bool isNodeRemoved(const BRepGraphInc_Storage& theStorage, const BRepGraph_NodeId theNode) +{ + if (!theNode.IsValid()) + { + return true; + } + switch (theNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + return theStorage.IsRemoved(BRepGraph_VertexId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::Edge: + return theStorage.IsRemoved(BRepGraph_EdgeId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::CoEdge: + return theStorage.IsRemoved(BRepGraph_CoEdgeId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::Wire: + return theStorage.IsRemoved(BRepGraph_WireId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::Face: + return theStorage.IsRemoved(BRepGraph_FaceId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::Shell: + return theStorage.IsRemoved(BRepGraph_ShellId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::Solid: + return theStorage.IsRemoved(BRepGraph_SolidId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::Compound: + return theStorage.IsRemoved(BRepGraph_CompoundId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::CompSolid: + return theStorage.IsRemoved(BRepGraph_CompSolidId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::Product: + return theStorage.IsRemoved(BRepGraph_ProductId::FromNodeId(theNode)); + case BRepGraph_NodeId::Kind::Occurrence: + return theStorage.IsRemoved(BRepGraph_OccurrenceId::FromNodeId(theNode)); + default: + return true; + } +} + +bool coEdgeOrientedVertices(const BRepGraphInc_Storage& theStorage, + const BRepGraph_CoEdgeId theCoEdgeId, + BRepGraph_VertexId& theStartVertex, + BRepGraph_VertexId& theEndVertex) +{ + if (!theCoEdgeId.IsValid(theStorage.NbCoEdges()) || theStorage.IsRemoved(theCoEdgeId)) + { + return false; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(theCoEdgeId); + if (!aCoEdge.ChildEdgeId.IsValid(theStorage.NbEdges()) + || theStorage.IsRemoved(aCoEdge.ChildEdgeId)) + { + return false; + } + + const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(aCoEdge.ChildEdgeId); + const BRepGraph_VertexRefId aStartRef = + (aCoEdge.Orientation == TopAbs_FORWARD) ? anEdge.StartVertexRefId : anEdge.EndVertexRefId; + const BRepGraph_VertexRefId anEndRef = + (aCoEdge.Orientation == TopAbs_FORWARD) ? anEdge.EndVertexRefId : anEdge.StartVertexRefId; + if (!aStartRef.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(aStartRef) + || !anEndRef.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(anEndRef)) + { + return false; + } + + theStartVertex = theStorage.VertexRef(aStartRef).ChildVertexId; + theEndVertex = theStorage.VertexRef(anEndRef).ChildVertexId; + return theStartVertex.IsValid(theStorage.NbVertices()) && !theStorage.IsRemoved(theStartVertex) + && theEndVertex.IsValid(theStorage.NbVertices()) && !theStorage.IsRemoved(theEndVertex); +} + +bool coEdgeIdsAreUnique(const NCollection_LinearVector& theCoEdgeIds) +{ + return BRepGraphInc_WireOrder::CoEdgeIdsAreUnique(theCoEdgeIds.ToArray1()); +} + } // namespace //================================================================================================= -BRepGraphInc_Storage::BRepGraphInc_Storage(const occ::handle& theAlloc) - : myVertices(256, theAlloc), - myEdges(256, theAlloc), - myCoEdges(256, theAlloc), - myWires(256, theAlloc), - myFaces(256, theAlloc), - myShells(256, theAlloc), - mySolids(256, theAlloc), - myCompounds(256, theAlloc), - myCompSolids(256, theAlloc), - myProducts(256, theAlloc), - myOccurrences(256, theAlloc), - myShellRefs(256, theAlloc), - myFaceRefs(256, theAlloc), - myWireRefs(256, theAlloc), - myCoEdgeRefs(256, theAlloc), - myVertexRefs(256, theAlloc), - mySolidRefs(256, theAlloc), - myChildRefs(256, theAlloc), - myOccurrenceRefs(256, theAlloc), - mySurfaces(256, theAlloc), - myCurves3D(256, theAlloc), - myCurves2D(256, theAlloc), - myTriangulationsRep(256, theAlloc), - myPolygons3D(256, theAlloc), - myPolygons2D(256, theAlloc), - myPolygonsOnTri(256, theAlloc), - myTShapeToNodeId(1, theAlloc), - myOriginalShapes(1, theAlloc), - myAllocator(theAlloc.IsNull() ? NCollection_BaseAllocator::CommonBaseAllocator() : theAlloc) +BRepGraphInc_Storage::BRepGraphInc_Storage() + : myRootProductIds(8), + myDeferredModified(128), + myDeferredRefModified(128), + myVertices(256, myAllocator), + myEdges(256, myAllocator), + myCoEdges(256, myAllocator), + myWires(256, myAllocator), + myFaces(256, myAllocator), + myShells(256, myAllocator), + mySolids(256, myAllocator), + myCompounds(256, myAllocator), + myCompSolids(256, myAllocator), + myProducts(64, myAllocator), + myOccurrences(256, myAllocator), + myShellRefs(256, myAllocator), + myFaceRefs(256, myAllocator), + myWireRefs(256, myAllocator), + myVertexRefs(256, myAllocator), + mySolidRefs(256, myAllocator), + myChildRefs(256, myAllocator), + myOccurrenceRefs(256, myAllocator), + myFaceRelations(256, myAllocator), + myWireRelations(256, myAllocator), + myEdgeRelations(256, myAllocator), + myShellRelations(256, myAllocator), + mySolidRelations(256, myAllocator), + myCompoundRelations(256, myAllocator), + myCompSolidRelations(256, myAllocator), + myVertexRelations(256, myAllocator), + myProductRelations(64, myAllocator), + myOccurrenceRelations(64, myAllocator), + myNodeToCompounds(1), + myNodeToOccurrences(1), + myEdgeCurves3D(256, myAllocator), + myEdgePolygons3D(256, myAllocator), + myCoEdgeCurves2D(256, myAllocator), + myCoEdgePolygons2D(256, myAllocator), + myCoEdgePolygonsOnTri(256, myAllocator), + myFaceSurfaces(256, myAllocator), + myFaceTriangulations(256, myAllocator), + myUIDToNodeId(1), + myRefUIDToRefId(1), + myTShapeToNodeId(1), + myOriginalShapes(1) { + myAllocator->SetThreadSafe(true); } //================================================================================================= -const NCollection_DynamicArray& BRepGraphInc_Storage::UIDs( - const BRepGraph_NodeId::Kind theKind) const +BRepGraphInc_Storage::~BRepGraphInc_Storage() +{ + Clear(); +} + +//================================================================================================= + +bool BRepGraphInc_Storage::IsEmpty() const +{ + return myVertices.Nb() == 0 && myEdges.Nb() == 0 && myCoEdges.Nb() == 0 && myWires.Nb() == 0 + && myFaces.Nb() == 0 && myShells.Nb() == 0 && mySolids.Nb() == 0 && myCompounds.Nb() == 0 + && myCompSolids.Nb() == 0 && myProducts.Nb() == 0 && myOccurrences.Nb() == 0; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::HasAnyGuard() const +{ + return myVertices.GuardFlags.HasAnyBitSet() || myEdges.GuardFlags.HasAnyBitSet() + || myCoEdges.GuardFlags.HasAnyBitSet() || myWires.GuardFlags.HasAnyBitSet() + || myFaces.GuardFlags.HasAnyBitSet() || myShells.GuardFlags.HasAnyBitSet() + || mySolids.GuardFlags.HasAnyBitSet() || myCompounds.GuardFlags.HasAnyBitSet() + || myCompSolids.GuardFlags.HasAnyBitSet() || myProducts.GuardFlags.HasAnyBitSet() + || myOccurrences.GuardFlags.HasAnyBitSet() || myVertexRefs.GuardFlags.HasAnyBitSet() + || myShellRefs.GuardFlags.HasAnyBitSet() || myFaceRefs.GuardFlags.HasAnyBitSet() + || myWireRefs.GuardFlags.HasAnyBitSet() || mySolidRefs.GuardFlags.HasAnyBitSet() + || myChildRefs.GuardFlags.HasAnyBitSet() || myOccurrenceRefs.GuardFlags.HasAnyBitSet(); +} + +//================================================================================================= + +uint32_t BRepGraphInc_Storage::NextNodeUIDCounter(const BRepGraph_NodeId::Kind theKind) const { switch (theKind) { case BRepGraph_NodeId::Kind::Vertex: - return myVertices.UIDs; + return myVertices.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::Edge: - return myEdges.UIDs; + return myEdges.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::CoEdge: - return myCoEdges.UIDs; + return myCoEdges.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::Wire: - return myWires.UIDs; + return myWires.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::Face: - return myFaces.UIDs; + return myFaces.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::Shell: - return myShells.UIDs; + return myShells.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::Solid: - return mySolids.UIDs; + return mySolids.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::Compound: - return myCompounds.UIDs; + return myCompounds.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::CompSolid: - return myCompSolids.UIDs; + return myCompSolids.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::Product: - return myProducts.UIDs; + return myProducts.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_NodeId::Kind::Occurrence: - return myOccurrences.UIDs; + return myOccurrences.NextUIDCounter.load(std::memory_order_relaxed); default: break; } - Standard_ASSERT_VOID(false, "UIDs: unhandled Kind"); - static const NCollection_DynamicArray THE_EMPTY; - return THE_EMPTY; + Standard_ASSERT_VOID(false, "NextNodeUIDCounter: unhandled Kind"); + return 0; } //================================================================================================= -NCollection_DynamicArray& BRepGraphInc_Storage::ChangeUIDs( - const BRepGraph_NodeId::Kind theKind) +void BRepGraphInc_Storage::SetNextNodeUIDCounter(const BRepGraph_NodeId::Kind theKind, + const uint32_t theCounter) { switch (theKind) { case BRepGraph_NodeId::Kind::Vertex: - return myVertices.UIDs; + myVertices.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::Edge: - return myEdges.UIDs; + myEdges.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::CoEdge: - return myCoEdges.UIDs; + myCoEdges.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::Wire: - return myWires.UIDs; + myWires.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::Face: - return myFaces.UIDs; + myFaces.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::Shell: - return myShells.UIDs; + myShells.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::Solid: - return mySolids.UIDs; + mySolids.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::Compound: - return myCompounds.UIDs; + myCompounds.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::CompSolid: - return myCompSolids.UIDs; + myCompSolids.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::Product: - return myProducts.UIDs; + myProducts.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_NodeId::Kind::Occurrence: - return myOccurrences.UIDs; + myOccurrences.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; + default: + break; } - Standard_ASSERT_RETURN(false, "ChangeUIDs: invalid Kind value", myVertices.UIDs); - return myVertices.UIDs; + Standard_ASSERT_VOID(false, "SetNextNodeUIDCounter: unhandled Kind"); } //================================================================================================= -void BRepGraphInc_Storage::ResetAllUIDs() -{ - myVertices.UIDs.Clear(); - myEdges.UIDs.Clear(); - myCoEdges.UIDs.Clear(); - myWires.UIDs.Clear(); - myFaces.UIDs.Clear(); - myShells.UIDs.Clear(); - mySolids.UIDs.Clear(); - myCompounds.UIDs.Clear(); - myCompSolids.UIDs.Clear(); - myProducts.UIDs.Clear(); - myOccurrences.UIDs.Clear(); -} - -//================================================================================================= - -const NCollection_DynamicArray& BRepGraphInc_Storage::RefUIDs( - const BRepGraph_RefId::Kind theKind) const +uint32_t BRepGraphInc_Storage::NextRefUIDCounter(const BRepGraph_RefId::Kind theKind) const { switch (theKind) { case BRepGraph_RefId::Kind::Shell: - return myShellRefs.UIDs; + return myShellRefs.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_RefId::Kind::Face: - return myFaceRefs.UIDs; + return myFaceRefs.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_RefId::Kind::Wire: - return myWireRefs.UIDs; - case BRepGraph_RefId::Kind::CoEdge: - return myCoEdgeRefs.UIDs; + return myWireRefs.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_RefId::Kind::Vertex: - return myVertexRefs.UIDs; + return myVertexRefs.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_RefId::Kind::Solid: - return mySolidRefs.UIDs; + return mySolidRefs.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_RefId::Kind::Child: - return myChildRefs.UIDs; + return myChildRefs.NextUIDCounter.load(std::memory_order_relaxed); case BRepGraph_RefId::Kind::Occurrence: - return myOccurrenceRefs.UIDs; + return myOccurrenceRefs.NextUIDCounter.load(std::memory_order_relaxed); default: break; } - Standard_ASSERT_VOID(false, "RefUIDs: unhandled Kind"); - static const NCollection_DynamicArray THE_EMPTY; - return THE_EMPTY; + Standard_ASSERT_VOID(false, "NextRefUIDCounter: unhandled Kind"); + return 0; } //================================================================================================= -NCollection_DynamicArray& BRepGraphInc_Storage::ChangeRefUIDs( - const BRepGraph_RefId::Kind theKind) +void BRepGraphInc_Storage::SetNextRefUIDCounter(const BRepGraph_RefId::Kind theKind, + const uint32_t theCounter) { switch (theKind) { case BRepGraph_RefId::Kind::Shell: - return myShellRefs.UIDs; + myShellRefs.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_RefId::Kind::Face: - return myFaceRefs.UIDs; + myFaceRefs.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_RefId::Kind::Wire: - return myWireRefs.UIDs; - case BRepGraph_RefId::Kind::CoEdge: - return myCoEdgeRefs.UIDs; + myWireRefs.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_RefId::Kind::Vertex: - return myVertexRefs.UIDs; + myVertexRefs.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_RefId::Kind::Solid: - return mySolidRefs.UIDs; + mySolidRefs.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_RefId::Kind::Child: - return myChildRefs.UIDs; + myChildRefs.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; case BRepGraph_RefId::Kind::Occurrence: - return myOccurrenceRefs.UIDs; + myOccurrenceRefs.NextUIDCounter.store(theCounter, std::memory_order_relaxed); + return; default: break; } - Standard_ASSERT_RETURN(false, "ChangeRefUIDs: invalid Kind value", myShellRefs.UIDs); - return myShellRefs.UIDs; + Standard_ASSERT_VOID(false, "SetNextRefUIDCounter: unhandled Kind"); } //================================================================================================= -void BRepGraphInc_Storage::ResetAllRefUIDs() +BRepGraph_UID BRepGraphInc_Storage::AllocateNodeUID(const BRepGraph_NodeId theNodeId) { - myShellRefs.UIDs.Clear(); - myFaceRefs.UIDs.Clear(); - myWireRefs.UIDs.Clear(); - myCoEdgeRefs.UIDs.Clear(); - myVertexRefs.UIDs.Clear(); - mySolidRefs.UIDs.Clear(); - myChildRefs.UIDs.Clear(); - myOccurrenceRefs.UIDs.Clear(); + if (!theNodeId.IsValid()) + { + return BRepGraph_UID(); + } + + // Get per-type counter from the appropriate store and advance it. + const uint32_t aCounter = NextNodeUIDCounter(theNodeId.NodeKind); + SetNextNodeUIDCounter(theNodeId.NodeKind, aCounter + 1); + + // Write counter into entity struct via per-kind mutable accessor. + switch (theNodeId.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + ChangeVertex(BRepGraph_VertexId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::Edge: + ChangeEdge(BRepGraph_EdgeId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::CoEdge: + ChangeCoEdge(BRepGraph_CoEdgeId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::Wire: + ChangeWire(BRepGraph_WireId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::Face: + ChangeFace(BRepGraph_FaceId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::Shell: + ChangeShell(BRepGraph_ShellId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::Solid: + ChangeSolid(BRepGraph_SolidId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::Compound: + ChangeCompound(BRepGraph_CompoundId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::CompSolid: + ChangeCompSolid(BRepGraph_CompSolidId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::Product: + ChangeProduct(BRepGraph_ProductId(theNodeId)).UID = aCounter; + break; + case BRepGraph_NodeId::Kind::Occurrence: + ChangeOccurrence(BRepGraph_OccurrenceId(theNodeId)).UID = aCounter; + break; + default: + break; + } + + // Build UID and bind reverse map. + BRepGraph_UID aUID(theNodeId.NodeKind, aCounter); + { + std::unique_lock aLock(myUIDToNodeIdMutex); + myUIDToNodeId.Bind(aUID, theNodeId); + } + return aUID; +} + +//================================================================================================= + +BRepGraph_RefUID BRepGraphInc_Storage::AllocateRefUID(const BRepGraph_RefId theRefId) +{ + if (!theRefId.IsValid()) + { + return BRepGraph_RefUID(); + } + + BRepGraphInc::BaseRef* aRef = ChangeBaseRef(theRefId); + if (aRef == nullptr) + { + return BRepGraph_RefUID(); + } + + // Get per-type counter from the appropriate store and advance it. + const uint32_t aCounter = NextRefUIDCounter(theRefId.RefKind); + SetNextRefUIDCounter(theRefId.RefKind, aCounter + 1); + + aRef->UID = aCounter; + + // Build RefUID and bind reverse map. + BRepGraph_RefUID aUID(theRefId.RefKind, aCounter); + { + std::unique_lock aLock(myRefUIDToRefIdMutex); + myRefUIDToRefId.Bind(aUID, theRefId); + } + return aUID; +} + +//================================================================================================= + +BRepGraph_NodeId BRepGraphInc_Storage::FindNodeIdByUID(const BRepGraph_UID& theUID) const +{ + if (!theUID.IsValid()) + { + return BRepGraph_NodeId(); + } + + EnsureUIDReverseIndex(); + + std::shared_lock aReadLock(myUIDToNodeIdMutex); + const BRepGraph_NodeId* aNodeId = myUIDToNodeId.Seek(theUID); + if (aNodeId == nullptr) + { + return BRepGraph_NodeId(); + } + + const auto aCheck = [this](const auto theTypedId) -> bool { + using TypeId = std::remove_cv_t; + + if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbVertices()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbEdges()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbCoEdges()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbWires()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbFaces()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbShells()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbSolids()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbCompounds()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbCompSolids()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbProducts()) && !IsRemoved(theTypedId); + } + else if constexpr (std::is_same_v) + { + return theTypedId.IsValid(NbOccurrences()) && !IsRemoved(theTypedId); + } + else + { + Standard_ASSERT_RETURN(false, "FindNodeIdByUID: unsupported node id type", false); + } + }; + + return BRepGraph_NodeId::Visit(*aNodeId, aCheck) ? *aNodeId : BRepGraph_NodeId(); +} + +//================================================================================================= + +BRepGraph_RefId BRepGraphInc_Storage::FindRefIdByUID(const BRepGraph_RefUID& theUID) const +{ + if (!theUID.IsValid()) + { + return BRepGraph_RefId(); + } + + EnsureRefUIDReverseIndex(); + + std::shared_lock aReadLock(myRefUIDToRefIdMutex); + const BRepGraph_RefId* aRefId = myRefUIDToRefId.Seek(theUID); + if (aRefId == nullptr) + { + return BRepGraph_RefId(); + } + + const auto aRefCheck = [this](const auto theTypedId) -> bool { return !IsRemoved(theTypedId); }; + return BRepGraph_RefId::Visit(*aRefId, aRefCheck) ? *aRefId : BRepGraph_RefId(); +} + +//================================================================================================= + +void BRepGraphInc_Storage::ClearStorageForReuse() +{ + myRootProductIds.Clear(true); + myVertices.Clear(true); + myEdges.Clear(true); + myCoEdges.Clear(true); + myWires.Clear(true); + myFaces.Clear(true); + myShells.Clear(true); + mySolids.Clear(true); + myCompounds.Clear(true); + myCompSolids.Clear(true); + myProducts.Clear(true); + myOccurrences.Clear(true); + myShellRefs.Clear(true); + myFaceRefs.Clear(true); + myWireRefs.Clear(true); + myVertexRefs.Clear(true); + mySolidRefs.Clear(true); + myChildRefs.Clear(true); + myOccurrenceRefs.Clear(true); + myFaceRelations.Clear(true); + myWireRelations.Clear(true); + myEdgeRelations.Clear(true); + myShellRelations.Clear(true); + mySolidRelations.Clear(true); + myCompoundRelations.Clear(true); + myCompSolidRelations.Clear(true); + myVertexRelations.Clear(true); + myProductRelations.Clear(true); + myOccurrenceRelations.Clear(true); + myEdgeCurves3D.Clear(true); + myEdgePolygons3D.Clear(true); + myCoEdgeCurves2D.Clear(true); + myCoEdgePolygons2D.Clear(true); + myCoEdgePolygonsOnTri.Clear(true); + myFaceSurfaces.Clear(true); + myFaceTriangulations.Clear(true); + myNodeToCompounds.Clear(); + myNodeToOccurrences.Clear(); + myTShapeToNodeId.Clear(); + myOriginalShapes.Clear(); + myAllocator->Reset(false); +} + +//================================================================================================= + +void BRepGraphInc_Storage::ClearRelations() +{ + myFaceRelations.Clear(true); + myWireRelations.Clear(true); + myEdgeRelations.Clear(true); + myShellRelations.Clear(true); + mySolidRelations.Clear(true); + myCompoundRelations.Clear(true); + myCompSolidRelations.Clear(true); + myVertexRelations.Clear(true); + myProductRelations.Clear(true); + myOccurrenceRelations.Clear(true); + myNodeToCompounds.Clear(); + myNodeToOccurrences.Clear(); +} + +//================================================================================================= + +const NCollection_LinearVector& BRepGraphInc_Storage::CompoundRefsOfNode( + const BRepGraph_NodeId theNode) const +{ + if (const NCollection_LinearVector* aRefs = myNodeToCompounds.Seek(theNode)) + { + return *aRefs; + } + return emptyLinearVector(); +} + +//================================================================================================= + +const NCollection_LinearVector& BRepGraphInc_Storage:: + OccurrenceRefsOfNode(const BRepGraph_NodeId theNode) const +{ + if (const NCollection_LinearVector* aRefs = + myNodeToOccurrences.Seek(theNode)) + { + return *aRefs; + } + return emptyLinearVector(); +} + +//================================================================================================= + +bool BRepGraphInc_Storage::HasCompoundParent(const BRepGraph_NodeId theNode) const +{ + if (!theNode.IsValid()) + { + return false; + } + switch (theNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + return HasCompoundParentTyped(BRepGraph_VertexId(theNode)); + case BRepGraph_NodeId::Kind::Edge: + return HasCompoundParentTyped(BRepGraph_EdgeId(theNode)); + case BRepGraph_NodeId::Kind::CoEdge: + return HasCompoundParentTyped(BRepGraph_CoEdgeId(theNode)); + case BRepGraph_NodeId::Kind::Wire: + return HasCompoundParentTyped(BRepGraph_WireId(theNode)); + case BRepGraph_NodeId::Kind::Face: + return HasCompoundParentTyped(BRepGraph_FaceId(theNode)); + case BRepGraph_NodeId::Kind::Shell: + return HasCompoundParentTyped(BRepGraph_ShellId(theNode)); + case BRepGraph_NodeId::Kind::Solid: + return HasCompoundParentTyped(BRepGraph_SolidId(theNode)); + case BRepGraph_NodeId::Kind::Compound: + return HasCompoundParentTyped(BRepGraph_CompoundId(theNode)); + case BRepGraph_NodeId::Kind::CompSolid: + return HasCompoundParentTyped(BRepGraph_CompSolidId(theNode)); + case BRepGraph_NodeId::Kind::Product: + return HasCompoundParentTyped(BRepGraph_ProductId(theNode)); + case BRepGraph_NodeId::Kind::Occurrence: + return HasCompoundParentTyped(BRepGraph_OccurrenceId(theNode)); + } + return false; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::HasOccurrenceParent(const BRepGraph_NodeId theNode) const +{ + if (!theNode.IsValid()) + { + return false; + } + switch (theNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + return HasOccurrenceParentTyped(BRepGraph_VertexId(theNode)); + case BRepGraph_NodeId::Kind::Edge: + return HasOccurrenceParentTyped(BRepGraph_EdgeId(theNode)); + case BRepGraph_NodeId::Kind::CoEdge: + return HasOccurrenceParentTyped(BRepGraph_CoEdgeId(theNode)); + case BRepGraph_NodeId::Kind::Wire: + return HasOccurrenceParentTyped(BRepGraph_WireId(theNode)); + case BRepGraph_NodeId::Kind::Face: + return HasOccurrenceParentTyped(BRepGraph_FaceId(theNode)); + case BRepGraph_NodeId::Kind::Shell: + return HasOccurrenceParentTyped(BRepGraph_ShellId(theNode)); + case BRepGraph_NodeId::Kind::Solid: + return HasOccurrenceParentTyped(BRepGraph_SolidId(theNode)); + case BRepGraph_NodeId::Kind::Compound: + return HasOccurrenceParentTyped(BRepGraph_CompoundId(theNode)); + case BRepGraph_NodeId::Kind::CompSolid: + return HasOccurrenceParentTyped(BRepGraph_CompSolidId(theNode)); + case BRepGraph_NodeId::Kind::Product: + return HasOccurrenceParentTyped(BRepGraph_ProductId(theNode)); + case BRepGraph_NodeId::Kind::Occurrence: + return HasOccurrenceParentTyped(BRepGraph_OccurrenceId(theNode)); + } + return false; +} + +//================================================================================================= + +void BRepGraphInc_Storage::SetHasCompoundParent(const BRepGraph_NodeId theNode, const bool theVal) +{ + if (!theNode.IsValid()) + { + return; + } + switch (theNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + SetHasCompoundParentTyped(BRepGraph_VertexId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Edge: + SetHasCompoundParentTyped(BRepGraph_EdgeId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::CoEdge: + SetHasCompoundParentTyped(BRepGraph_CoEdgeId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Wire: + SetHasCompoundParentTyped(BRepGraph_WireId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Face: + SetHasCompoundParentTyped(BRepGraph_FaceId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Shell: + SetHasCompoundParentTyped(BRepGraph_ShellId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Solid: + SetHasCompoundParentTyped(BRepGraph_SolidId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Compound: + SetHasCompoundParentTyped(BRepGraph_CompoundId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::CompSolid: + SetHasCompoundParentTyped(BRepGraph_CompSolidId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Product: + SetHasCompoundParentTyped(BRepGraph_ProductId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Occurrence: + SetHasCompoundParentTyped(BRepGraph_OccurrenceId(theNode), theVal); + return; + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::SetHasOccurrenceParent(const BRepGraph_NodeId theNode, const bool theVal) +{ + if (!theNode.IsValid()) + { + return; + } + switch (theNode.NodeKind) + { + case BRepGraph_NodeId::Kind::Vertex: + SetHasOccurrenceParentTyped(BRepGraph_VertexId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Edge: + SetHasOccurrenceParentTyped(BRepGraph_EdgeId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::CoEdge: + SetHasOccurrenceParentTyped(BRepGraph_CoEdgeId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Wire: + SetHasOccurrenceParentTyped(BRepGraph_WireId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Face: + SetHasOccurrenceParentTyped(BRepGraph_FaceId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Shell: + SetHasOccurrenceParentTyped(BRepGraph_ShellId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Solid: + SetHasOccurrenceParentTyped(BRepGraph_SolidId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Compound: + SetHasOccurrenceParentTyped(BRepGraph_CompoundId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::CompSolid: + SetHasOccurrenceParentTyped(BRepGraph_CompSolidId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Product: + SetHasOccurrenceParentTyped(BRepGraph_ProductId(theNode), theVal); + return; + case BRepGraph_NodeId::Kind::Occurrence: + SetHasOccurrenceParentTyped(BRepGraph_OccurrenceId(theNode), theVal); + return; + } +} + +//================================================================================================= + +NCollection_LinearVector& BRepGraphInc_Storage:: + ChangeCompoundRefsOfNodeInternal(const BRepGraph_NodeId theNode) +{ + if (!myNodeToCompounds.IsBound(theNode)) + { + myNodeToCompounds.Bind(theNode, NCollection_LinearVector()); + } + return *myNodeToCompounds.ChangeSeek(theNode); +} + +//================================================================================================= + +NCollection_LinearVector& BRepGraphInc_Storage:: + ChangeOccurrenceRefsOfNodeInternal(const BRepGraph_NodeId theNode) +{ + if (!myNodeToOccurrences.IsBound(theNode)) + { + myNodeToOccurrences.Bind(theNode, NCollection_LinearVector()); + } + return *myNodeToOccurrences.ChangeSeek(theNode); +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebuildDerivedRelations() +{ + rebuildDerivedRelationsInternal(true); +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebuildDerivedRelationsPreservingActiveCounts() +{ + rebuildDerivedRelationsInternal(false); +} + +//================================================================================================= + +void BRepGraphInc_Storage::rebuildDerivedRelationsInternal(const bool theRecountActiveCounts) +{ + prepareRelationTable(myVertexRelations, NbVertices()); + prepareRelationTable(myEdgeRelations, NbEdges()); + prepareRelationTable(myWireRelations, NbWires()); + prepareRelationTable(myFaceRelations, NbFaces()); + prepareRelationTable(myShellRelations, NbShells()); + prepareRelationTable(mySolidRelations, NbSolids()); + prepareRelationTable(myCompoundRelations, NbCompounds()); + prepareRelationTable(myCompSolidRelations, NbCompSolids()); + prepareRelationTable(myProductRelations, NbProducts()); + prepareRelationTable(myOccurrenceRelations, NbOccurrences()); + + for (BRepGraph_VertexId aVertexId(0); aVertexId.IsValid(NbVertices()); ++aVertexId) + { + ChangeVertexRelationsInternal(aVertexId).EdgeIds.Clear(); + } + for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(NbEdges()); ++anEdgeId) + { + BRepGraphInc::EdgeRelations& anEdgeRel = ChangeEdgeRelationsInternal(anEdgeId); + anEdgeRel.CoEdgeIds.Clear(); + } + for (BRepGraph_WireId aWireId(0); aWireId.IsValid(NbWires()); ++aWireId) + { + ChangeWireRelationsInternal(aWireId).ParentWireRefIds.Clear(); + } + for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(NbFaces()); ++aFaceId) + { + ChangeFaceRelationsInternal(aFaceId).ParentFaceRefIds.Clear(); + } + for (BRepGraph_ShellId aShellId(0); aShellId.IsValid(NbShells()); ++aShellId) + { + ChangeShellRelationsInternal(aShellId).ParentShellRefIds.Clear(); + } + for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(NbSolids()); ++aSolidId) + { + ChangeSolidRelationsInternal(aSolidId).ParentSolidRefIds.Clear(); + } + for (BRepGraph_OccurrenceId anOccurrenceId(0); anOccurrenceId.IsValid(NbOccurrences()); + ++anOccurrenceId) + { + ChangeOccurrenceRelationsInternal(anOccurrenceId).ParentOccurrenceRefIds.Clear(); + } + myNodeToCompounds.Clear(); + myNodeToOccurrences.Clear(); + + // Clear the compound/occurrence parent bitsets for all node kinds. + myVertices.HasCompoundParentFlags.ClearAll(); + myVertices.HasOccurrenceParentFlags.ClearAll(); + myEdges.HasCompoundParentFlags.ClearAll(); + myEdges.HasOccurrenceParentFlags.ClearAll(); + myCoEdges.HasCompoundParentFlags.ClearAll(); + myCoEdges.HasOccurrenceParentFlags.ClearAll(); + myWires.HasCompoundParentFlags.ClearAll(); + myWires.HasOccurrenceParentFlags.ClearAll(); + myFaces.HasCompoundParentFlags.ClearAll(); + myFaces.HasOccurrenceParentFlags.ClearAll(); + myShells.HasCompoundParentFlags.ClearAll(); + myShells.HasOccurrenceParentFlags.ClearAll(); + mySolids.HasCompoundParentFlags.ClearAll(); + mySolids.HasOccurrenceParentFlags.ClearAll(); + myCompounds.HasCompoundParentFlags.ClearAll(); + myCompounds.HasOccurrenceParentFlags.ClearAll(); + myCompSolids.HasCompoundParentFlags.ClearAll(); + myCompSolids.HasOccurrenceParentFlags.ClearAll(); + myProducts.HasCompoundParentFlags.ClearAll(); + myProducts.HasOccurrenceParentFlags.ClearAll(); + myOccurrences.HasCompoundParentFlags.ClearAll(); + myOccurrences.HasOccurrenceParentFlags.ClearAll(); + + for (BRepGraph_WireId aWireId(0); aWireId.IsValid(NbWires()); ++aWireId) + { + pruneRelationIds(ChangeWireRelationsInternal(aWireId).CoEdgeIds, + [&](const BRepGraph_CoEdgeId theCoEdgeId) { + return !IsRemoved(aWireId) && theCoEdgeId.IsValid(NbCoEdges()) + && !IsRemoved(theCoEdgeId) + && CoEdge(theCoEdgeId).ParentWireId == aWireId; + }); + } + for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(NbFaces()); ++aFaceId) + { + pruneRelationIds(ChangeFaceRelationsInternal(aFaceId).WireRefIds, + [&](const BRepGraph_WireRefId theRefId) { + return !IsRemoved(aFaceId) && theRefId.IsValid(NbWireRefs()) + && !IsRemoved(theRefId) && WireRef(theRefId).ParentFaceId == aFaceId; + }); + } + for (BRepGraph_ShellId aShellId(0); aShellId.IsValid(NbShells()); ++aShellId) + { + pruneRelationIds(ChangeShellRelationsInternal(aShellId).FaceRefIds, + [&](const BRepGraph_FaceRefId theRefId) { + return !IsRemoved(aShellId) && theRefId.IsValid(NbFaceRefs()) + && !IsRemoved(theRefId) + && FaceRef(theRefId).ParentShellId == aShellId; + }); + } + for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(NbSolids()); ++aSolidId) + { + pruneRelationIds(ChangeSolidRelationsInternal(aSolidId).ShellRefIds, + [&](const BRepGraph_ShellRefId theRefId) { + return !IsRemoved(aSolidId) && theRefId.IsValid(NbShellRefs()) + && !IsRemoved(theRefId) + && ShellRef(theRefId).ParentSolidId == aSolidId; + }); + } + for (BRepGraph_CompSolidId aCompSolidId(0); aCompSolidId.IsValid(NbCompSolids()); ++aCompSolidId) + { + pruneRelationIds(ChangeCompSolidRelationsInternal(aCompSolidId).SolidRefIds, + [&](const BRepGraph_SolidRefId theRefId) { + return !IsRemoved(aCompSolidId) && theRefId.IsValid(NbSolidRefs()) + && !IsRemoved(theRefId) + && SolidRef(theRefId).ParentCompSolidId == aCompSolidId; + }); + } + for (BRepGraph_CompoundId aCompoundId(0); aCompoundId.IsValid(NbCompounds()); ++aCompoundId) + { + pruneRelationIds(ChangeCompoundRelationsInternal(aCompoundId).ChildRefIds, + [&](const BRepGraph_ChildRefId theRefId) { + return !IsRemoved(aCompoundId) && theRefId.IsValid(NbChildRefs()) + && !IsRemoved(theRefId) + && ChildRef(theRefId).ParentCompoundId == aCompoundId; + }); + } + for (BRepGraph_ProductId aProductId(0); aProductId.IsValid(NbProducts()); ++aProductId) + { + pruneRelationIds(ChangeProductRelationsInternal(aProductId).OccurrenceRefIds, + [&](const BRepGraph_OccurrenceRefId theRefId) { + return !IsRemoved(aProductId) && theRefId.IsValid(NbOccurrenceRefs()) + && !IsRemoved(theRefId) + && OccurrenceRef(theRefId).ParentProductId == aProductId; + }); + } + + for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(NbEdges()); ++anEdgeId) + { + if (IsRemoved(anEdgeId)) + { + continue; + } + const BRepGraphInc::EdgeDef& anEdge = Edge(anEdgeId); + const BRepGraph_VertexRefId aStartRefId = anEdge.StartVertexRefId; + if (aStartRefId.IsValid(NbVertexRefs()) && !IsRemoved(aStartRefId)) + { + const BRepGraph_VertexId aVertexId = VertexRef(aStartRefId).ChildVertexId; + if (aVertexId.IsValid(NbVertices()) && !IsRemoved(aVertexId)) + { + appendUniqueRelationId(ChangeVertexRelationsInternal(aVertexId).EdgeIds, anEdgeId); + } + } + + const BRepGraph_VertexRefId anEndRefId = anEdge.EndVertexRefId; + if (anEndRefId.IsValid(NbVertexRefs()) && !IsRemoved(anEndRefId)) + { + const BRepGraph_VertexId aVertexId = VertexRef(anEndRefId).ChildVertexId; + if (aVertexId.IsValid(NbVertices()) && !IsRemoved(aVertexId)) + { + appendUniqueRelationId(ChangeVertexRelationsInternal(aVertexId).EdgeIds, anEdgeId); + } + } + } + + for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(NbCoEdges()); ++aCoEdgeId) + { + if (IsRemoved(aCoEdgeId)) + { + continue; + } + const BRepGraphInc::CoEdgeDef& aCoEdge = CoEdge(aCoEdgeId); + if (aCoEdge.ChildEdgeId.IsValid(NbEdges()) && !IsRemoved(aCoEdge.ChildEdgeId)) + { + BRepGraphInc::EdgeRelations& anEdgeRel = ChangeEdgeRelationsInternal(aCoEdge.ChildEdgeId); + appendUniqueRelationId(anEdgeRel.CoEdgeIds, aCoEdgeId); + } + } + + for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(NbFaces()); ++aFaceId) + { + if (IsRemoved(aFaceId)) + { + continue; + } + const BRepGraphInc::FaceRelations& aFaceRel = FaceRelations(aFaceId); + for (const BRepGraph_WireRefId& aWireRefId : aFaceRel.WireRefIds) + { + if (!aWireRefId.IsValid(NbWireRefs()) || IsRemoved(aWireRefId)) + { + continue; + } + const BRepGraph_WireId aWireId = WireRef(aWireRefId).ChildWireId; + if (!aWireId.IsValid(NbWires()) || IsRemoved(aWireId)) + { + continue; + } + appendUniqueRelationId(ChangeWireRelationsInternal(aWireId).ParentWireRefIds, aWireRefId); + } + } + + for (BRepGraph_ShellId aShellId(0); aShellId.IsValid(NbShells()); ++aShellId) + { + if (IsRemoved(aShellId)) + { + continue; + } + const BRepGraphInc::ShellRelations& aShellRel = ShellRelations(aShellId); + for (const BRepGraph_FaceRefId& aFaceRefId : aShellRel.FaceRefIds) + { + if (!aFaceRefId.IsValid(NbFaceRefs()) || IsRemoved(aFaceRefId)) + { + continue; + } + const BRepGraph_FaceId aFaceId = FaceRef(aFaceRefId).ChildFaceId; + if (!aFaceId.IsValid(NbFaces()) || IsRemoved(aFaceId)) + { + continue; + } + appendUniqueRelationId(ChangeFaceRelationsInternal(aFaceId).ParentFaceRefIds, aFaceRefId); + } + } + + for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(NbSolids()); ++aSolidId) + { + if (IsRemoved(aSolidId)) + { + continue; + } + const BRepGraphInc::SolidRelations& aSolidRel = SolidRelations(aSolidId); + for (const BRepGraph_ShellRefId& aShellRefId : aSolidRel.ShellRefIds) + { + if (!aShellRefId.IsValid(NbShellRefs()) || IsRemoved(aShellRefId)) + { + continue; + } + const BRepGraph_ShellId aShellId = ShellRef(aShellRefId).ChildShellId; + if (!aShellId.IsValid(NbShells()) || IsRemoved(aShellId)) + { + continue; + } + appendUniqueRelationId(ChangeShellRelationsInternal(aShellId).ParentShellRefIds, aShellRefId); + } + } + + for (BRepGraph_CompoundId aCompoundId(0); aCompoundId.IsValid(NbCompounds()); ++aCompoundId) + { + if (IsRemoved(aCompoundId)) + { + continue; + } + const BRepGraphInc::CompoundRelations& aCompoundRel = CompoundRelations(aCompoundId); + for (const BRepGraph_ChildRefId& aChildRefId : aCompoundRel.ChildRefIds) + { + if (!aChildRefId.IsValid(NbChildRefs()) || IsRemoved(aChildRefId)) + { + continue; + } + const BRepGraph_NodeId aChildNode = ChildRef(aChildRefId).ChildNodeId; + if (aChildNode.IsValid() && !isNodeRemoved(*this, aChildNode)) + { + appendUniqueRelationId(ChangeCompoundRefsOfNodeInternal(aChildNode), aChildRefId); + } + } + } + + for (BRepGraph_CompSolidId aCompSolidId(0); aCompSolidId.IsValid(NbCompSolids()); ++aCompSolidId) + { + if (IsRemoved(aCompSolidId)) + { + continue; + } + const BRepGraphInc::CompSolidRelations& aCompSolidRel = CompSolidRelations(aCompSolidId); + for (const BRepGraph_SolidRefId& aSolidRefId : aCompSolidRel.SolidRefIds) + { + if (!aSolidRefId.IsValid(NbSolidRefs()) || IsRemoved(aSolidRefId)) + { + continue; + } + const BRepGraph_SolidId aSolidId = SolidRef(aSolidRefId).ChildSolidId; + if (!aSolidId.IsValid(NbSolids()) || IsRemoved(aSolidId)) + { + continue; + } + appendUniqueRelationId(ChangeSolidRelationsInternal(aSolidId).ParentSolidRefIds, aSolidRefId); + } + } + + for (BRepGraph_ProductId aProductId(0); aProductId.IsValid(NbProducts()); ++aProductId) + { + if (IsRemoved(aProductId)) + { + continue; + } + const BRepGraphInc::ProductRelations& aProductRel = ProductRelations(aProductId); + for (const BRepGraph_OccurrenceRefId& anOccurrenceRefId : aProductRel.OccurrenceRefIds) + { + if (!anOccurrenceRefId.IsValid(NbOccurrenceRefs()) || IsRemoved(anOccurrenceRefId)) + { + continue; + } + const BRepGraph_OccurrenceId anOccurrenceId = + OccurrenceRef(anOccurrenceRefId).ChildOccurrenceId; + if (!anOccurrenceId.IsValid(NbOccurrences()) || IsRemoved(anOccurrenceId)) + { + continue; + } + appendUniqueRelationId( + ChangeOccurrenceRelationsInternal(anOccurrenceId).ParentOccurrenceRefIds, + anOccurrenceRefId); + const BRepGraph_NodeId aChildNode = Occurrence(anOccurrenceId).ChildNodeId; + if (aChildNode.IsValid() && !isNodeRemoved(*this, aChildNode)) + { + appendUniqueRelationId(ChangeOccurrenceRefsOfNodeInternal(aChildNode), anOccurrenceRefId); + } + } + } + + // Rebuild compound/occurrence parent bitsets from the populated maps. + for (NCollection_DataMap>::Iterator + anIt(myNodeToCompounds); + anIt.More(); + anIt.Next()) + { + if (!anIt.Value().IsEmpty()) + { + SetHasCompoundParent(anIt.Key(), true); + } + } + for (NCollection_DataMap>::Iterator + anIt(myNodeToOccurrences); + anIt.More(); + anIt.Next()) + { + if (!anIt.Value().IsEmpty()) + { + SetHasOccurrenceParent(anIt.Key(), true); + } + } + + if (theRecountActiveCounts) + { + RecountActiveCounts(); + } +} + +//================================================================================================= + +bool BRepGraphInc_Storage::ValidateRelations() const +{ + for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(NbCoEdges()); ++aCoEdgeId) + { + if (IsRemoved(aCoEdgeId)) + { + continue; + } + const BRepGraphInc::CoEdgeDef& aCoEdge = CoEdge(aCoEdgeId); + const bool hasActiveFaceContext = + aCoEdge.FaceId.IsValid(NbFaces()) && !IsRemoved(aCoEdge.FaceId); + if (aCoEdge.Curve2DRepId.IsValid()) + { + if (!aCoEdge.Curve2DRepId.IsValid(NbCoEdgeCurves2D())) + { + return false; + } + if (!IsRemoved(aCoEdge.Curve2DRepId) + && (!hasActiveFaceContext + || CoEdgeCurve2DRep(aCoEdge.Curve2DRepId).ParentCoEdgeId != aCoEdgeId)) + { + return false; + } + } + if (aCoEdge.Polygon2DRepId.IsValid()) + { + if (!aCoEdge.Polygon2DRepId.IsValid(NbCoEdgePolygons2D())) + { + return false; + } + if (!IsRemoved(aCoEdge.Polygon2DRepId) + && (!hasActiveFaceContext + || CoEdgePolygon2DRep(aCoEdge.Polygon2DRepId).ParentCoEdgeId != aCoEdgeId)) + { + return false; + } + } + if (aCoEdge.PolygonOnTriRepId.IsValid()) + { + if (!aCoEdge.PolygonOnTriRepId.IsValid(NbCoEdgePolygonsOnTri())) + { + return false; + } + if (!IsRemoved(aCoEdge.PolygonOnTriRepId) + && (!hasActiveFaceContext + || CoEdgePolygonOnTriRep(aCoEdge.PolygonOnTriRepId).ParentCoEdgeId != aCoEdgeId)) + { + return false; + } + } + if (aCoEdge.ParentWireId.IsValid(NbWires())) + { + if (!containsRelationId(WireRelations(aCoEdge.ParentWireId).CoEdgeIds, aCoEdgeId)) + { + return false; + } + } + if (aCoEdge.ChildEdgeId.IsValid(NbEdges())) + { + const BRepGraphInc::EdgeRelations& anEdgeRel = EdgeRelations(aCoEdge.ChildEdgeId); + if (!containsRelationId(anEdgeRel.CoEdgeIds, aCoEdgeId)) + { + return false; + } + if (aCoEdge.ParentWireId.IsValid(NbWires()) && aCoEdge.FaceId.IsValid(NbFaces())) + { + bool hasFaceWireUse = false; + for (const BRepGraph_WireRefId& aWireRefId : FaceRelations(aCoEdge.FaceId).WireRefIds) + { + if (aWireRefId.IsValid(NbWireRefs()) && !IsRemoved(aWireRefId)) + { + const BRepGraphInc::WireRef& aRef = WireRef(aWireRefId); + if (aRef.ParentFaceId == aCoEdge.FaceId && aRef.ChildWireId == aCoEdge.ParentWireId) + { + hasFaceWireUse = true; + break; + } + } + } + if (!hasFaceWireUse) + { + return false; + } + } + } + } + + for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(NbEdges()); ++anEdgeId) + { + if (IsRemoved(anEdgeId)) + { + continue; + } + const BRepGraphInc::EdgeRelations& anEdgeRel = EdgeRelations(anEdgeId); + for (const BRepGraph_CoEdgeId& aCoEdgeId : anEdgeRel.CoEdgeIds) + { + if (!aCoEdgeId.IsValid(NbCoEdges()) || IsRemoved(aCoEdgeId) + || CoEdge(aCoEdgeId).ChildEdgeId != anEdgeId) + { + return false; + } + } + const BRepGraphInc::EdgeDef& anEdge = Edge(anEdgeId); + const auto aCheckVertex = [&](const BRepGraph_VertexRefId theRefId) { + if (!theRefId.IsValid(NbVertexRefs()) || IsRemoved(theRefId)) + { + return true; + } + const BRepGraph_VertexId aVertexId = VertexRef(theRefId).ChildVertexId; + return !aVertexId.IsValid(NbVertices()) || IsRemoved(aVertexId) + || containsRelationId(VertexRelations(aVertexId).EdgeIds, anEdgeId); + }; + if (!aCheckVertex(anEdge.StartVertexRefId) || !aCheckVertex(anEdge.EndVertexRefId)) + { + return false; + } + } + + for (BRepGraph_WireId aWireId(0); aWireId.IsValid(NbWires()); ++aWireId) + { + if (IsRemoved(aWireId)) + { + continue; + } + for (const BRepGraph_CoEdgeId& aCoEdgeId : WireRelations(aWireId).CoEdgeIds) + { + if (!aCoEdgeId.IsValid(NbCoEdges()) || IsRemoved(aCoEdgeId) + || CoEdge(aCoEdgeId).ParentWireId != aWireId) + { + return false; + } + } + } + + for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(NbFaces()); ++aFaceId) + { + if (IsRemoved(aFaceId)) + { + continue; + } + for (const BRepGraph_WireRefId& aWireRefId : FaceRelations(aFaceId).WireRefIds) + { + if (!aWireRefId.IsValid(NbWireRefs()) || IsRemoved(aWireRefId)) + { + return false; + } + const BRepGraphInc::WireRef& aRef = WireRef(aWireRefId); + if (aRef.ParentFaceId != aFaceId || !aRef.ChildWireId.IsValid(NbWires()) + || IsRemoved(aRef.ChildWireId) + || !containsRelationId(WireRelations(aRef.ChildWireId).ParentWireRefIds, aWireRefId)) + { + return false; + } + } + } + + for (BRepGraph_ShellId aShellId(0); aShellId.IsValid(NbShells()); ++aShellId) + { + if (IsRemoved(aShellId)) + { + continue; + } + for (const BRepGraph_FaceRefId& aFaceRefId : ShellRelations(aShellId).FaceRefIds) + { + if (!aFaceRefId.IsValid(NbFaceRefs()) || IsRemoved(aFaceRefId)) + { + return false; + } + const BRepGraphInc::FaceRef& aRef = FaceRef(aFaceRefId); + if (aRef.ParentShellId != aShellId || !aRef.ChildFaceId.IsValid(NbFaces()) + || IsRemoved(aRef.ChildFaceId) + || !containsRelationId(FaceRelations(aRef.ChildFaceId).ParentFaceRefIds, aFaceRefId)) + { + return false; + } + } + } + + for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(NbSolids()); ++aSolidId) + { + if (IsRemoved(aSolidId)) + { + continue; + } + for (const BRepGraph_ShellRefId& aShellRefId : SolidRelations(aSolidId).ShellRefIds) + { + if (!aShellRefId.IsValid(NbShellRefs()) || IsRemoved(aShellRefId)) + { + return false; + } + const BRepGraphInc::ShellRef& aRef = ShellRef(aShellRefId); + if (aRef.ParentSolidId != aSolidId || !aRef.ChildShellId.IsValid(NbShells()) + || IsRemoved(aRef.ChildShellId) + || !containsRelationId(ShellRelations(aRef.ChildShellId).ParentShellRefIds, aShellRefId)) + { + return false; + } + } + } + + for (BRepGraph_CompSolidId aCompSolidId(0); aCompSolidId.IsValid(NbCompSolids()); ++aCompSolidId) + { + if (IsRemoved(aCompSolidId)) + { + continue; + } + for (const BRepGraph_SolidRefId& aSolidRefId : CompSolidRelations(aCompSolidId).SolidRefIds) + { + if (!aSolidRefId.IsValid(NbSolidRefs()) || IsRemoved(aSolidRefId)) + { + return false; + } + const BRepGraphInc::SolidRef& aRef = SolidRef(aSolidRefId); + if (aRef.ParentCompSolidId != aCompSolidId || !aRef.ChildSolidId.IsValid(NbSolids()) + || IsRemoved(aRef.ChildSolidId) + || !containsRelationId(SolidRelations(aRef.ChildSolidId).ParentSolidRefIds, aSolidRefId)) + { + return false; + } + } + } + + for (BRepGraph_CompoundId aCompoundId(0); aCompoundId.IsValid(NbCompounds()); ++aCompoundId) + { + if (IsRemoved(aCompoundId)) + { + continue; + } + for (const BRepGraph_ChildRefId& aChildRefId : CompoundRelations(aCompoundId).ChildRefIds) + { + if (!aChildRefId.IsValid(NbChildRefs()) || IsRemoved(aChildRefId) + || ChildRef(aChildRefId).ParentCompoundId != aCompoundId) + { + return false; + } + const BRepGraph_NodeId aChildNode = ChildRef(aChildRefId).ChildNodeId; + if (aChildNode.IsValid() && !isNodeRemoved(*this, aChildNode) + && !containsRelationId(CompoundRefsOfNode(aChildNode), aChildRefId)) + { + return false; + } + } + } + + for (BRepGraph_ProductId aProductId(0); aProductId.IsValid(NbProducts()); ++aProductId) + { + if (IsRemoved(aProductId)) + { + continue; + } + for (const BRepGraph_OccurrenceRefId& aRefId : ProductRelations(aProductId).OccurrenceRefIds) + { + if (!aRefId.IsValid(NbOccurrenceRefs()) || IsRemoved(aRefId) + || OccurrenceRef(aRefId).ParentProductId != aProductId) + { + return false; + } + const BRepGraph_OccurrenceId anOccurrenceId = OccurrenceRef(aRefId).ChildOccurrenceId; + if (anOccurrenceId.IsValid(NbOccurrences()) && !IsRemoved(anOccurrenceId)) + { + if (!containsRelationId(OccurrenceRelations(anOccurrenceId).ParentOccurrenceRefIds, aRefId)) + { + return false; + } + const BRepGraph_NodeId aChildNode = Occurrence(anOccurrenceId).ChildNodeId; + if (aChildNode.IsValid() && !isNodeRemoved(*this, aChildNode) + && !containsRelationId(OccurrenceRefsOfNode(aChildNode), aRefId)) + { + return false; + } + } + } + } + + for (BRepGraph_OccurrenceId anOccurrenceId(0); anOccurrenceId.IsValid(NbOccurrences()); + ++anOccurrenceId) + { + if (IsRemoved(anOccurrenceId)) + { + continue; + } + for (const BRepGraph_OccurrenceRefId& aRefId : + OccurrenceRelations(anOccurrenceId).ParentOccurrenceRefIds) + { + if (!aRefId.IsValid(NbOccurrenceRefs()) || IsRemoved(aRefId) + || OccurrenceRef(aRefId).ChildOccurrenceId != anOccurrenceId) + { + return false; + } + const BRepGraph_ProductId aProductId = OccurrenceRef(aRefId).ParentProductId; + if (!aProductId.IsValid(NbProducts()) || IsRemoved(aProductId) + || !containsRelationId(ProductRelations(aProductId).OccurrenceRefIds, aRefId)) + { + return false; + } + } + } + + return true; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::ValidateWireCoEdgeOrder(const BRepGraph_WireId theWireId) const +{ + if (!theWireId.IsValid(NbWires()) || IsRemoved(theWireId)) + { + return false; + } + + const BRepGraphInc::WireRelations& aWireRel = WireRelations(theWireId); + if (!coEdgeIdsAreUnique(aWireRel.CoEdgeIds)) + { + return false; + } + + BRepGraph_VertexId aPrevEnd; + bool hasPrev = false; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aWireRel.CoEdgeIds) + { + if (!aCoEdgeId.IsValid(NbCoEdges()) || IsRemoved(aCoEdgeId) + || CoEdge(aCoEdgeId).ParentWireId != theWireId) + { + return false; + } + + BRepGraph_VertexId aStart; + BRepGraph_VertexId anEnd; + if (!coEdgeOrientedVertices(*this, aCoEdgeId, aStart, anEnd)) + { + return false; + } + if (hasPrev && aStart != aPrevEnd) + { + return false; + } + + aPrevEnd = anEnd; + hasPrev = true; + } + return true; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::ValidateWireCoEdgeOrders() const +{ + for (BRepGraph_WireId aWireId(0); aWireId.IsValid(NbWires()); ++aWireId) + { + if (!IsRemoved(aWireId) && !ValidateWireCoEdgeOrder(aWireId)) + { + return false; + } + } + return true; +} + +//================================================================================================= + +BRepGraphInc_Storage::WireCoEdgeOrderStatus BRepGraphInc_Storage::CanonicalizeWireCoEdgeOrderStatus( + const BRepGraph_WireId theWireId) +{ + using Status = BRepGraphInc_Storage::WireCoEdgeOrderStatus; + if (!theWireId.IsValid(NbWires()) || IsRemoved(theWireId)) + { + return Status::InvalidInput; + } + const NCollection_LinearVector& aCurrent = WireRelations(theWireId).CoEdgeIds; + const NCollection_Array1 aCurrentView = aCurrent.ToArray1(); + NCollection_LinearVector anOrdered; + const Status aStatus = + BRepGraphInc_WireOrder::BuildCoEdgeOrder(*this, theWireId, aCurrentView, anOrdered); + if (aStatus == Status::Reordered || aStatus == Status::ToleranceOrdered + || aStatus == Status::Partial) + { + SetWireCoEdges(theWireId, anOrdered.ToArray1()); + } + return aStatus; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::CanonicalizeWireCoEdgeOrder(const BRepGraph_WireId theWireId) +{ + return CanonicalizeWireCoEdgeOrderStatus(theWireId) + != BRepGraphInc_Storage::WireCoEdgeOrderStatus::InvalidInput; +} + +//================================================================================================= + +BRepGraph_CoEdgeId BRepGraphInc_Storage::CreateCoEdgeUse( + const BRepGraph_WireId theParentWireId, + const BRepGraph_EdgeId theChildEdgeId, + const BRepGraph_FaceId theFaceId, + const BRepGraphInc::ParityOrientation theOrientation) +{ + const BRepGraph_CoEdgeId aCoEdgeId = AppendCoEdge(); + BRepGraphInc::CoEdgeDef& aCoEdge = ChangeCoEdge(aCoEdgeId); + aCoEdge.ParentWireId = theParentWireId; + aCoEdge.ChildEdgeId = theChildEdgeId; + aCoEdge.FaceId = theFaceId; + aCoEdge.Orientation = theOrientation; + + ChangeWireRelationsInternal(theParentWireId).CoEdgeIds.Append(aCoEdgeId); + BRepGraphInc::EdgeRelations& anEdgeRel = ChangeEdgeRelationsInternal(theChildEdgeId); + appendUniqueRelationId(anEdgeRel.CoEdgeIds, aCoEdgeId); + return aCoEdgeId; +} + +//================================================================================================= + +void BRepGraphInc_Storage::AttachEdgeToVertex(const BRepGraph_EdgeId theEdgeId, + const BRepGraph_VertexId theVertexId) +{ + if (!theEdgeId.IsValid(NbEdges()) || !theVertexId.IsValid(NbVertices())) + { + return; + } + appendUniqueRelationId(ChangeVertexRelationsInternal(theVertexId).EdgeIds, theEdgeId); +} + +//================================================================================================= + +BRepGraph_WireRefId BRepGraphInc_Storage::AttachWireToFace( + const BRepGraph_FaceId theParentFaceId, + const BRepGraph_WireId theChildWireId, + const BRepGraphInc::ParityOrientation theOrientation) +{ + const BRepGraph_WireRefId aRefId = AppendWireRef(); + BRepGraphInc::WireRef& aRef = ChangeWireRef(aRefId); + aRef.ParentFaceId = theParentFaceId; + aRef.ChildWireId = theChildWireId; + aRef.Orientation = theOrientation; + ChangeFaceRelationsInternal(theParentFaceId).WireRefIds.Append(aRefId); + appendUniqueRelationId(ChangeWireRelationsInternal(theChildWireId).ParentWireRefIds, aRefId); + return aRefId; +} + +//================================================================================================= + +BRepGraph_FaceRefId BRepGraphInc_Storage::AttachFaceToShell( + const BRepGraph_ShellId theParentShellId, + const BRepGraph_FaceId theChildFaceId, + const BRepGraphInc::ParityOrientation theOrientation) +{ + const BRepGraph_FaceRefId aRefId = AppendFaceRef(); + BRepGraphInc::FaceRef& aRef = ChangeFaceRef(aRefId); + aRef.ParentShellId = theParentShellId; + aRef.ChildFaceId = theChildFaceId; + aRef.Orientation = theOrientation; + ChangeShellRelationsInternal(theParentShellId).FaceRefIds.Append(aRefId); + appendUniqueRelationId(ChangeFaceRelationsInternal(theChildFaceId).ParentFaceRefIds, aRefId); + return aRefId; +} + +//================================================================================================= + +BRepGraph_ShellRefId BRepGraphInc_Storage::AttachShellToSolid( + const BRepGraph_SolidId theParentSolidId, + const BRepGraph_ShellId theChildShellId, + const BRepGraphInc::ParityOrientation theOrientation) +{ + const BRepGraph_ShellRefId aRefId = AppendShellRef(); + BRepGraphInc::ShellRef& aRef = ChangeShellRef(aRefId); + aRef.ParentSolidId = theParentSolidId; + aRef.ChildShellId = theChildShellId; + aRef.Orientation = theOrientation; + ChangeSolidRelationsInternal(theParentSolidId).ShellRefIds.Append(aRefId); + appendUniqueRelationId(ChangeShellRelationsInternal(theChildShellId).ParentShellRefIds, aRefId); + return aRefId; +} + +//================================================================================================= + +BRepGraph_SolidRefId BRepGraphInc_Storage::AttachSolidToCompSolid( + const BRepGraph_CompSolidId theParentCompSolidId, + const BRepGraph_SolidId theChildSolidId, + const BRepGraphInc::ParityOrientation theOrientation) +{ + const BRepGraph_SolidRefId aRefId = AppendSolidRef(); + BRepGraphInc::SolidRef& aRef = ChangeSolidRef(aRefId); + aRef.ParentCompSolidId = theParentCompSolidId; + aRef.ChildSolidId = theChildSolidId; + aRef.Orientation = theOrientation; + ChangeCompSolidRelationsInternal(theParentCompSolidId).SolidRefIds.Append(aRefId); + appendUniqueRelationId(ChangeSolidRelationsInternal(theChildSolidId).ParentSolidRefIds, aRefId); + return aRefId; +} + +//================================================================================================= + +BRepGraph_ChildRefId BRepGraphInc_Storage::AttachChildToCompound( + const BRepGraph_CompoundId theParentCompoundId, + const BRepGraph_NodeId theChildNodeId, + const TopLoc_Location& theLocation, + const BRepGraphInc::ParityOrientation theOrientation) +{ + const BRepGraph_ChildRefId aRefId = AppendChildRef(); + BRepGraphInc::ChildRef& aRef = ChangeChildRef(aRefId); + aRef.ParentCompoundId = theParentCompoundId; + aRef.ChildNodeId = theChildNodeId; + aRef.LocalLocation = theLocation; + aRef.Orientation = theOrientation; + ChangeCompoundRelationsInternal(theParentCompoundId).ChildRefIds.Append(aRefId); + if (theChildNodeId.IsValid()) + { + appendUniqueRelationId(ChangeCompoundRefsOfNodeInternal(theChildNodeId), aRefId); + SetHasCompoundParent(theChildNodeId, true); + } + return aRefId; +} + +//================================================================================================= + +BRepGraph_OccurrenceRefId BRepGraphInc_Storage::AttachOccurrenceToProduct( + const BRepGraph_ProductId theParentProductId, + const BRepGraph_OccurrenceId theChildOccurrenceId, + const TopLoc_Location& theLocation) +{ + const BRepGraph_OccurrenceRefId aRefId = AppendOccurrenceRef(); + BRepGraphInc::OccurrenceRef& aRef = ChangeOccurrenceRef(aRefId); + aRef.ParentProductId = theParentProductId; + aRef.ChildOccurrenceId = theChildOccurrenceId; + aRef.LocalLocation = theLocation; + ChangeProductRelationsInternal(theParentProductId).OccurrenceRefIds.Append(aRefId); + if (theChildOccurrenceId.IsValid(NbOccurrences())) + { + appendUniqueRelationId( + ChangeOccurrenceRelationsInternal(theChildOccurrenceId).ParentOccurrenceRefIds, + aRefId); + const BRepGraph_NodeId aChildNode = Occurrence(theChildOccurrenceId).ChildNodeId; + if (aChildNode.IsValid()) + { + appendUniqueRelationId(ChangeOccurrenceRefsOfNodeInternal(aChildNode), aRefId); + SetHasOccurrenceParent(aChildNode, true); + } + } + return aRefId; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::DetachCoEdgeUse(const BRepGraph_WireId theParentWireId, + const BRepGraph_CoEdgeId theCoEdgeId) +{ + if (!theParentWireId.IsValid(NbWires()) || !theCoEdgeId.IsValid(NbCoEdges())) + { + return false; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = CoEdge(theCoEdgeId); + const BRepGraph_EdgeId anEdgeId = aCoEdge.ChildEdgeId; + const bool isErased = + eraseRelationId(ChangeWireRelationsInternal(theParentWireId).CoEdgeIds, theCoEdgeId); + if (!isErased) + { + return false; + } + + if (anEdgeId.IsValid(NbEdges())) + { + BRepGraphInc::EdgeRelations& anEdgeRel = ChangeEdgeRelationsInternal(anEdgeId); + eraseRelationId(anEdgeRel.CoEdgeIds, theCoEdgeId); + } + return true; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::ReplaceCoEdgeUseWithPair(const BRepGraph_WireId theParentWireId, + const BRepGraph_CoEdgeId theOldCoEdgeId, + const BRepGraph_CoEdgeId theNewFirstCoEdgeId, + const BRepGraph_CoEdgeId theNewSecondCoEdgeId) +{ + if (!theParentWireId.IsValid(NbWires()) || !theOldCoEdgeId.IsValid(NbCoEdges()) + || !theNewFirstCoEdgeId.IsValid(NbCoEdges()) || !theNewSecondCoEdgeId.IsValid(NbCoEdges())) + { + return false; + } + + NCollection_LinearVector& aWireCoEdges = + ChangeWireRelationsInternal(theParentWireId).CoEdgeIds; + size_t aPos = 0; + for (; aPos < aWireCoEdges.Size(); ++aPos) + { + if (aWireCoEdges.Value(aPos) == theOldCoEdgeId) + { + break; + } + } + if (aPos >= aWireCoEdges.Size()) + { + return false; + } + + BRepGraphInc::CoEdgeDef& aNewFirst = ChangeCoEdge(theNewFirstCoEdgeId); + BRepGraphInc::CoEdgeDef& aNewSecond = ChangeCoEdge(theNewSecondCoEdgeId); + aNewFirst.ParentWireId = theParentWireId; + aNewSecond.ParentWireId = theParentWireId; + + const BRepGraphInc::CoEdgeDef& anOldCoEdge = CoEdge(theOldCoEdgeId); + if (anOldCoEdge.ChildEdgeId.IsValid(NbEdges())) + { + BRepGraphInc::EdgeRelations& anOldEdgeRel = + ChangeEdgeRelationsInternal(anOldCoEdge.ChildEdgeId); + eraseRelationId(anOldEdgeRel.CoEdgeIds, theOldCoEdgeId); + } + aWireCoEdges.ChangeValue(aPos) = theNewFirstCoEdgeId; + aWireCoEdges.InsertAfter(aPos, theNewSecondCoEdgeId); + + auto bindEdgeRelations = [&](const BRepGraph_CoEdgeId theCoEdgeId) { + const BRepGraphInc::CoEdgeDef& aCoEdge = CoEdge(theCoEdgeId); + if (!aCoEdge.ChildEdgeId.IsValid(NbEdges())) + { + return; + } + BRepGraphInc::EdgeRelations& anEdgeRel = ChangeEdgeRelationsInternal(aCoEdge.ChildEdgeId); + appendUniqueRelationId(anEdgeRel.CoEdgeIds, theCoEdgeId); + }; + bindEdgeRelations(theNewFirstCoEdgeId); + bindEdgeRelations(theNewSecondCoEdgeId); + return true; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::DetachWireFromFace(const BRepGraph_FaceId theParentFaceId, + const BRepGraph_WireRefId theRefId) +{ + if (!theParentFaceId.IsValid(NbFaces()) || !theRefId.IsValid(NbWireRefs())) + { + return false; + } + const BRepGraphInc::WireRef& aRef = WireRef(theRefId); + const bool isErased = + eraseRelationId(ChangeFaceRelationsInternal(theParentFaceId).WireRefIds, theRefId); + if (!isErased) + { + return false; + } + if (aRef.ChildWireId.IsValid(NbWires())) + { + eraseRelationId(ChangeWireRelationsInternal(aRef.ChildWireId).ParentWireRefIds, theRefId); + if (!wireHasFaceUse(*this, aRef.ChildWireId, theParentFaceId)) + { + clearWireFaceContext(*this, aRef.ChildWireId, theParentFaceId); + } + } + return true; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::DetachFaceFromShell(const BRepGraph_ShellId theParentShellId, + const BRepGraph_FaceRefId theRefId) +{ + if (!theParentShellId.IsValid(NbShells()) || !theRefId.IsValid(NbFaceRefs())) + { + return false; + } + const BRepGraphInc::FaceRef& aRef = FaceRef(theRefId); + const bool isErased = + eraseRelationId(ChangeShellRelationsInternal(theParentShellId).FaceRefIds, theRefId); + if (aRef.ChildFaceId.IsValid(NbFaces())) + { + eraseRelationId(ChangeFaceRelationsInternal(aRef.ChildFaceId).ParentFaceRefIds, theRefId); + } + return isErased; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::DetachShellFromSolid(const BRepGraph_SolidId theParentSolidId, + const BRepGraph_ShellRefId theRefId) +{ + if (!theParentSolidId.IsValid(NbSolids()) || !theRefId.IsValid(NbShellRefs())) + { + return false; + } + const BRepGraphInc::ShellRef& aRef = ShellRef(theRefId); + const bool isErased = + eraseRelationId(ChangeSolidRelationsInternal(theParentSolidId).ShellRefIds, theRefId); + if (aRef.ChildShellId.IsValid(NbShells())) + { + eraseRelationId(ChangeShellRelationsInternal(aRef.ChildShellId).ParentShellRefIds, theRefId); + } + return isErased; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::DetachSolidFromCompSolid( + const BRepGraph_CompSolidId theParentCompSolidId, + const BRepGraph_SolidRefId theRefId) +{ + if (!theParentCompSolidId.IsValid(NbCompSolids()) || !theRefId.IsValid(NbSolidRefs())) + { + return false; + } + const BRepGraphInc::SolidRef& aRef = SolidRef(theRefId); + const bool isErased = + eraseRelationId(ChangeCompSolidRelationsInternal(theParentCompSolidId).SolidRefIds, theRefId); + if (aRef.ChildSolidId.IsValid(NbSolids())) + { + eraseRelationId(ChangeSolidRelationsInternal(aRef.ChildSolidId).ParentSolidRefIds, theRefId); + } + return isErased; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::DetachChildFromCompound(const BRepGraph_CompoundId theParentCompoundId, + const BRepGraph_ChildRefId theRefId) +{ + if (!theParentCompoundId.IsValid(NbCompounds()) || !theRefId.IsValid(NbChildRefs())) + { + return false; + } + const BRepGraph_NodeId aChildNode = ChildRef(theRefId).ChildNodeId; + const bool isErased = + eraseRelationId(ChangeCompoundRelationsInternal(theParentCompoundId).ChildRefIds, theRefId); + if (isErased && aChildNode.IsValid()) + { + if (NCollection_LinearVector* aRefs = + myNodeToCompounds.ChangeSeek(aChildNode)) + { + eraseRelationId(*aRefs, theRefId); + if (aRefs->IsEmpty()) + { + SetHasCompoundParent(aChildNode, false); + } + } + } + return isErased; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::DetachOccurrenceFromProduct(const BRepGraph_ProductId theParentProductId, + const BRepGraph_OccurrenceRefId theRefId) +{ + if (!theParentProductId.IsValid(NbProducts()) || !theRefId.IsValid(NbOccurrenceRefs())) + { + return false; + } + BRepGraph_NodeId aChildNode; + const BRepGraphInc::OccurrenceRef& aRef = OccurrenceRef(theRefId); + if (aRef.ChildOccurrenceId.IsValid(NbOccurrences())) + { + aChildNode = Occurrence(aRef.ChildOccurrenceId).ChildNodeId; + } + const bool isErased = + eraseRelationId(ChangeProductRelationsInternal(theParentProductId).OccurrenceRefIds, theRefId); + if (isErased && aRef.ChildOccurrenceId.IsValid(NbOccurrences())) + { + eraseRelationId( + ChangeOccurrenceRelationsInternal(aRef.ChildOccurrenceId).ParentOccurrenceRefIds, + theRefId); + } + if (isErased && aChildNode.IsValid()) + { + if (NCollection_LinearVector* aRefs = + myNodeToOccurrences.ChangeSeek(aChildNode)) + { + eraseRelationId(*aRefs, theRefId); + if (aRefs->IsEmpty()) + { + SetHasOccurrenceParent(aChildNode, false); + } + } + } + return isErased; +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindOccurrenceChild(const BRepGraph_OccurrenceId theOccurrence, + const BRepGraph_NodeId theOldChild, + const BRepGraph_NodeId theNewChild) +{ + if (theOldChild == theNewChild || !theOccurrence.IsValid(NbOccurrences())) + { + return; + } + + for (BRepGraph_OccurrenceRefId aRefId = BRepGraph_OccurrenceRefId::Start(); + aRefId.IsValid(NbOccurrenceRefs()); + ++aRefId) + { + if (IsRemoved(aRefId) || OccurrenceRef(aRefId).ChildOccurrenceId != theOccurrence) + { + continue; + } + if (theOldChild.IsValid()) + { + if (NCollection_LinearVector* aRefs = + myNodeToOccurrences.ChangeSeek(theOldChild)) + { + eraseRelationId(*aRefs, aRefId); + if (aRefs->IsEmpty()) + { + SetHasOccurrenceParent(theOldChild, false); + } + } + } + if (theNewChild.IsValid()) + { + appendUniqueRelationId(ChangeOccurrenceRefsOfNodeInternal(theNewChild), aRefId); + SetHasOccurrenceParent(theNewChild, true); + } + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindVertexEdge(const BRepGraph_VertexId theOldVertex, + const BRepGraph_VertexId theNewVertex, + const BRepGraph_EdgeId theEdge, + const BRepGraph_VertexRefId theExcludingRef) +{ + if (theOldVertex == theNewVertex || !theEdge.IsValid(NbEdges())) + { + return; + } + if (IsRemoved(theEdge)) + { + return; + } + + BRepGraphInc::EdgeDef& anEdge = ChangeEdge(theEdge); + auto refTargets = [&](const BRepGraph_VertexRefId theRefId, + const BRepGraph_VertexId theVertex) -> bool { + if (!theRefId.IsValid(NbVertexRefs()) || theRefId == theExcludingRef || IsRemoved(theRefId)) + { + return false; + } + return VertexRef(theRefId).ChildVertexId == theVertex; + }; + + auto rebindRef = [&](const BRepGraph_VertexRefId theRefId) { + if (!theRefId.IsValid(NbVertexRefs()) || theRefId == theExcludingRef || IsRemoved(theRefId)) + { + return; + } + BRepGraphInc::VertexRef& aRef = ChangeVertexRef(theRefId); + if (aRef.ChildVertexId == theOldVertex) + { + aRef.ChildVertexId = theNewVertex; + } + }; + + rebindRef(anEdge.StartVertexRefId); + rebindRef(anEdge.EndVertexRefId); + + if (theOldVertex.IsValid(NbVertices()) && !refTargets(anEdge.StartVertexRefId, theOldVertex) + && !refTargets(anEdge.EndVertexRefId, theOldVertex)) + { + eraseRelationId(ChangeVertexRelationsInternal(theOldVertex).EdgeIds, theEdge); + } + if (theNewVertex.IsValid(NbVertices()) + && (refTargets(anEdge.StartVertexRefId, theNewVertex) + || refTargets(anEdge.EndVertexRefId, theNewVertex))) + { + appendUniqueRelationId(ChangeVertexRelationsInternal(theNewVertex).EdgeIds, theEdge); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindVertexRef(const BRepGraph_VertexRefId theRefId, + const BRepGraph_VertexId theOldVertex, + const BRepGraph_VertexId theNewVertex) +{ + if (theOldVertex == theNewVertex || !theRefId.IsValid(NbVertexRefs())) + { + return; + } + + BRepGraphInc::VertexRef& aRef = ChangeVertexRef(theRefId); + if (aRef.ChildVertexId != theOldVertex) + { + return; + } + + const BRepGraph_EdgeId anEdgeId = aRef.ParentEdgeId; + if (!anEdgeId.IsValid(NbEdges()) || IsRemoved(anEdgeId)) + { + return; + } + aRef.ChildVertexId = theNewVertex; + + const BRepGraphInc::EdgeDef& anEdge = Edge(anEdgeId); + auto refTargets = [&](const BRepGraph_VertexRefId theOtherRef, + const BRepGraph_VertexId theVertex) -> bool { + if (!theOtherRef.IsValid(NbVertexRefs()) || theOtherRef == theRefId || IsRemoved(theOtherRef)) + { + return false; + } + return VertexRef(theOtherRef).ChildVertexId == theVertex; + }; + + if (theOldVertex.IsValid(NbVertices()) && !refTargets(anEdge.StartVertexRefId, theOldVertex) + && !refTargets(anEdge.EndVertexRefId, theOldVertex)) + { + eraseRelationId(ChangeVertexRelationsInternal(theOldVertex).EdgeIds, anEdgeId); + } + if (theNewVertex.IsValid(NbVertices())) + { + appendUniqueRelationId(ChangeVertexRelationsInternal(theNewVertex).EdgeIds, anEdgeId); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindCoEdgeEdge(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_EdgeId theOldEdge, + const BRepGraph_EdgeId theNewEdge) +{ + if (theOldEdge == theNewEdge || !theCoEdge.IsValid(NbCoEdges())) + { + return; + } + + if (theOldEdge.IsValid(NbEdges())) + { + BRepGraphInc::EdgeRelations& anOldRel = ChangeEdgeRelationsInternal(theOldEdge); + eraseRelationId(anOldRel.CoEdgeIds, theCoEdge); + } + + if (theNewEdge.IsValid(NbEdges())) + { + BRepGraphInc::EdgeRelations& aNewRel = ChangeEdgeRelationsInternal(theNewEdge); + appendUniqueRelationId(aNewRel.CoEdgeIds, theCoEdge); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindWireRef(const BRepGraph_WireRefId theRefId, + const BRepGraph_WireId theOldWire, + const BRepGraph_WireId theNewWire) +{ + if (theOldWire == theNewWire || !theRefId.IsValid(NbWireRefs())) + { + return; + } + const BRepGraph_FaceId aParentFace = WireRef(theRefId).ParentFaceId; + if (theOldWire.IsValid(NbWires())) + { + eraseRelationId(ChangeWireRelationsInternal(theOldWire).ParentWireRefIds, theRefId); + if (aParentFace.IsValid(NbFaces())) + { + if (!wireHasFaceUse(*this, theOldWire, aParentFace)) + { + clearWireFaceContext(*this, theOldWire, aParentFace); + } + } + } + if (theNewWire.IsValid(NbWires()) && !IsRemoved(theRefId)) + { + appendUniqueRelationId(ChangeWireRelationsInternal(theNewWire).ParentWireRefIds, theRefId); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindFaceRef(const BRepGraph_FaceRefId theRefId, + const BRepGraph_FaceId theOldFace, + const BRepGraph_FaceId theNewFace) +{ + if (theOldFace == theNewFace || !theRefId.IsValid(NbFaceRefs())) + { + return; + } + if (theOldFace.IsValid(NbFaces())) + { + eraseRelationId(ChangeFaceRelationsInternal(theOldFace).ParentFaceRefIds, theRefId); + } + if (theNewFace.IsValid(NbFaces()) && !IsRemoved(theRefId)) + { + appendUniqueRelationId(ChangeFaceRelationsInternal(theNewFace).ParentFaceRefIds, theRefId); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindShellRef(const BRepGraph_ShellRefId theRefId, + const BRepGraph_ShellId theOldShell, + const BRepGraph_ShellId theNewShell) +{ + if (theOldShell == theNewShell || !theRefId.IsValid(NbShellRefs())) + { + return; + } + if (theOldShell.IsValid(NbShells())) + { + eraseRelationId(ChangeShellRelationsInternal(theOldShell).ParentShellRefIds, theRefId); + } + if (theNewShell.IsValid(NbShells()) && !IsRemoved(theRefId)) + { + appendUniqueRelationId(ChangeShellRelationsInternal(theNewShell).ParentShellRefIds, theRefId); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindSolidRef(const BRepGraph_SolidRefId theRefId, + const BRepGraph_SolidId theOldSolid, + const BRepGraph_SolidId theNewSolid) +{ + if (theOldSolid == theNewSolid || !theRefId.IsValid(NbSolidRefs())) + { + return; + } + if (theOldSolid.IsValid(NbSolids())) + { + eraseRelationId(ChangeSolidRelationsInternal(theOldSolid).ParentSolidRefIds, theRefId); + } + if (theNewSolid.IsValid(NbSolids()) && !IsRemoved(theRefId)) + { + appendUniqueRelationId(ChangeSolidRelationsInternal(theNewSolid).ParentSolidRefIds, theRefId); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindChildRef(const BRepGraph_ChildRefId theRefId, + const BRepGraph_NodeId theOldChild, + const BRepGraph_NodeId theNewChild) +{ + if (theOldChild == theNewChild || !theRefId.IsValid(NbChildRefs())) + { + return; + } + if (theOldChild.IsValid()) + { + if (NCollection_LinearVector* aRefs = + myNodeToCompounds.ChangeSeek(theOldChild)) + { + eraseRelationId(*aRefs, theRefId); + if (aRefs->IsEmpty()) + { + SetHasCompoundParent(theOldChild, false); + } + } + } + if (theNewChild.IsValid() && !IsRemoved(theRefId)) + { + appendUniqueRelationId(ChangeCompoundRefsOfNodeInternal(theNewChild), theRefId); + SetHasCompoundParent(theNewChild, true); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::RebindOccurrenceRef(const BRepGraph_OccurrenceRefId theRefId, + const BRepGraph_OccurrenceId theOldOccurrence, + const BRepGraph_OccurrenceId theNewOccurrence) +{ + if (theOldOccurrence == theNewOccurrence || !theRefId.IsValid(NbOccurrenceRefs())) + { + return; + } + + BRepGraph_NodeId anOldChild; + if (theOldOccurrence.IsValid(NbOccurrences())) + { + anOldChild = Occurrence(theOldOccurrence).ChildNodeId; + } + BRepGraph_NodeId aNewChild; + if (theNewOccurrence.IsValid(NbOccurrences())) + { + aNewChild = Occurrence(theNewOccurrence).ChildNodeId; + } + if (anOldChild.IsValid()) + { + if (NCollection_LinearVector* aRefs = + myNodeToOccurrences.ChangeSeek(anOldChild)) + { + eraseRelationId(*aRefs, theRefId); + if (aRefs->IsEmpty()) + { + SetHasOccurrenceParent(anOldChild, false); + } + } + } + if (theOldOccurrence.IsValid(NbOccurrences())) + { + eraseRelationId(ChangeOccurrenceRelationsInternal(theOldOccurrence).ParentOccurrenceRefIds, + theRefId); + } + if (theNewOccurrence.IsValid(NbOccurrences()) && !IsRemoved(theRefId)) + { + appendUniqueRelationId( + ChangeOccurrenceRelationsInternal(theNewOccurrence).ParentOccurrenceRefIds, + theRefId); + } + if (aNewChild.IsValid() && !IsRemoved(theRefId)) + { + appendUniqueRelationId(ChangeOccurrenceRefsOfNodeInternal(aNewChild), theRefId); + SetHasOccurrenceParent(aNewChild, true); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::ReverseWireCoEdges(const BRepGraph_WireId theWireId) +{ + if (!theWireId.IsValid(NbWires())) + { + return; + } + NCollection_LinearVector& aRefs = + ChangeWireRelationsInternal(theWireId).CoEdgeIds; + const size_t aNb = aRefs.Size(); + if (aNb < 2) + { + return; + } + + for (size_t i = 0, j = aNb - 1; i < j; ++i, --j) + { + const BRepGraph_CoEdgeId aTmp = aRefs.Value(i); + aRefs.ChangeValue(i) = aRefs.Value(j); + aRefs.ChangeValue(j) = aTmp; + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::SetWireCoEdges( + const BRepGraph_WireId theWireId, + const NCollection_Array1& theCoEdgeIds) +{ + if (!theWireId.IsValid(NbWires())) + { + return; + } + assignRelationIds(ChangeWireRelationsInternal(theWireId).CoEdgeIds, theCoEdgeIds); +} + +//================================================================================================= + +void BRepGraphInc_Storage::SetFaceWireRefs( + const BRepGraph_FaceId theFaceId, + const NCollection_Array1& theWireRefIds) +{ + if (theFaceId.IsValid(NbFaces())) + { + assignRelationIds(ChangeFaceRelationsInternal(theFaceId).WireRefIds, theWireRefIds); + } +} + +void BRepGraphInc_Storage::SetShellFaceRefs( + const BRepGraph_ShellId theShellId, + const NCollection_Array1& theFaceRefIds) +{ + if (theShellId.IsValid(NbShells())) + { + assignRelationIds(ChangeShellRelationsInternal(theShellId).FaceRefIds, theFaceRefIds); + } +} + +void BRepGraphInc_Storage::SetSolidShellRefs( + const BRepGraph_SolidId theSolidId, + const NCollection_Array1& theShellRefIds) +{ + if (theSolidId.IsValid(NbSolids())) + { + assignRelationIds(ChangeSolidRelationsInternal(theSolidId).ShellRefIds, theShellRefIds); + } +} + +void BRepGraphInc_Storage::SetCompSolidSolidRefs( + const BRepGraph_CompSolidId theCompSolidId, + const NCollection_Array1& theSolidRefIds) +{ + if (theCompSolidId.IsValid(NbCompSolids())) + { + assignRelationIds(ChangeCompSolidRelationsInternal(theCompSolidId).SolidRefIds, theSolidRefIds); + } +} + +void BRepGraphInc_Storage::SetCompoundChildRefs( + const BRepGraph_CompoundId theCompoundId, + const NCollection_Array1& theChildRefIds) +{ + if (theCompoundId.IsValid(NbCompounds())) + { + assignRelationIds(ChangeCompoundRelationsInternal(theCompoundId).ChildRefIds, theChildRefIds); + } +} + +void BRepGraphInc_Storage::SetProductOccurrenceRefs( + const BRepGraph_ProductId theProductId, + const NCollection_Array1& theOccurrenceRefIds) +{ + if (theProductId.IsValid(NbProducts())) + { + assignRelationIds(ChangeProductRelationsInternal(theProductId).OccurrenceRefIds, + theOccurrenceRefIds); + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::ClearUIDIndexes() +{ + { + std::unique_lock aLock(myUIDToNodeIdMutex); + myUIDToNodeId.Clear(); + myUIDToNodeIdDirty = false; + } + { + std::unique_lock aLock(myRefUIDToRefIdMutex); + myRefUIDToRefId.Clear(); + myRefUIDToRefIdDirty = false; + } +} + +//================================================================================================= + +void BRepGraphInc_Storage::ClearShapeCache() +{ + std::unique_lock aLock(myCurrentShapesMutex); + myCurrentShapes.Clear(true); +} + +//================================================================================================= + +void BRepGraphInc_Storage::ClearCurrentShapes() +{ + ClearShapeCache(); +} + +//================================================================================================= + +void BRepGraphInc_Storage::UnbindCurrentShape(const BRepGraph_NodeId theNode) +{ + std::unique_lock aLock(myCurrentShapesMutex); + myCurrentShapes.UnBind(theNode); +} + +//================================================================================================= + +void BRepGraphInc_Storage::ClearDeferredQueues() +{ + myDeferredModified.Clear(true); + myDeferredRefModified.Clear(true); } //================================================================================================= @@ -272,10 +2652,6 @@ const BRepGraphInc::BaseRef& BRepGraphInc_Storage::BaseRef(const BRepGraph_RefId { return findInStore(myWireRefs, theTypedId); } - else if constexpr (std::is_same_v) - { - return findInStore(myCoEdgeRefs, theTypedId); - } else if constexpr (std::is_same_v) { return findInStore(myVertexRefs, theTypedId); @@ -304,12 +2680,11 @@ const BRepGraphInc::BaseRef& BRepGraphInc_Storage::BaseRef(const BRepGraph_RefId //================================================================================================= -BRepGraphInc::BaseRef& BRepGraphInc_Storage::ChangeBaseRef(const BRepGraph_RefId theRefId) +BRepGraphInc::BaseRef* BRepGraphInc_Storage::ChangeBaseRef(const BRepGraph_RefId theRefId) { - static BRepGraphInc::BaseRef anInvalid; if (!theRefId.IsValid()) { - return anInvalid; + return nullptr; } const auto aChangeRef = [this](const auto theTypedId) -> BRepGraphInc::BaseRef* { @@ -327,10 +2702,6 @@ BRepGraphInc::BaseRef& BRepGraphInc_Storage::ChangeBaseRef(const BRepGraph_RefId { return changeFindInStore(myWireRefs, theTypedId); } - else if constexpr (std::is_same_v) - { - return changeFindInStore(myCoEdgeRefs, theTypedId); - } else if constexpr (std::is_same_v) { return changeFindInStore(myVertexRefs, theTypedId); @@ -353,44 +2724,94 @@ BRepGraphInc::BaseRef& BRepGraphInc_Storage::ChangeBaseRef(const BRepGraph_RefId } }; - BRepGraphInc::BaseRef* aRef = BRepGraph_RefId::Visit(theRefId, aChangeRef); - return aRef != nullptr ? *aRef : anInvalid; + return BRepGraph_RefId::Visit(theRefId, aChangeRef); } //================================================================================================= void BRepGraphInc_Storage::Clear() { - myVertices.Clear(); - myEdges.Clear(); - myCoEdges.Clear(); - myWires.Clear(); - myFaces.Clear(); - myShells.Clear(); - mySolids.Clear(); - myCompounds.Clear(); - myCompSolids.Clear(); - myProducts.Clear(); - myOccurrences.Clear(); - myShellRefs.Clear(); - myFaceRefs.Clear(); - myWireRefs.Clear(); - myCoEdgeRefs.Clear(); - myVertexRefs.Clear(); - mySolidRefs.Clear(); - myChildRefs.Clear(); - myOccurrenceRefs.Clear(); - mySurfaces.Clear(); - myCurves3D.Clear(); - myCurves2D.Clear(); - myTriangulationsRep.Clear(); - myPolygons3D.Clear(); - myPolygons2D.Clear(); - myPolygonsOnTri.Clear(); - myReverseIdx.Clear(); - myTShapeToNodeId.Clear(); - myOriginalShapes.Clear(); - myIsDone = false; + ClearStorageForReuse(); + ClearUIDIndexes(); + ClearShapeCache(); + ClearDeferredQueues(); +} + +//================================================================================================= + +void BRepGraphInc_Storage::PrepareForLoad(const BRepGraphInc_Load::Counts& theCounts) +{ + Clear(); + + prepareDefStore(myVertices, theCounts.NbVertices, myAllocator); + prepareDefStore(myEdges, theCounts.NbEdges, myAllocator); + prepareDefStore(myCoEdges, theCounts.NbCoEdges, myAllocator); + prepareDefStore(myWires, theCounts.NbWires, myAllocator); + prepareDefStore(myFaces, theCounts.NbFaces, myAllocator); + prepareDefStore(myShells, theCounts.NbShells, myAllocator); + prepareDefStore(mySolids, theCounts.NbSolids, myAllocator); + prepareDefStore(myCompounds, theCounts.NbCompounds, myAllocator); + prepareDefStore(myCompSolids, theCounts.NbCompSolids, myAllocator); + prepareDefStore(myProducts, theCounts.NbProducts, myAllocator); + prepareDefStore(myOccurrences, theCounts.NbOccurrences, myAllocator); + + prepareRelationTable(myVertexRelations, theCounts.NbVertices); + prepareRelationTable(myEdgeRelations, theCounts.NbEdges); + prepareRelationTable(myWireRelations, theCounts.NbWires); + prepareRelationTable(myFaceRelations, theCounts.NbFaces); + prepareRelationTable(myShellRelations, theCounts.NbShells); + prepareRelationTable(mySolidRelations, theCounts.NbSolids); + prepareRelationTable(myCompoundRelations, theCounts.NbCompounds); + prepareRelationTable(myCompSolidRelations, theCounts.NbCompSolids); + prepareRelationTable(myProductRelations, theCounts.NbProducts); + prepareRelationTable(myOccurrenceRelations, theCounts.NbOccurrences); + + prepareRefStore(myShellRefs, theCounts.NbShellRefs); + prepareRefStore(myFaceRefs, theCounts.NbFaceRefs); + prepareRefStore(myWireRefs, theCounts.NbWireRefs); + prepareRefStore(myVertexRefs, theCounts.NbVertexRefs); + prepareRefStore(mySolidRefs, theCounts.NbSolidRefs); + prepareRefStore(myChildRefs, theCounts.NbChildRefs); + prepareRefStore(myOccurrenceRefs, theCounts.NbOccurrenceRefs); + + prepareRepStore(myFaceSurfaces, theCounts.NbFaceSurfaceReps); + prepareRepStore(myEdgeCurves3D, theCounts.NbEdgeCurve3DReps); + prepareRepStore(myCoEdgeCurves2D, theCounts.NbCoEdgeCurve2DReps); + prepareRepStore(myFaceTriangulations, theCounts.NbFaceTriangulationReps); + prepareRepStore(myEdgePolygons3D, theCounts.NbEdgePolygon3DReps); + prepareRepStore(myCoEdgePolygons2D, theCounts.NbCoEdgePolygon2DReps); + prepareRepStore(myCoEdgePolygonsOnTri, theCounts.NbCoEdgePolygonOnTriReps); +} + +//================================================================================================= + +void BRepGraphInc_Storage::SetActiveCounts(const BRepGraphInc_Load::Counts& theCounts) +{ + myVertices.NbActive = theCounts.NbVertices; + myEdges.NbActive = theCounts.NbEdges; + myCoEdges.NbActive = theCounts.NbCoEdges; + myWires.NbActive = theCounts.NbWires; + myFaces.NbActive = theCounts.NbFaces; + myShells.NbActive = theCounts.NbShells; + mySolids.NbActive = theCounts.NbSolids; + myCompounds.NbActive = theCounts.NbCompounds; + myCompSolids.NbActive = theCounts.NbCompSolids; + myProducts.NbActive = theCounts.NbProducts; + myOccurrences.NbActive = theCounts.NbOccurrences; + myShellRefs.NbActive = theCounts.NbShellRefs; + myFaceRefs.NbActive = theCounts.NbFaceRefs; + myWireRefs.NbActive = theCounts.NbWireRefs; + myVertexRefs.NbActive = theCounts.NbVertexRefs; + mySolidRefs.NbActive = theCounts.NbSolidRefs; + myChildRefs.NbActive = theCounts.NbChildRefs; + myOccurrenceRefs.NbActive = theCounts.NbOccurrenceRefs; + myFaceSurfaces.NbActive = theCounts.NbFaceSurfaceReps; + myEdgeCurves3D.NbActive = theCounts.NbEdgeCurve3DReps; + myCoEdgeCurves2D.NbActive = theCounts.NbCoEdgeCurve2DReps; + myFaceTriangulations.NbActive = theCounts.NbFaceTriangulationReps; + myEdgePolygons3D.NbActive = theCounts.NbEdgePolygon3DReps; + myCoEdgePolygons2D.NbActive = theCounts.NbCoEdgePolygon2DReps; + myCoEdgePolygonsOnTri.NbActive = theCounts.NbCoEdgePolygonOnTriReps; } //================================================================================================= @@ -450,7 +2871,89 @@ bool BRepGraphInc_Storage::MarkRemoved(const BRepGraph_NodeId theNodeId) } }; - return theNodeId.IsValid() ? BRepGraph_NodeId::Visit(theNodeId, aMarkRemoved) : false; + const bool isRemoved = + theNodeId.IsValid() ? BRepGraph_NodeId::Visit(theNodeId, aMarkRemoved) : false; + if (isRemoved) + { + const TopoDS_Shape* aShape = myOriginalShapes.Seek(theNodeId); + const TopoDS_TShape* aTShapeToUnbind = + (aShape != nullptr && !aShape->IsNull()) ? aShape->TShape().get() : nullptr; + myOriginalShapes.UnBind(theNodeId); + if (aTShapeToUnbind != nullptr) + { + const BRepGraph_NodeId* aBound = myTShapeToNodeId.Seek(aTShapeToUnbind); + if (aBound != nullptr && *aBound == theNodeId) + { + myTShapeToNodeId.UnBind(aTShapeToUnbind); + } + } + } + return isRemoved; +} + +//================================================================================================= + +bool BRepGraphInc_Storage::MarkRemoved(const BRepGraph_RepId theRepId) +{ + if (!theRepId.IsValid()) + { + return false; + } + + switch (theRepId.RepKind) + { + case BRepGraph_RepId::Kind::EdgeCurve3D: + return myEdgeCurves3D.MarkRemoved(BRepGraph_EdgeCurve3DRepId(theRepId.Index)); + case BRepGraph_RepId::Kind::EdgePolygon3D: + return myEdgePolygons3D.MarkRemoved(BRepGraph_EdgePolygon3DRepId(theRepId.Index)); + case BRepGraph_RepId::Kind::CoEdgeCurve2D: + return myCoEdgeCurves2D.MarkRemoved(BRepGraph_CoEdgeCurve2DRepId(theRepId.Index)); + case BRepGraph_RepId::Kind::CoEdgePolygon2D: + return myCoEdgePolygons2D.MarkRemoved(BRepGraph_CoEdgePolygon2DRepId(theRepId.Index)); + case BRepGraph_RepId::Kind::CoEdgePolygonOnTri: + return myCoEdgePolygonsOnTri.MarkRemoved(BRepGraph_CoEdgePolygonOnTriRepId(theRepId.Index)); + case BRepGraph_RepId::Kind::FaceSurface: + return myFaceSurfaces.MarkRemoved(BRepGraph_FaceSurfaceRepId(theRepId.Index)); + case BRepGraph_RepId::Kind::FaceTriangulation: + return myFaceTriangulations.MarkRemoved(BRepGraph_FaceTriangulationRepId(theRepId.Index)); + } + + return false; +} + +//================================================================================================= + +void BRepGraphInc_Storage::SetRemoved(const BRepGraph_RepId theRepId, const bool theVal) +{ + if (!theRepId.IsValid()) + { + return; + } + + switch (theRepId.RepKind) + { + case BRepGraph_RepId::Kind::EdgeCurve3D: + SetRemoved(BRepGraph_EdgeCurve3DRepId(theRepId.Index), theVal); + return; + case BRepGraph_RepId::Kind::EdgePolygon3D: + SetRemoved(BRepGraph_EdgePolygon3DRepId(theRepId.Index), theVal); + return; + case BRepGraph_RepId::Kind::CoEdgeCurve2D: + SetRemoved(BRepGraph_CoEdgeCurve2DRepId(theRepId.Index), theVal); + return; + case BRepGraph_RepId::Kind::CoEdgePolygon2D: + SetRemoved(BRepGraph_CoEdgePolygon2DRepId(theRepId.Index), theVal); + return; + case BRepGraph_RepId::Kind::CoEdgePolygonOnTri: + SetRemoved(BRepGraph_CoEdgePolygonOnTriRepId(theRepId.Index), theVal); + return; + case BRepGraph_RepId::Kind::FaceSurface: + SetRemoved(BRepGraph_FaceSurfaceRepId(theRepId.Index), theVal); + return; + case BRepGraph_RepId::Kind::FaceTriangulation: + SetRemoved(BRepGraph_FaceTriangulationRepId(theRepId.Index), theVal); + return; + } } //================================================================================================= @@ -472,10 +2975,6 @@ bool BRepGraphInc_Storage::MarkRemovedRef(const BRepGraph_RefId theRefId) { return myWireRefs.MarkRemoved(theTypedId); } - else if constexpr (std::is_same_v) - { - return myCoEdgeRefs.MarkRemoved(theTypedId); - } else if constexpr (std::is_same_v) { return myVertexRefs.MarkRemoved(theTypedId); @@ -503,527 +3002,331 @@ bool BRepGraphInc_Storage::MarkRemovedRef(const BRepGraph_RefId theRefId) //================================================================================================= -bool BRepGraphInc_Storage::MarkRemovedRep(const BRepGraph_RepId theRepId) +void BRepGraphInc_Storage::RecountActiveCounts() { - const auto aMarkRemoved = [this](const auto theTypedId) -> bool { - using TypeId = std::remove_cv_t; - - if constexpr (std::is_same_v) - { - return mySurfaces.MarkRemoved(theTypedId); - } - else if constexpr (std::is_same_v) - { - return myCurves3D.MarkRemoved(theTypedId); - } - else if constexpr (std::is_same_v) - { - return myCurves2D.MarkRemoved(theTypedId); - } - else if constexpr (std::is_same_v) - { - return myTriangulationsRep.MarkRemoved(theTypedId); - } - else if constexpr (std::is_same_v) - { - return myPolygons3D.MarkRemoved(theTypedId); - } - else if constexpr (std::is_same_v) - { - return myPolygons2D.MarkRemoved(theTypedId); - } - else if constexpr (std::is_same_v) - { - return myPolygonsOnTri.MarkRemoved(theTypedId); - } - else - { - Standard_ASSERT_RETURN(false, "MarkRemovedRep: unsupported rep id type", false); - } - }; - - return theRepId.IsValid() ? BRepGraph_RepId::Visit(theRepId, aMarkRemoved) : false; + recountActiveStore(myVertices, BRepGraph_VertexId()); + recountActiveStore(myEdges, BRepGraph_EdgeId()); + recountActiveStore(myCoEdges, BRepGraph_CoEdgeId()); + recountActiveStore(myWires, BRepGraph_WireId()); + recountActiveStore(myFaces, BRepGraph_FaceId()); + recountActiveStore(myShells, BRepGraph_ShellId()); + recountActiveStore(mySolids, BRepGraph_SolidId()); + recountActiveStore(myCompounds, BRepGraph_CompoundId()); + recountActiveStore(myCompSolids, BRepGraph_CompSolidId()); + recountActiveStore(myProducts, BRepGraph_ProductId()); + recountActiveStore(myOccurrences, BRepGraph_OccurrenceId()); + recountActiveStore(myShellRefs, BRepGraph_ShellRefId()); + recountActiveStore(myFaceRefs, BRepGraph_FaceRefId()); + recountActiveStore(myWireRefs, BRepGraph_WireRefId()); + recountActiveStore(myVertexRefs, BRepGraph_VertexRefId()); + recountActiveStore(mySolidRefs, BRepGraph_SolidRefId()); + recountActiveStore(myChildRefs, BRepGraph_ChildRefId()); + recountActiveStore(myOccurrenceRefs, BRepGraph_OccurrenceRefId()); + recountActiveStore(myFaceSurfaces, BRepGraph_FaceSurfaceRepId()); + recountActiveStore(myEdgeCurves3D, BRepGraph_EdgeCurve3DRepId()); + recountActiveStore(myCoEdgeCurves2D, BRepGraph_CoEdgeCurve2DRepId()); + recountActiveStore(myFaceTriangulations, BRepGraph_FaceTriangulationRepId()); + recountActiveStore(myEdgePolygons3D, BRepGraph_EdgePolygon3DRepId()); + recountActiveStore(myCoEdgePolygons2D, BRepGraph_CoEdgePolygon2DRepId()); + recountActiveStore(myCoEdgePolygonsOnTri, BRepGraph_CoEdgePolygonOnTriRepId()); } //================================================================================================= -void BRepGraphInc_Storage::BuildReverseIndex() +void BRepGraphInc_Storage::RebuildUIDReverseIndexes() { - myReverseIdx.SetAllocator(myAllocator); - myReverseIdx.Build(*this); - myReverseIdx.BuildProductOccurrences(myOccurrences.Entities, myProducts.Nb()); + MarkUIDReverseIndexesDirty(); + EnsureUIDReverseIndex(); + EnsureRefUIDReverseIndex(); +} - // Recount active entities to sync counters after Build. - // Populate may have set IsRemoved on some entities without going through RemoveNode. - myVertices.NbActive = 0; - myEdges.NbActive = 0; - myCoEdges.NbActive = 0; - myWires.NbActive = 0; - myFaces.NbActive = 0; - myShells.NbActive = 0; - mySolids.NbActive = 0; - myCompounds.NbActive = 0; - myCompSolids.NbActive = 0; - myProducts.NbActive = 0; - myOccurrences.NbActive = 0; - mySurfaces.NbActive = 0; - myCurves3D.NbActive = 0; - myCurves2D.NbActive = 0; - myTriangulationsRep.NbActive = 0; - myPolygons3D.NbActive = 0; - myPolygons2D.NbActive = 0; - myPolygonsOnTri.NbActive = 0; - for (BRepGraph_VertexId aId = BRepGraph_VertexId::Start(); aId.IsValid(myVertices.Nb()); ++aId) +//================================================================================================= + +void BRepGraphInc_Storage::MarkUIDReverseIndexesDirty() +{ { - if (!myVertices.Get(aId).IsRemoved) - { - ++myVertices.NbActive; - } + std::unique_lock aLock(myUIDToNodeIdMutex); + myUIDToNodeId.Clear(); + myUIDToNodeIdDirty = true; } - for (BRepGraph_EdgeId aId = BRepGraph_EdgeId::Start(); aId.IsValid(myEdges.Nb()); ++aId) { - if (!myEdges.Get(aId).IsRemoved) - { - ++myEdges.NbActive; - } - } - for (BRepGraph_CoEdgeId aId = BRepGraph_CoEdgeId::Start(); aId.IsValid(myCoEdges.Nb()); ++aId) - { - if (!myCoEdges.Get(aId).IsRemoved) - { - ++myCoEdges.NbActive; - } - } - for (BRepGraph_WireId aId = BRepGraph_WireId::Start(); aId.IsValid(myWires.Nb()); ++aId) - { - if (!myWires.Get(aId).IsRemoved) - { - ++myWires.NbActive; - } - } - for (BRepGraph_FaceId aId = BRepGraph_FaceId::Start(); aId.IsValid(myFaces.Nb()); ++aId) - { - if (!myFaces.Get(aId).IsRemoved) - { - ++myFaces.NbActive; - } - } - for (BRepGraph_ShellId aId = BRepGraph_ShellId::Start(); aId.IsValid(myShells.Nb()); ++aId) - { - if (!myShells.Get(aId).IsRemoved) - { - ++myShells.NbActive; - } - } - for (BRepGraph_SolidId aId = BRepGraph_SolidId::Start(); aId.IsValid(mySolids.Nb()); ++aId) - { - if (!mySolids.Get(aId).IsRemoved) - { - ++mySolids.NbActive; - } - } - for (BRepGraph_CompoundId aId = BRepGraph_CompoundId::Start(); aId.IsValid(myCompounds.Nb()); - ++aId) - { - if (!myCompounds.Get(aId).IsRemoved) - { - ++myCompounds.NbActive; - } - } - for (BRepGraph_CompSolidId aId = BRepGraph_CompSolidId::Start(); aId.IsValid(myCompSolids.Nb()); - ++aId) - { - if (!myCompSolids.Get(aId).IsRemoved) - { - ++myCompSolids.NbActive; - } - } - for (BRepGraph_ProductId aId = BRepGraph_ProductId::Start(); aId.IsValid(myProducts.Nb()); ++aId) - { - if (!myProducts.Get(aId).IsRemoved) - { - ++myProducts.NbActive; - } - } - for (BRepGraph_OccurrenceId aId = BRepGraph_OccurrenceId::Start(); - aId.IsValid(myOccurrences.Nb()); - ++aId) - { - if (!myOccurrences.Get(aId).IsRemoved) - { - ++myOccurrences.NbActive; - } - } - for (BRepGraph_SurfaceRepId aId = BRepGraph_SurfaceRepId::Start(); aId.IsValid(mySurfaces.Nb()); - ++aId) - { - if (!mySurfaces.Get(aId).IsRemoved) - { - ++mySurfaces.NbActive; - } - } - for (BRepGraph_Curve3DRepId aId = BRepGraph_Curve3DRepId::Start(); aId.IsValid(myCurves3D.Nb()); - ++aId) - { - if (!myCurves3D.Get(aId).IsRemoved) - { - ++myCurves3D.NbActive; - } - } - for (BRepGraph_Curve2DRepId aId = BRepGraph_Curve2DRepId::Start(); aId.IsValid(myCurves2D.Nb()); - ++aId) - { - if (!myCurves2D.Get(aId).IsRemoved) - { - ++myCurves2D.NbActive; - } - } - for (BRepGraph_TriangulationRepId aId = BRepGraph_TriangulationRepId::Start(); - aId.IsValid(myTriangulationsRep.Nb()); - ++aId) - { - if (!myTriangulationsRep.Get(aId).IsRemoved) - { - ++myTriangulationsRep.NbActive; - } - } - for (BRepGraph_Polygon3DRepId aId = BRepGraph_Polygon3DRepId::Start(); - aId.IsValid(myPolygons3D.Nb()); - ++aId) - { - if (!myPolygons3D.Get(aId).IsRemoved) - { - ++myPolygons3D.NbActive; - } - } - for (BRepGraph_Polygon2DRepId aId = BRepGraph_Polygon2DRepId::Start(); - aId.IsValid(myPolygons2D.Nb()); - ++aId) - { - if (!myPolygons2D.Get(aId).IsRemoved) - { - ++myPolygons2D.NbActive; - } - } - for (BRepGraph_PolygonOnTriRepId aId = BRepGraph_PolygonOnTriRepId::Start(); - aId.IsValid(myPolygonsOnTri.Nb()); - ++aId) - { - if (!myPolygonsOnTri.Get(aId).IsRemoved) - { - ++myPolygonsOnTri.NbActive; - } + std::unique_lock aLock(myRefUIDToRefIdMutex); + myRefUIDToRefId.Clear(); + myRefUIDToRefIdDirty = true; } } //================================================================================================= -void BRepGraphInc_Storage::BuildDeltaReverseIndex(const uint32_t theOldNbEdges, - const uint32_t theOldNbWires, - const uint32_t theOldNbFaces, - const uint32_t theOldNbShells, - const uint32_t theOldNbSolids, - const uint32_t theOldNbCompounds, - const uint32_t theOldNbCompSolids, - const uint32_t theOldNbChildRefs, - const uint32_t theOldNbSolidRefs) +void BRepGraphInc_Storage::EnsureUIDReverseIndex() const { - // Ensure allocator is set for reverse index inner vectors. - // BuildReverseIndex() always calls SetAllocator(), but when - // AppendFlattened/AppendFull is the first operation on a fresh - // graph (no prior BRepGraph_Builder::Add()), the allocator has not been set yet. - myReverseIdx.SetAllocator(myAllocator); - myReverseIdx.BuildDelta(myVertices.Entities, - myEdges.Entities, - myCoEdges.Entities, - myWires.Entities, - myFaces.Entities, - myShells.Entities, - mySolids.Entities, - myCompounds.Entities, - myCompSolids.Entities, - myShellRefs.Refs, - myFaceRefs.Refs, - myWireRefs.Refs, - myCoEdgeRefs.Refs, - mySolidRefs.Refs, - myChildRefs.Refs, - myVertexRefs.Refs, - theOldNbEdges, - theOldNbWires, - theOldNbFaces, - theOldNbShells, - theOldNbSolids, - theOldNbCompounds, - theOldNbCompSolids, - theOldNbChildRefs, - theOldNbSolidRefs); -} - -//================================================================================================= - -bool BRepGraphInc_Storage::ValidateReverseIndex() const -{ - // Self-ID consistency is a separate invariant; Audit-mode BRepGraph_Validate - // runs checkDefIds / checkRefIds for that. Keeping the Self-ID check out of - // this function lets Lightweight/MutationBoundary remain O(active-refs) and - // preserves the design contract that Lightweight does NOT perform deep id - // drift detection. - if (!myReverseIdx.Validate(myVertices.Entities, - myEdges.Entities, - myCoEdges.Entities, - myWires.Entities, - myFaces.Entities, - myShells.Entities, - mySolids.Entities, - myCompounds.Entities, - myCompSolids.Entities, - myShellRefs.Refs, - myFaceRefs.Refs, - myWireRefs.Refs, - myCoEdgeRefs.Refs, - mySolidRefs.Refs, - myChildRefs.Refs, - myVertexRefs.Refs)) + std::unique_lock aLock(myUIDToNodeIdMutex); + if (!myUIDToNodeIdDirty) { - return false; + return; } - // Wire -> CoEdge and Edge -> CoEdge coherence via coedge ref entries. - for (BRepGraph_CoEdgeRefId aCoEdgeRefId = BRepGraph_CoEdgeRefId::Start(); - aCoEdgeRefId.IsValid(myCoEdgeRefs.Nb()); - ++aCoEdgeRefId) + myUIDToNodeId.Clear(); + const BRepGraph_NodeId::Kind aKinds[] = {BRepGraph_NodeId::Kind::Solid, + BRepGraph_NodeId::Kind::Shell, + BRepGraph_NodeId::Kind::Face, + BRepGraph_NodeId::Kind::Wire, + BRepGraph_NodeId::Kind::Edge, + BRepGraph_NodeId::Kind::Vertex, + BRepGraph_NodeId::Kind::Compound, + BRepGraph_NodeId::Kind::CompSolid, + BRepGraph_NodeId::Kind::CoEdge, + BRepGraph_NodeId::Kind::Product, + BRepGraph_NodeId::Kind::Occurrence}; + // Reserve based on total entity count. + size_t aNodeUidCount = 0; + for (const BRepGraph_NodeId::Kind aKind : aKinds) { - const BRepGraphInc::CoEdgeRef& aRef = myCoEdgeRefs.Get(aCoEdgeRefId); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Wire || !aRef.CoEdgeDefId.IsValid()) + switch (aKind) { - continue; - } - if (!aRef.ParentId.IsValid(myWires.Nb()) || !aRef.CoEdgeDefId.IsValid(myCoEdges.Nb())) - { - return false; - } - - const BRepGraphInc::WireDef& aWire = myWires.Get(BRepGraph_WireId(aRef.ParentId.Index)); - const BRepGraphInc::CoEdgeDef& aCoEdge = myCoEdges.Get(aRef.CoEdgeDefId); - if (aWire.IsRemoved || aCoEdge.IsRemoved) - { - continue; - } - if (!containsNodeIndex(myReverseIdx.WiresOfCoEdge(aRef.CoEdgeDefId), aRef.ParentId.Index)) - { - return false; - } - if (aCoEdge.EdgeDefId.IsValid() - && !containsNodeIndex(myReverseIdx.CoEdgesOfEdge(aCoEdge.EdgeDefId), - aRef.CoEdgeDefId.Index)) - { - return false; - } - } - - // Reverse Edge -> CoEdges entries must point back to active CoEdges. - for (BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); anEdgeId.IsValid(myEdges.Nb()); - ++anEdgeId) - { - const NCollection_DynamicArray* aCoEdges = - myReverseIdx.CoEdgesOfEdge(anEdgeId); - if (aCoEdges == nullptr) - { - continue; - } - for (const BRepGraph_CoEdgeId& aCoEdgeElem : *aCoEdges) - { - if (!aCoEdgeElem.IsValid(myCoEdges.Nb())) - { - return false; - } - const BRepGraphInc::CoEdgeDef& aCoEdge = myCoEdges.Get(aCoEdgeElem); - if (aCoEdge.IsRemoved || !aCoEdge.EdgeDefId.IsValid() || aCoEdge.EdgeDefId != anEdgeId) - { - return false; - } - } - } - - // Compound child reverse maps via child ref entries. - for (BRepGraph_ChildRefId aChildRefId = BRepGraph_ChildRefId::Start(); - aChildRefId.IsValid(myChildRefs.Nb()); - ++aChildRefId) - { - const BRepGraphInc::ChildRef& aRef = myChildRefs.Get(aChildRefId); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::Compound || !aRef.ChildDefId.IsValid()) - { - continue; - } - if (!aRef.ParentId.IsValid(myCompounds.Nb())) - { - return false; - } - - const BRepGraphInc::CompoundDef& aCompound = - myCompounds.Get(BRepGraph_CompoundId(aRef.ParentId.Index)); - if (aCompound.IsRemoved) - { - continue; - } - - switch (aRef.ChildDefId.NodeKind) - { - case BRepGraph_NodeId::Kind::Solid: - if (!containsNodeIndex( - myReverseIdx.CompoundsOfSolid(BRepGraph_SolidId(aRef.ChildDefId.Index)), - aRef.ParentId.Index)) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Shell: - if (!containsNodeIndex( - myReverseIdx.CompoundsOfShell(BRepGraph_ShellId(aRef.ChildDefId.Index)), - aRef.ParentId.Index)) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Face: - if (!containsNodeIndex( - myReverseIdx.CompoundsOfFace(BRepGraph_FaceId(aRef.ChildDefId.Index)), - aRef.ParentId.Index)) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Compound: - if (!containsNodeIndex( - myReverseIdx.CompoundsOfCompound(BRepGraph_CompoundId(aRef.ChildDefId.Index)), - aRef.ParentId.Index)) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::CompSolid: - if (!containsNodeIndex( - myReverseIdx.CompoundsOfCompSolid(BRepGraph_CompSolidId(aRef.ChildDefId.Index)), - aRef.ParentId.Index)) - { - return false; - } - break; - case BRepGraph_NodeId::Kind::Wire: - if (!containsNodeIndex( - myReverseIdx.CompoundsOfWire(BRepGraph_WireId(aRef.ChildDefId.Index)), - aRef.ParentId.Index)) - { - return false; - } + case BRepGraph_NodeId::Kind::Vertex: + aNodeUidCount += myVertices.Nb(); break; case BRepGraph_NodeId::Kind::Edge: - if (!containsNodeIndex( - myReverseIdx.CompoundsOfEdge(BRepGraph_EdgeId(aRef.ChildDefId.Index)), - aRef.ParentId.Index)) - { - return false; - } + aNodeUidCount += myEdges.Nb(); break; - case BRepGraph_NodeId::Kind::Vertex: - if (!containsNodeIndex( - myReverseIdx.CompoundsOfVertex(BRepGraph_VertexId(aRef.ChildDefId.Index)), - aRef.ParentId.Index)) - { - return false; - } + case BRepGraph_NodeId::Kind::CoEdge: + aNodeUidCount += myCoEdges.Nb(); + break; + case BRepGraph_NodeId::Kind::Wire: + aNodeUidCount += myWires.Nb(); + break; + case BRepGraph_NodeId::Kind::Face: + aNodeUidCount += myFaces.Nb(); + break; + case BRepGraph_NodeId::Kind::Shell: + aNodeUidCount += myShells.Nb(); + break; + case BRepGraph_NodeId::Kind::Solid: + aNodeUidCount += mySolids.Nb(); + break; + case BRepGraph_NodeId::Kind::Compound: + aNodeUidCount += myCompounds.Nb(); + break; + case BRepGraph_NodeId::Kind::CompSolid: + aNodeUidCount += myCompSolids.Nb(); + break; + case BRepGraph_NodeId::Kind::Product: + aNodeUidCount += myProducts.Nb(); + break; + case BRepGraph_NodeId::Kind::Occurrence: + aNodeUidCount += myOccurrences.Nb(); break; default: break; } } - - // CompSolid -> Solid reverse map via solid ref entries. - for (BRepGraph_SolidRefId aSolidRefId = BRepGraph_SolidRefId::Start(); - aSolidRefId.IsValid(mySolidRefs.Nb()); - ++aSolidRefId) + myUIDToNodeId.Reserve(aNodeUidCount); + for (const BRepGraph_NodeId::Kind aKind : aKinds) { - const BRepGraphInc::SolidRef& aRef = mySolidRefs.Get(aSolidRefId); - if (aRef.IsRemoved || !aRef.ParentId.IsValid() - || aRef.ParentId.NodeKind != BRepGraph_NodeId::Kind::CompSolid - || !aRef.SolidDefId.IsValid()) - { - continue; - } - if (!aRef.ParentId.IsValid(myCompSolids.Nb()) || !aRef.SolidDefId.IsValid(mySolids.Nb())) - { - return false; - } - - const BRepGraphInc::CompSolidDef& aCompSolid = - myCompSolids.Get(BRepGraph_CompSolidId(aRef.ParentId.Index)); - if (aCompSolid.IsRemoved || mySolids.Get(aRef.SolidDefId).IsRemoved) - { - continue; - } - if (!containsNodeIndex(myReverseIdx.CompSolidsOfSolid(aRef.SolidDefId), aRef.ParentId.Index)) - { - return false; - } - } - - // Occurrence -> Product reverse map. - BRepGraph_OccurrenceId anOccurrenceId(0); - for (const BRepGraphInc::OccurrenceDef& anOcc : myOccurrences.Entities) - { - if (!anOcc.IsRemoved) - { - if (!anOcc.ChildDefId.IsValid()) + // Iterate all entities of this kind and read inline UID field. + const auto bindFromStore = [&](const auto& theStore, const BRepGraph_NodeId::Kind theKind) { + for (uint32_t i = 0; i < theStore.Nb(); ++i) { - return false; - } - if (anOcc.ChildDefId.NodeKind == BRepGraph_NodeId::Kind::Product) - { - if (!anOcc.ChildDefId.IsValid(myProducts.Nb())) + const BRepGraph_NodeId aNodeId(theKind, i); + // Skip removed entities - their UIDs should not resolve. + if (theStore.RemovedFlags.Test(i)) { - return false; + continue; } - if (!containsNodeIndex( - myReverseIdx.OccurrencesOfProduct(BRepGraph_ProductId(anOcc.ChildDefId.Index)), - anOccurrenceId.Index)) + const uint32_t aUidCounter = theStore.Entities.Value(static_cast(i)).UID; + if (aUidCounter > 0) { - return false; + const BRepGraph_UID aUID(theKind, aUidCounter); + myUIDToNodeId.Bind(aUID, aNodeId); } } - } - ++anOccurrenceId; - } - - // Reverse Product -> Occurrences entries must point to active occurrences - // that reference the same product. - for (BRepGraph_ProductId aProductId = BRepGraph_ProductId::Start(); - aProductId.IsValid(myProducts.Nb()); - ++aProductId) - { - const NCollection_DynamicArray* anOccs = - myReverseIdx.OccurrencesOfProduct(aProductId); - if (anOccs == nullptr) + }; + switch (aKind) { - continue; - } - for (const BRepGraph_OccurrenceId& anOccElem : *anOccs) - { - if (!anOccElem.IsValid(myOccurrences.Nb())) - { - return false; - } - const BRepGraphInc::OccurrenceDef& anOcc = myOccurrences.Get(anOccElem); - if (anOcc.IsRemoved || !anOcc.ChildDefId.IsValid() - || anOcc.ChildDefId.NodeKind != BRepGraph_NodeId::Kind::Product - || anOcc.ChildDefId != aProductId) - { - return false; - } + case BRepGraph_NodeId::Kind::Vertex: + bindFromStore(myVertices, aKind); + break; + case BRepGraph_NodeId::Kind::Edge: + bindFromStore(myEdges, aKind); + break; + case BRepGraph_NodeId::Kind::CoEdge: + bindFromStore(myCoEdges, aKind); + break; + case BRepGraph_NodeId::Kind::Wire: + bindFromStore(myWires, aKind); + break; + case BRepGraph_NodeId::Kind::Face: + bindFromStore(myFaces, aKind); + break; + case BRepGraph_NodeId::Kind::Shell: + bindFromStore(myShells, aKind); + break; + case BRepGraph_NodeId::Kind::Solid: + bindFromStore(mySolids, aKind); + break; + case BRepGraph_NodeId::Kind::Compound: + bindFromStore(myCompounds, aKind); + break; + case BRepGraph_NodeId::Kind::CompSolid: + bindFromStore(myCompSolids, aKind); + break; + case BRepGraph_NodeId::Kind::Product: + bindFromStore(myProducts, aKind); + break; + case BRepGraph_NodeId::Kind::Occurrence: + bindFromStore(myOccurrences, aKind); + break; + default: + break; } } + myUIDToNodeIdDirty = false; +} - return true; +//================================================================================================= + +void BRepGraphInc_Storage::EnsureRefUIDReverseIndex() const +{ + std::unique_lock aLock(myRefUIDToRefIdMutex); + if (!myRefUIDToRefIdDirty) + { + return; + } + + myRefUIDToRefId.Clear(); + const BRepGraph_RefId::Kind aKinds[] = {BRepGraph_RefId::Kind::Shell, + BRepGraph_RefId::Kind::Face, + BRepGraph_RefId::Kind::Wire, + BRepGraph_RefId::Kind::Vertex, + BRepGraph_RefId::Kind::Solid, + BRepGraph_RefId::Kind::Child, + BRepGraph_RefId::Kind::Occurrence}; + size_t aRefUidCount = 0; + for (const BRepGraph_RefId::Kind aKind : aKinds) + { + switch (aKind) + { + case BRepGraph_RefId::Kind::Shell: + aRefUidCount += myShellRefs.Nb(); + break; + case BRepGraph_RefId::Kind::Face: + aRefUidCount += myFaceRefs.Nb(); + break; + case BRepGraph_RefId::Kind::Wire: + aRefUidCount += myWireRefs.Nb(); + break; + case BRepGraph_RefId::Kind::Vertex: + aRefUidCount += myVertexRefs.Nb(); + break; + case BRepGraph_RefId::Kind::Solid: + aRefUidCount += mySolidRefs.Nb(); + break; + case BRepGraph_RefId::Kind::Child: + aRefUidCount += myChildRefs.Nb(); + break; + case BRepGraph_RefId::Kind::Occurrence: + aRefUidCount += myOccurrenceRefs.Nb(); + break; + default: + break; + } + } + myRefUIDToRefId.Reserve(aRefUidCount); + for (const BRepGraph_RefId::Kind aKind : aKinds) + { + const auto bindFromStore = [&](const auto& theStore, const BRepGraph_RefId::Kind theKind) { + for (uint32_t i = 0; i < theStore.Nb(); ++i) + { + const BRepGraph_RefId aRefId(theKind, i); + // Skip removed refs - their UIDs should not resolve. + if (theStore.RemovedFlags.Test(i)) + { + continue; + } + const uint32_t aUidCounter = theStore.Refs.Value(static_cast(i)).UID; + if (aUidCounter > 0) + { + const BRepGraph_RefUID aUID(theKind, aUidCounter); + myRefUIDToRefId.Bind(aUID, aRefId); + } + } + }; + switch (aKind) + { + case BRepGraph_RefId::Kind::Shell: + bindFromStore(myShellRefs, aKind); + break; + case BRepGraph_RefId::Kind::Face: + bindFromStore(myFaceRefs, aKind); + break; + case BRepGraph_RefId::Kind::Wire: + bindFromStore(myWireRefs, aKind); + break; + case BRepGraph_RefId::Kind::Vertex: + bindFromStore(myVertexRefs, aKind); + break; + case BRepGraph_RefId::Kind::Solid: + bindFromStore(mySolidRefs, aKind); + break; + case BRepGraph_RefId::Kind::Child: + bindFromStore(myChildRefs, aKind); + break; + case BRepGraph_RefId::Kind::Occurrence: + bindFromStore(myOccurrenceRefs, aKind); + break; + default: + break; + } + } + myRefUIDToRefIdDirty = false; +} + +//================================================================================================= + +uint32_t BRepGraphInc_Storage::NbActiveEdgeCurves3D() const +{ + return myEdgeCurves3D.NbActive; +} + +//================================================================================================= + +uint32_t BRepGraphInc_Storage::NbActiveCoEdgeCurves2D() const +{ + return myCoEdgeCurves2D.NbActive; +} + +//================================================================================================= + +uint32_t BRepGraphInc_Storage::NbActiveFaceSurfaces() const +{ + return myFaceSurfaces.NbActive; +} + +//================================================================================================= + +uint32_t BRepGraphInc_Storage::NbActiveFaceTriangulations() const +{ + return myFaceTriangulations.NbActive; +} + +//================================================================================================= + +uint32_t BRepGraphInc_Storage::NbActiveEdgePolygons3D() const +{ + return myEdgePolygons3D.NbActive; +} + +//================================================================================================= + +uint32_t BRepGraphInc_Storage::NbActiveCoEdgePolygons2D() const +{ + return myCoEdgePolygons2D.NbActive; +} + +//================================================================================================= + +uint32_t BRepGraphInc_Storage::NbActiveCoEdgePolygonsOnTri() const +{ + return myCoEdgePolygonsOnTri.NbActive; } diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.hxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.hxx index 791bf56e02..cfd551d43b 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.hxx +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.hxx @@ -14,52 +14,183 @@ #ifndef _BRepGraphInc_Storage_HeaderFile #define _BRepGraphInc_Storage_HeaderFile +#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 + //! @brief Central backend storage container for the incidence-table topology model. //! //! Holds all entity vectors (Vertex through Occurrence), representation -//! vectors (Surface, Curve3D, Curve2D, Triangulation, Polygon), reverse -//! indices for O(1) upward navigation, TShape deduplication maps, original +//! vectors (Surface, Curve3D, Curve2D, Triangulation, Polygon), relation +//! tables for connectivity navigation, TShape deduplication maps, original //! shape bindings, and per-kind UID vectors. Provides typed accessors //! enforcing compile-time safety for backend code. External callers should //! normally use the BRepGraph facade rather than reaching into this storage //! directly. BRepGraphInc_Populate has friend access for efficient bulk writes //! during graph population. -class BRepGraph_Builder; - class BRepGraphInc_Storage { public: DEFINE_STANDARD_ALLOC - //! Construct with allocator for internal collections. - //! If null, uses CommonBaseAllocator. - Standard_EXPORT explicit BRepGraphInc_Storage( - const occ::handle& theAlloc = - occ::handle()); + //! Gen-validated shape cache entry. + struct CachedShape + { + //! Reconstructed shape cached for a node id. + TopoDS_Shape Shape; - //! Return the allocator used for internal collections. + //! Subtree generation captured when the cached shape was built. + uint32_t StoredSubtreeGen = 0; + }; + + //! Construct an empty storage with no entities or representations. + Standard_EXPORT BRepGraphInc_Storage(); + + //! Clear allocator-backed containers before member destructors walk them. + Standard_EXPORT ~BRepGraphInc_Storage(); + + //! Return the allocator used for backend storage. [[nodiscard]] const occ::handle& Allocator() const { return myAllocator; } + //! Return products not referenced by any active occurrence. + [[nodiscard]] const NCollection_LinearVector& RootProductIds() const + { + return myRootProductIds; + } + + //! Return products not referenced by any active occurrence. + NCollection_LinearVector& ChangeRootProductIds() { return myRootProductIds; } + + //! Return nodes accumulated during deferred invalidation. + [[nodiscard]] const NCollection_LinearVector& DeferredModified() const + { + return myDeferredModified; + } + + //! Return nodes accumulated during deferred invalidation. + NCollection_LinearVector& ChangeDeferredModified() + { + return myDeferredModified; + } + + //! Return refs accumulated during deferred invalidation. + [[nodiscard]] const NCollection_LinearVector& DeferredRefModified() const + { + return myDeferredRefModified; + } + + //! Return refs accumulated during deferred invalidation. + NCollection_LinearVector& ChangeDeferredRefModified() + { + return myDeferredRefModified; + } + + //! Return true when the graph contains no topology definitions. + //! Checks whether any node kind (Vertex, Edge, Wire, Face, Shell, Solid, + //! Compound, CompSolid, Product, Occurrence) has been allocated. + [[nodiscard]] Standard_EXPORT bool IsEmpty() const; + + //! Return the next UID counter for a given node kind. + [[nodiscard]] Standard_EXPORT uint32_t NextNodeUIDCounter(BRepGraph_NodeId::Kind theKind) const; + + //! Override the next UID counter for a given node kind. + Standard_EXPORT void SetNextNodeUIDCounter(BRepGraph_NodeId::Kind theKind, uint32_t theCounter); + + //! Return the next UID counter for a given reference kind. + [[nodiscard]] Standard_EXPORT uint32_t NextRefUIDCounter(BRepGraph_RefId::Kind theKind) const; + + //! Override the next UID counter for a given reference kind. + Standard_EXPORT void SetNextRefUIDCounter(BRepGraph_RefId::Kind theKind, uint32_t theCounter); + + //! Allocate a node UID: write counter into the entity, bind reverse map, advance counter. + Standard_EXPORT BRepGraph_UID AllocateNodeUID(BRepGraph_NodeId theNodeId); + + //! Allocate a reference UID: write counter into the ref, bind reverse map, advance counter. + Standard_EXPORT BRepGraph_RefUID AllocateRefUID(BRepGraph_RefId theRefId); + + //! Return the current graph generation used by VersionStamp staleness checks. + [[nodiscard]] uint32_t Generation() const { return myGeneration.load(std::memory_order_relaxed); } + + //! Override the current graph generation. + void SetGeneration(const uint32_t theGeneration) + { + myGeneration.store(theGeneration, std::memory_order_relaxed); + } + + //! Increment the graph generation after a structural mutation batch. + void IncrementGeneration() { myGeneration.fetch_add(1, std::memory_order_relaxed); } + + //! Return the stable graph instance GUID. + [[nodiscard]] const Standard_GUID& GraphGUID() const { return myGraphGUID; } + + //! Override the stable graph instance GUID. + void SetGraphGUID(const Standard_GUID& theGuid) { myGraphGUID = theGuid; } + + //! Return whether invalidation is currently deferred. + [[nodiscard]] bool DeferredMode() const { return myDeferredMode.load(std::memory_order_relaxed); } + + //! Enable or disable deferred invalidation mode. + void SetDeferredMode(const bool theEnabled) + { + myDeferredMode.store(theEnabled, std::memory_order_relaxed); + } + + //! Return the current propagation wave id used to avoid revisiting parents. + [[nodiscard]] uint32_t PropagationWave() const + { + return myPropagationWave.load(std::memory_order_relaxed); + } + + //! Increment the propagation wave and return the new value. + [[nodiscard]] uint32_t AdvancePropagationWave() + { + return myPropagationWave.fetch_add(1, std::memory_order_relaxed) + 1; + } + + //! Increment the propagation wave without reading it back. + void IncrementPropagationWave() { myPropagationWave.fetch_add(1, std::memory_order_relaxed); } + + //! Return the recursion depth of the active RemoveSubgraph cascade. + [[nodiscard]] uint32_t RemoveSubgraphDepth() const { return myRemoveSubgraphDepth; } + + //! Enter one nested RemoveSubgraph scope. + void IncrementRemoveSubgraphDepth() { ++myRemoveSubgraphDepth; } + + //! Leave one nested RemoveSubgraph scope. + void DecrementRemoveSubgraphDepth() + { + Standard_ASSERT_VOID(myRemoveSubgraphDepth > 0, "RemoveSubgraphDepth underflow"); + if (myRemoveSubgraphDepth > 0) + { + --myRemoveSubgraphDepth; + } + } + //! Returns the total number of vertex entities (including removed). [[nodiscard]] uint32_t NbVertices() const { return myVertices.Nb(); } @@ -102,9 +233,6 @@ public: //! Returns the total number of wire reference entries (including removed). [[nodiscard]] uint32_t NbWireRefs() const { return myWireRefs.Nb(); } - //! Returns the total number of coedge reference entries (including removed). - [[nodiscard]] uint32_t NbCoEdgeRefs() const { return myCoEdgeRefs.Nb(); } - //! Returns the total number of vertex reference entries (including removed). [[nodiscard]] uint32_t NbVertexRefs() const { return myVertexRefs.Nb(); } @@ -117,48 +245,6 @@ public: //! Returns the total number of occurrence reference entries (including removed). [[nodiscard]] uint32_t NbOccurrenceRefs() const { return myOccurrenceRefs.Nb(); } - //! Returns the total number of surface representations. - [[nodiscard]] uint32_t NbSurfaces() const { return mySurfaces.Nb(); } - - //! Returns the total number of 3D curve representations. - [[nodiscard]] uint32_t NbCurves3D() const { return myCurves3D.Nb(); } - - //! Returns the total number of 2D curve representations. - [[nodiscard]] uint32_t NbCurves2D() const { return myCurves2D.Nb(); } - - //! Returns the total number of triangulation representations. - [[nodiscard]] uint32_t NbTriangulations() const { return myTriangulationsRep.Nb(); } - - //! Returns the total number of 3D polygon representations. - [[nodiscard]] uint32_t NbPolygons3D() const { return myPolygons3D.Nb(); } - - //! Returns the total number of 2D polygon representations. - [[nodiscard]] uint32_t NbPolygons2D() const { return myPolygons2D.Nb(); } - - //! Returns the total number of polygon-on-triangulation representations. - [[nodiscard]] uint32_t NbPolygonsOnTri() const { return myPolygonsOnTri.Nb(); } - - //! Returns the number of active surface representations (excluding removed). - [[nodiscard]] uint32_t NbActiveSurfaces() const { return mySurfaces.NbActive; } - - //! Returns the number of active 3D curve representations (excluding removed). - [[nodiscard]] uint32_t NbActiveCurves3D() const { return myCurves3D.NbActive; } - - //! Returns the number of active 2D curve representations (excluding removed). - [[nodiscard]] uint32_t NbActiveCurves2D() const { return myCurves2D.NbActive; } - - //! Returns the number of active triangulation representations (excluding removed). - [[nodiscard]] uint32_t NbActiveTriangulations() const { return myTriangulationsRep.NbActive; } - - //! Returns the number of active 3D polygon representations (excluding removed). - [[nodiscard]] uint32_t NbActivePolygons3D() const { return myPolygons3D.NbActive; } - - //! Returns the number of active 2D polygon representations (excluding removed). - [[nodiscard]] uint32_t NbActivePolygons2D() const { return myPolygons2D.NbActive; } - - //! Returns the number of active polygon-on-triangulation representations (excluding removed). - [[nodiscard]] uint32_t NbActivePolygonsOnTri() const { return myPolygonsOnTri.NbActive; } - //! Returns the number of active vertex entities (excluding removed). [[nodiscard]] uint32_t NbActiveVertices() const { return myVertices.NbActive; } @@ -201,9 +287,6 @@ public: //! Returns the number of active wire reference entries (excluding removed). [[nodiscard]] uint32_t NbActiveWireRefs() const { return myWireRefs.NbActive; } - //! Returns the number of active coedge reference entries (excluding removed). - [[nodiscard]] uint32_t NbActiveCoEdgeRefs() const { return myCoEdgeRefs.NbActive; } - //! Returns the number of active vertex reference entries (excluding removed). [[nodiscard]] uint32_t NbActiveVertexRefs() const { return myVertexRefs.NbActive; } @@ -226,137 +309,178 @@ public: //! @return true if the ref transitioned from active to removed Standard_EXPORT bool MarkRemovedRef(const BRepGraph_RefId theRefId); - //! Mark a representation entry as removed and decrement its active counter once. - //! @param[in] theRepId typed representation id - //! @return true if the representation transitioned from active to removed - Standard_EXPORT bool MarkRemovedRep(const BRepGraph_RepId theRepId); + //! Returns the number of edge 3D curve use records. + [[nodiscard]] uint32_t NbEdgeCurves3D() const { return myEdgeCurves3D.Nb(); } - //! Returns the surface representation at the given typed id. - //! @param[in] theRep typed surface representation id - [[nodiscard]] const BRepGraphInc::SurfaceRep& SurfaceRep( - const BRepGraph_SurfaceRepId theRep) const + //! Returns the number of edge 3D polygon use records. + [[nodiscard]] uint32_t NbEdgePolygons3D() const { return myEdgePolygons3D.Nb(); } + + //! Returns the number of coedge 2D curve use records. + [[nodiscard]] uint32_t NbCoEdgeCurves2D() const { return myCoEdgeCurves2D.Nb(); } + + //! Returns the number of coedge 2D polygon use records. + [[nodiscard]] uint32_t NbCoEdgePolygons2D() const { return myCoEdgePolygons2D.Nb(); } + + //! Returns the number of coedge polygon-on-triangulation use records. + [[nodiscard]] uint32_t NbCoEdgePolygonsOnTri() const { return myCoEdgePolygonsOnTri.Nb(); } + + //! Returns the number of face surface use records. + [[nodiscard]] uint32_t NbFaceSurfaces() const { return myFaceSurfaces.Nb(); } + + //! Returns the number of face triangulation use records. + [[nodiscard]] uint32_t NbFaceTriangulations() const { return myFaceTriangulations.Nb(); } + + //! Returns the number of active (parent-valid) edge 3D curve use records. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveEdgeCurves3D() const; + + //! Returns the number of active (parent-valid) coedge 2D curve use records. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveCoEdgeCurves2D() const; + + //! Returns the number of active (parent-valid) face surface use records. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveFaceSurfaces() const; + + //! Returns the number of active (parent-valid) face triangulation use records. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveFaceTriangulations() const; + + //! Returns the number of active (parent-valid) edge 3D polygon use records. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveEdgePolygons3D() const; + + //! Returns the number of active (parent-valid) coedge 2D polygon use records. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveCoEdgePolygons2D() const; + + //! Returns the number of active (parent-valid) coedge polygon-on-triangulation use records. + [[nodiscard]] Standard_EXPORT uint32_t NbActiveCoEdgePolygonsOnTri() const; + + //! Returns the edge 3D curve use at the given id. + [[nodiscard]] const BRepGraphInc::EdgeCurve3DRep& EdgeCurve3DRep( + const BRepGraph_EdgeCurve3DRepId theId) const { - return mySurfaces.Get(theRep); + return myEdgeCurves3D.Get(theId); } - //! Returns the 3D curve representation at the given typed id. - //! @param[in] theRep typed curve-3D representation id - [[nodiscard]] const BRepGraphInc::Curve3DRep& Curve3DRep( - const BRepGraph_Curve3DRepId theRep) const + //! Returns a mutable reference to the edge 3D curve use at the given id. + BRepGraphInc::EdgeCurve3DRep& ChangeEdgeCurve3DRep(const BRepGraph_EdgeCurve3DRepId theId) { - return myCurves3D.Get(theRep); + return myEdgeCurves3D.Change(theId); } - //! Returns the 2D curve representation at the given typed id. - //! @param[in] theRep typed curve-2D representation id - [[nodiscard]] const BRepGraphInc::Curve2DRep& Curve2DRep( - const BRepGraph_Curve2DRepId theRep) const + //! Returns the edge 3D polygon use at the given id. + [[nodiscard]] const BRepGraphInc::EdgePolygon3DRep& EdgePolygon3DRep( + const BRepGraph_EdgePolygon3DRepId theId) const { - return myCurves2D.Get(theRep); + return myEdgePolygons3D.Get(theId); } - //! Returns the triangulation representation at the given typed id. - //! @param[in] theRep typed triangulation representation id - [[nodiscard]] const BRepGraphInc::TriangulationRep& TriangulationRep( - const BRepGraph_TriangulationRepId theRep) const + //! Returns a mutable reference to the edge 3D polygon use at the given id. + BRepGraphInc::EdgePolygon3DRep& ChangeEdgePolygon3DRep(const BRepGraph_EdgePolygon3DRepId theId) { - return myTriangulationsRep.Get(theRep); + return myEdgePolygons3D.Change(theId); } - //! Returns the 3D polygon representation at the given typed id. - //! @param[in] theRep typed polygon-3D representation id - [[nodiscard]] const BRepGraphInc::Polygon3DRep& Polygon3DRep( - const BRepGraph_Polygon3DRepId theRep) const + //! Returns the coedge 2D curve use at the given id. + [[nodiscard]] const BRepGraphInc::CoEdgeCurve2DRep& CoEdgeCurve2DRep( + const BRepGraph_CoEdgeCurve2DRepId theId) const { - return myPolygons3D.Get(theRep); + return myCoEdgeCurves2D.Get(theId); } - //! Returns the 2D polygon representation at the given typed id. - //! @param[in] theRep typed polygon-2D representation id - [[nodiscard]] const BRepGraphInc::Polygon2DRep& Polygon2DRep( - const BRepGraph_Polygon2DRepId theRep) const + //! Returns a mutable reference to the coedge 2D curve use at the given id. + BRepGraphInc::CoEdgeCurve2DRep& ChangeCoEdgeCurve2DRep(const BRepGraph_CoEdgeCurve2DRepId theId) { - return myPolygons2D.Get(theRep); + return myCoEdgeCurves2D.Change(theId); } - //! Returns the polygon-on-triangulation representation at the given typed id. - //! @param[in] theRep typed polygon-on-triangulation representation id - [[nodiscard]] const BRepGraphInc::PolygonOnTriRep& PolygonOnTriRep( - const BRepGraph_PolygonOnTriRepId theRep) const + //! Returns the coedge 2D polygon use at the given id. + [[nodiscard]] const BRepGraphInc::CoEdgePolygon2DRep& CoEdgePolygon2DRep( + const BRepGraph_CoEdgePolygon2DRepId theId) const { - return myPolygonsOnTri.Get(theRep); + return myCoEdgePolygons2D.Get(theId); } - //! Returns a mutable reference to the surface representation at the given typed id. - //! @param[in] theRep typed surface representation id - BRepGraphInc::SurfaceRep& ChangeSurfaceRep(const BRepGraph_SurfaceRepId theRep) + //! Returns a mutable reference to the coedge 2D polygon use at the given id. + BRepGraphInc::CoEdgePolygon2DRep& ChangeCoEdgePolygon2DRep( + const BRepGraph_CoEdgePolygon2DRepId theId) { - return mySurfaces.Change(theRep); + return myCoEdgePolygons2D.Change(theId); } - //! Returns a mutable reference to the 3D curve representation at the given typed id. - //! @param[in] theRep typed curve-3D representation id - BRepGraphInc::Curve3DRep& ChangeCurve3DRep(const BRepGraph_Curve3DRepId theRep) + //! Returns the coedge polygon-on-triangulation use at the given id. + [[nodiscard]] const BRepGraphInc::CoEdgePolygonOnTriRep& CoEdgePolygonOnTriRep( + const BRepGraph_CoEdgePolygonOnTriRepId theId) const { - return myCurves3D.Change(theRep); + return myCoEdgePolygonsOnTri.Get(theId); } - //! Returns a mutable reference to the 2D curve representation at the given typed id. - //! @param[in] theRep typed curve-2D representation id - BRepGraphInc::Curve2DRep& ChangeCurve2DRep(const BRepGraph_Curve2DRepId theRep) + //! Returns a mutable reference to the coedge polygon-on-triangulation use at the given id. + BRepGraphInc::CoEdgePolygonOnTriRep& ChangeCoEdgePolygonOnTriRep( + const BRepGraph_CoEdgePolygonOnTriRepId theId) { - return myCurves2D.Change(theRep); + return myCoEdgePolygonsOnTri.Change(theId); } - //! Returns a mutable reference to the triangulation representation at the given typed id. - //! @param[in] theRep typed triangulation representation id - BRepGraphInc::TriangulationRep& ChangeTriangulationRep(const BRepGraph_TriangulationRepId theRep) + //! Returns the face surface use at the given id. + [[nodiscard]] const BRepGraphInc::FaceSurfaceRep& FaceSurfaceRep( + const BRepGraph_FaceSurfaceRepId theId) const { - return myTriangulationsRep.Change(theRep); + return myFaceSurfaces.Get(theId); } - //! Returns a mutable reference to the 3D polygon representation at the given typed id. - //! @param[in] theRep typed polygon-3D representation id - BRepGraphInc::Polygon3DRep& ChangePolygon3DRep(const BRepGraph_Polygon3DRepId theRep) + //! Returns a mutable reference to the face surface use at the given id. + BRepGraphInc::FaceSurfaceRep& ChangeFaceSurfaceRep(const BRepGraph_FaceSurfaceRepId theId) { - return myPolygons3D.Change(theRep); + return myFaceSurfaces.Change(theId); } - //! Returns a mutable reference to the 2D polygon representation at the given typed id. - //! @param[in] theRep typed polygon-2D representation id - BRepGraphInc::Polygon2DRep& ChangePolygon2DRep(const BRepGraph_Polygon2DRepId theRep) + //! Returns the face triangulation use at the given id. + [[nodiscard]] const BRepGraphInc::FaceTriangulationRep& FaceTriangulationRep( + const BRepGraph_FaceTriangulationRepId theId) const { - return myPolygons2D.Change(theRep); + return myFaceTriangulations.Get(theId); } - //! Returns a mutable reference to the polygon-on-triangulation representation at the given typed - //! id. - //! @param[in] theRep typed polygon-on-triangulation representation id - BRepGraphInc::PolygonOnTriRep& ChangePolygonOnTriRep(const BRepGraph_PolygonOnTriRepId theRep) + //! Returns a mutable reference to the face triangulation use at the given id. + BRepGraphInc::FaceTriangulationRep& ChangeFaceTriangulationRep( + const BRepGraph_FaceTriangulationRepId theId) { - return myPolygonsOnTri.Change(theRep); + return myFaceTriangulations.Change(theId); } - //! Appends a new surface representation slot and returns its typed id. - BRepGraph_SurfaceRepId AppendSurfaceRep() { return mySurfaces.Append(); } + //! Appends a new edge 3D curve use record and returns its id. + BRepGraph_EdgeCurve3DRepId AppendEdgeCurve3DRep() { return myEdgeCurves3D.Append(); } - //! Appends a new 3D curve representation slot and returns its typed id. - BRepGraph_Curve3DRepId AppendCurve3DRep() { return myCurves3D.Append(); } + //! Appends a new edge 3D polygon use record and returns its id. + BRepGraph_EdgePolygon3DRepId AppendEdgePolygon3DRep() { return myEdgePolygons3D.Append(); } - //! Appends a new 2D curve representation slot and returns its typed id. - BRepGraph_Curve2DRepId AppendCurve2DRep() { return myCurves2D.Append(); } + //! Appends a new coedge 2D curve use record and returns its id. + BRepGraph_CoEdgeCurve2DRepId AppendCoEdgeCurve2DRep() { return myCoEdgeCurves2D.Append(); } - //! Appends a new triangulation representation slot and returns its typed id. - BRepGraph_TriangulationRepId AppendTriangulationRep() { return myTriangulationsRep.Append(); } + //! Appends a new coedge 2D polygon use record and returns its id. + BRepGraph_CoEdgePolygon2DRepId AppendCoEdgePolygon2DRep() { return myCoEdgePolygons2D.Append(); } - //! Appends a new 3D polygon representation slot and returns its typed id. - BRepGraph_Polygon3DRepId AppendPolygon3DRep() { return myPolygons3D.Append(); } + //! Appends a new coedge polygon-on-triangulation use record and returns its id. + BRepGraph_CoEdgePolygonOnTriRepId AppendCoEdgePolygonOnTriRep() + { + return myCoEdgePolygonsOnTri.Append(); + } - //! Appends a new 2D polygon representation slot and returns its typed id. - BRepGraph_Polygon2DRepId AppendPolygon2DRep() { return myPolygons2D.Append(); } + //! Appends a new face surface use record and returns its id. + BRepGraph_FaceSurfaceRepId AppendFaceSurfaceRep() { return myFaceSurfaces.Append(); } - //! Appends a new polygon-on-triangulation representation slot and returns its typed id. - BRepGraph_PolygonOnTriRepId AppendPolygonOnTriRep() { return myPolygonsOnTri.Append(); } + //! Appends a new face triangulation use record and returns its id. + BRepGraph_FaceTriangulationRepId AppendFaceTriangulationRep() + { + return myFaceTriangulations.Append(); + } + + //! Mark a representation-use record as removed and decrement its active counter once. + //! @param[in] theRepId typed use id + //! @return true if the use transitioned from active to removed + Standard_EXPORT bool MarkRemoved(const BRepGraph_RepId theRepId); + + //! Set or clear the soft-removal flag for a representation-use record. + //! @param[in] theRepId typed use id + //! @param[in] theVal true to mark removed, false to mark active + Standard_EXPORT void SetRemoved(const BRepGraph_RepId theRepId, const bool theVal); //! Returns the vertex entity at the given typed id. //! @param[in] theVertex typed vertex id @@ -456,12 +580,6 @@ public: return myWireRefs.Get(theRefId); } - //! Returns the coedge reference entry at the given typed id. - [[nodiscard]] const BRepGraphInc::CoEdgeRef& CoEdgeRef(const BRepGraph_CoEdgeRefId theRefId) const - { - return myCoEdgeRefs.Get(theRefId); - } - //! Returns the vertex reference entry at the given typed id. [[nodiscard]] const BRepGraphInc::VertexRef& VertexRef(const BRepGraph_VertexRefId theRefId) const { @@ -582,12 +700,6 @@ public: return myWireRefs.Change(theRefId); } - //! Returns a mutable reference to the coedge reference entry at the given typed id. - BRepGraphInc::CoEdgeRef& ChangeCoEdgeRef(const BRepGraph_CoEdgeRefId theRefId) - { - return myCoEdgeRefs.Change(theRefId); - } - //! Returns a mutable reference to the vertex reference entry at the given typed id. BRepGraphInc::VertexRef& ChangeVertexRef(const BRepGraph_VertexRefId theRefId) { @@ -612,38 +724,187 @@ public: return myOccurrenceRefs.Change(theRefId); } + //! Return the face relations for a given face identifier. + //! @param[in] theId face identifier + //! @return const reference to the face relation representation + [[nodiscard]] const BRepGraphInc::FaceRelations& FaceRelations(const BRepGraph_FaceId theId) const + { + return myFaceRelations.Value(static_cast(theId.Index)); + } + + //! Return the wire relations for a given wire identifier. + //! @param[in] theId wire identifier + //! @return const reference to the wire relation representation + [[nodiscard]] const BRepGraphInc::WireRelations& WireRelations(const BRepGraph_WireId theId) const + { + return myWireRelations.Value(static_cast(theId.Index)); + } + + //! Return the edge relations for a given edge identifier. + //! @param[in] theId edge identifier + //! @return const reference to the edge relation representation + [[nodiscard]] const BRepGraphInc::EdgeRelations& EdgeRelations(const BRepGraph_EdgeId theId) const + { + return myEdgeRelations.Value(static_cast(theId.Index)); + } + + //! Return the shell relations for a given shell identifier. + //! @param[in] theId shell identifier + //! @return const reference to the shell relation representation + [[nodiscard]] const BRepGraphInc::ShellRelations& ShellRelations( + const BRepGraph_ShellId theId) const + { + return myShellRelations.Value(static_cast(theId.Index)); + } + + //! Return the solid relations for a given solid identifier. + //! @param[in] theId solid identifier + //! @return const reference to the solid relation representation + [[nodiscard]] const BRepGraphInc::SolidRelations& SolidRelations( + const BRepGraph_SolidId theId) const + { + return mySolidRelations.Value(static_cast(theId.Index)); + } + + //! Return the compound relations for a given compound identifier. + //! @param[in] theId compound identifier + //! @return const reference to the compound relation representation + [[nodiscard]] const BRepGraphInc::CompoundRelations& CompoundRelations( + const BRepGraph_CompoundId theId) const + { + return myCompoundRelations.Value(static_cast(theId.Index)); + } + + //! Return the compsolid relations for a given compsolid identifier. + //! @param[in] theId compsolid identifier + //! @return const reference to the compsolid relation representation + [[nodiscard]] const BRepGraphInc::CompSolidRelations& CompSolidRelations( + const BRepGraph_CompSolidId theId) const + { + return myCompSolidRelations.Value(static_cast(theId.Index)); + } + + //! Return the vertex relations for a given vertex identifier. + //! @param[in] theId vertex identifier + //! @return const reference to the vertex relation representation + [[nodiscard]] const BRepGraphInc::VertexRelations& VertexRelations( + const BRepGraph_VertexId theId) const + { + return myVertexRelations.Value(static_cast(theId.Index)); + } + + //! Return the product relations for a given product identifier. + //! @param[in] theId product identifier + //! @return const reference to the product relation representation + [[nodiscard]] const BRepGraphInc::ProductRelations& ProductRelations( + const BRepGraph_ProductId theId) const + { + return myProductRelations.Value(static_cast(theId.Index)); + } + + //! Return the occurrence relations for a given occurrence identifier. + //! @param[in] theId occurrence identifier + //! @return const reference to the occurrence relation representation + [[nodiscard]] const BRepGraphInc::OccurrenceRelations& OccurrenceRelations( + const BRepGraph_OccurrenceId theId) const + { + return myOccurrenceRelations.Value(static_cast(theId.Index)); + } + + //! Return the compound child reference identifiers that point to a given node. + //! @param[in] theNode node identifier + //! @return const reference to the list of child reference identifiers + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& + CompoundRefsOfNode(const BRepGraph_NodeId theNode) const; + + //! Return the occurrence reference identifiers that point to a given node. + //! @param[in] theNode node identifier + //! @return const reference to the list of occurrence reference identifiers + [[nodiscard]] Standard_EXPORT const NCollection_LinearVector& + OccurrenceRefsOfNode(const BRepGraph_NodeId theNode) const; + //! Appends a new vertex entity and returns its typed id. - BRepGraph_VertexId AppendVertex() { return myVertices.Append(myAllocator); } + BRepGraph_VertexId AppendVertex() + { + const BRepGraph_VertexId anId = myVertices.Append(); + myVertexRelations.Appended(); + return anId; + } //! Appends a new edge entity and returns its typed id. - BRepGraph_EdgeId AppendEdge() { return myEdges.Append(myAllocator); } + BRepGraph_EdgeId AppendEdge() + { + const BRepGraph_EdgeId anId = myEdges.Append(); + myEdgeRelations.Appended(); + return anId; + } //! Appends a new coedge entity and returns its typed id. - BRepGraph_CoEdgeId AppendCoEdge() { return myCoEdges.Append(myAllocator); } + BRepGraph_CoEdgeId AppendCoEdge() { return myCoEdges.Append(); } //! Appends a new wire entity and returns its typed id. - BRepGraph_WireId AppendWire() { return myWires.Append(myAllocator); } + BRepGraph_WireId AppendWire() + { + const BRepGraph_WireId anId = myWires.Append(); + myWireRelations.Appended(); + return anId; + } //! Appends a new face entity and returns its typed id. - BRepGraph_FaceId AppendFace() { return myFaces.Append(myAllocator); } + BRepGraph_FaceId AppendFace() + { + const BRepGraph_FaceId anId = myFaces.Append(); + myFaceRelations.Appended(); + return anId; + } //! Appends a new shell entity and returns its typed id. - BRepGraph_ShellId AppendShell() { return myShells.Append(myAllocator); } + BRepGraph_ShellId AppendShell() + { + const BRepGraph_ShellId anId = myShells.Append(); + myShellRelations.Appended(); + return anId; + } //! Appends a new solid entity and returns its typed id. - BRepGraph_SolidId AppendSolid() { return mySolids.Append(myAllocator); } + BRepGraph_SolidId AppendSolid() + { + const BRepGraph_SolidId anId = mySolids.Append(); + mySolidRelations.Appended(); + return anId; + } //! Appends a new compound entity and returns its typed id. - BRepGraph_CompoundId AppendCompound() { return myCompounds.Append(myAllocator); } + BRepGraph_CompoundId AppendCompound() + { + const BRepGraph_CompoundId anId = myCompounds.Append(); + myCompoundRelations.Appended(); + return anId; + } //! Appends a new compsolid entity and returns its typed id. - BRepGraph_CompSolidId AppendCompSolid() { return myCompSolids.Append(myAllocator); } + BRepGraph_CompSolidId AppendCompSolid() + { + const BRepGraph_CompSolidId anId = myCompSolids.Append(); + myCompSolidRelations.Appended(); + return anId; + } //! Appends a new product entity and returns its typed id. - BRepGraph_ProductId AppendProduct() { return myProducts.Append(myAllocator); } + BRepGraph_ProductId AppendProduct() + { + const BRepGraph_ProductId anId = myProducts.Append(); + myProductRelations.Appended(); + return anId; + } //! Appends a new occurrence entity and returns its typed id. - BRepGraph_OccurrenceId AppendOccurrence() { return myOccurrences.Append(myAllocator); } + BRepGraph_OccurrenceId AppendOccurrence() + { + const BRepGraph_OccurrenceId anId = myOccurrences.Append(); + myOccurrenceRelations.Appended(); + return anId; + } //! Appends a new shell reference entry and returns its typed id. BRepGraph_ShellRefId AppendShellRef() { return myShellRefs.Append(); } @@ -654,9 +915,6 @@ public: //! Appends a new wire reference entry and returns its typed id. BRepGraph_WireRefId AppendWireRef() { return myWireRefs.Append(); } - //! Appends a new coedge reference entry and returns its typed id. - BRepGraph_CoEdgeRefId AppendCoEdgeRef() { return myCoEdgeRefs.Append(); } - //! Appends a new vertex reference entry and returns its typed id. BRepGraph_VertexRefId AppendVertexRef() { return myVertexRefs.Append(); } @@ -669,16 +927,279 @@ public: //! Appends a new occurrence reference entry and returns its typed id. BRepGraph_OccurrenceRefId AppendOccurrenceRef() { return myOccurrenceRefs.Append(); } - //! Return the per-kind UID vector for a given Kind. - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& UIDs( - const BRepGraph_NodeId::Kind theKind) const; + //! Create a coedge use record binding an edge to a wire within a face context. + //! @param[in] theParentWireId owning wire identifier + //! @param[in] theChildEdgeId referenced edge identifier + //! @param[in] theFaceId face context identifier + //! @param[in] theOrientation orientation of the coedge + //! @return the newly created coedge identifier + Standard_EXPORT BRepGraph_CoEdgeId + CreateCoEdgeUse(const BRepGraph_WireId theParentWireId, + const BRepGraph_EdgeId theChildEdgeId, + const BRepGraph_FaceId theFaceId, + const BRepGraphInc::ParityOrientation theOrientation); - //! Return the per-kind UID vector for a given Kind (mutable). - Standard_EXPORT NCollection_DynamicArray& ChangeUIDs( - const BRepGraph_NodeId::Kind theKind); + //! Attach an edge to a vertex by creating a vertex reference. + //! @param[in] theEdgeId edge identifier + //! @param[in] theVertexId vertex identifier + Standard_EXPORT void AttachEdgeToVertex(const BRepGraph_EdgeId theEdgeId, + const BRepGraph_VertexId theVertexId); - //! Clear all UID vectors (reset lengths to 0). - Standard_EXPORT void ResetAllUIDs(); + //! Attach a wire to a face by creating a wire reference. + //! @param[in] theParentFaceId parent face identifier + //! @param[in] theChildWireId child wire identifier + //! @param[in] theOrientation orientation within parent + //! @return the newly created wire reference identifier + Standard_EXPORT BRepGraph_WireRefId + AttachWireToFace(const BRepGraph_FaceId theParentFaceId, + const BRepGraph_WireId theChildWireId, + const BRepGraphInc::ParityOrientation theOrientation = TopAbs_FORWARD); + + //! Attach a face to a shell by creating a face reference. + //! @param[in] theParentShellId parent shell identifier + //! @param[in] theChildFaceId child face identifier + //! @param[in] theOrientation orientation within parent + //! @return the newly created face reference identifier + Standard_EXPORT BRepGraph_FaceRefId + AttachFaceToShell(const BRepGraph_ShellId theParentShellId, + const BRepGraph_FaceId theChildFaceId, + const BRepGraphInc::ParityOrientation theOrientation = TopAbs_FORWARD); + + //! Attach a shell to a solid by creating a shell reference. + //! @param[in] theParentSolidId parent solid identifier + //! @param[in] theChildShellId child shell identifier + //! @param[in] theOrientation orientation within parent + //! @return the newly created shell reference identifier + Standard_EXPORT BRepGraph_ShellRefId + AttachShellToSolid(const BRepGraph_SolidId theParentSolidId, + const BRepGraph_ShellId theChildShellId, + const BRepGraphInc::ParityOrientation theOrientation = TopAbs_FORWARD); + + //! Attach a solid to a compsolid by creating a solid reference. + //! @param[in] theParentCompSolidId parent compsolid identifier + //! @param[in] theChildSolidId child solid identifier + //! @param[in] theOrientation orientation within parent + //! @return the newly created solid reference identifier + Standard_EXPORT BRepGraph_SolidRefId + AttachSolidToCompSolid(const BRepGraph_CompSolidId theParentCompSolidId, + const BRepGraph_SolidId theChildSolidId, + const BRepGraphInc::ParityOrientation theOrientation = TopAbs_FORWARD); + + //! Attach a child node to a compound by creating a child reference. + //! @param[in] theParentCompoundId parent compound identifier + //! @param[in] theChildNodeId child node identifier + //! @param[in] theLocation optional location transformation + //! @param[in] theOrientation orientation within parent + //! @return the newly created child reference identifier + Standard_EXPORT BRepGraph_ChildRefId + AttachChildToCompound(const BRepGraph_CompoundId theParentCompoundId, + const BRepGraph_NodeId theChildNodeId, + const TopLoc_Location& theLocation = TopLoc_Location(), + const BRepGraphInc::ParityOrientation theOrientation = TopAbs_FORWARD); + + //! Attach an occurrence to a product by creating an occurrence reference. + //! @param[in] theParentProductId parent product identifier + //! @param[in] theChildOccurrenceId child occurrence identifier + //! @param[in] theLocation optional location transformation + //! @return the newly created occurrence reference identifier + Standard_EXPORT BRepGraph_OccurrenceRefId + AttachOccurrenceToProduct(const BRepGraph_ProductId theParentProductId, + const BRepGraph_OccurrenceId theChildOccurrenceId, + const TopLoc_Location& theLocation = TopLoc_Location()); + + //! Detach a coedge use from its parent wire. + //! @param[in] theParentWireId owning wire identifier + //! @param[in] theCoEdgeId coedge identifier to detach + //! @return true if the coedge was found and removed + Standard_EXPORT bool DetachCoEdgeUse(const BRepGraph_WireId theParentWireId, + const BRepGraph_CoEdgeId theCoEdgeId); + + //! Replace a single coedge with a pair of new coedges in a wire. + //! @param[in] theParentWireId owning wire identifier + //! @param[in] theOldCoEdgeId coedge to replace + //! @param[in] theNewFirstCoEdgeId first replacement coedge + //! @param[in] theNewSecondCoEdgeId second replacement coedge + //! @return true if the replacement succeeded + Standard_EXPORT bool ReplaceCoEdgeUseWithPair(const BRepGraph_WireId theParentWireId, + const BRepGraph_CoEdgeId theOldCoEdgeId, + const BRepGraph_CoEdgeId theNewFirstCoEdgeId, + const BRepGraph_CoEdgeId theNewSecondCoEdgeId); + + //! Detach a wire reference from its parent face. + //! @param[in] theParentFaceId parent face identifier + //! @param[in] theRefId wire reference identifier to detach + //! @return true if the reference was found and removed + Standard_EXPORT bool DetachWireFromFace(const BRepGraph_FaceId theParentFaceId, + const BRepGraph_WireRefId theRefId); + + //! Detach a face reference from its parent shell. + //! @param[in] theParentShellId parent shell identifier + //! @param[in] theRefId face reference identifier to detach + //! @return true if the reference was found and removed + Standard_EXPORT bool DetachFaceFromShell(const BRepGraph_ShellId theParentShellId, + const BRepGraph_FaceRefId theRefId); + + //! Detach a shell reference from its parent solid. + //! @param[in] theParentSolidId parent solid identifier + //! @param[in] theRefId shell reference identifier to detach + //! @return true if the reference was found and removed + Standard_EXPORT bool DetachShellFromSolid(const BRepGraph_SolidId theParentSolidId, + const BRepGraph_ShellRefId theRefId); + + //! Detach a solid reference from its parent compsolid. + //! @param[in] theParentCompSolidId parent compsolid identifier + //! @param[in] theRefId solid reference identifier to detach + //! @return true if the reference was found and removed + Standard_EXPORT bool DetachSolidFromCompSolid(const BRepGraph_CompSolidId theParentCompSolidId, + const BRepGraph_SolidRefId theRefId); + + //! Detach a child reference from its parent compound. + //! @param[in] theParentCompoundId parent compound identifier + //! @param[in] theRefId child reference identifier to detach + //! @return true if the reference was found and removed + Standard_EXPORT bool DetachChildFromCompound(const BRepGraph_CompoundId theParentCompoundId, + const BRepGraph_ChildRefId theRefId); + + //! Detach an occurrence reference from its parent product. + //! @param[in] theParentProductId parent product identifier + //! @param[in] theRefId occurrence reference identifier to detach + //! @return true if the reference was found and removed + Standard_EXPORT bool DetachOccurrenceFromProduct(const BRepGraph_ProductId theParentProductId, + const BRepGraph_OccurrenceRefId theRefId); + + //! Rebind the child node of an occurrence to a new node. + //! @param[in] theOccurrence occurrence identifier + //! @param[in] theOldChild old child node identifier + //! @param[in] theNewChild new child node identifier + Standard_EXPORT void RebindOccurrenceChild(const BRepGraph_OccurrenceId theOccurrence, + const BRepGraph_NodeId theOldChild, + const BRepGraph_NodeId theNewChild); + + //! Rebind vertex edge references from one vertex to another, excluding a specific ref. + //! @param[in] theOldVertex old vertex identifier + //! @param[in] theNewVertex new vertex identifier + //! @param[in] theEdge edge identifier + //! @param[in] theExcludingRef reference identifier to exclude from rebinding + Standard_EXPORT void RebindVertexEdge(const BRepGraph_VertexId theOldVertex, + const BRepGraph_VertexId theNewVertex, + const BRepGraph_EdgeId theEdge, + const BRepGraph_VertexRefId theExcludingRef); + + //! Rebind a vertex reference to point to a new vertex. + //! @param[in] theRefId vertex reference identifier + //! @param[in] theOldVertex old vertex identifier + //! @param[in] theNewVertex new vertex identifier + Standard_EXPORT void RebindVertexRef(const BRepGraph_VertexRefId theRefId, + const BRepGraph_VertexId theOldVertex, + const BRepGraph_VertexId theNewVertex); + + //! Rebind a coedge to reference a different edge. + //! @param[in] theCoEdge coedge identifier + //! @param[in] theOldEdge old edge identifier + //! @param[in] theNewEdge new edge identifier + Standard_EXPORT void RebindCoEdgeEdge(const BRepGraph_CoEdgeId theCoEdge, + const BRepGraph_EdgeId theOldEdge, + const BRepGraph_EdgeId theNewEdge); + + //! Rebind a wire reference to point to a new wire. + //! @param[in] theRefId wire reference identifier + //! @param[in] theOldWire old wire identifier + //! @param[in] theNewWire new wire identifier + Standard_EXPORT void RebindWireRef(const BRepGraph_WireRefId theRefId, + const BRepGraph_WireId theOldWire, + const BRepGraph_WireId theNewWire); + + //! Rebind a face reference to point to a new face. + //! @param[in] theRefId face reference identifier + //! @param[in] theOldFace old face identifier + //! @param[in] theNewFace new face identifier + Standard_EXPORT void RebindFaceRef(const BRepGraph_FaceRefId theRefId, + const BRepGraph_FaceId theOldFace, + const BRepGraph_FaceId theNewFace); + + //! Rebind a shell reference to point to a new shell. + //! @param[in] theRefId shell reference identifier + //! @param[in] theOldShell old shell identifier + //! @param[in] theNewShell new shell identifier + Standard_EXPORT void RebindShellRef(const BRepGraph_ShellRefId theRefId, + const BRepGraph_ShellId theOldShell, + const BRepGraph_ShellId theNewShell); + + //! Rebind a solid reference to point to a new solid. + //! @param[in] theRefId solid reference identifier + //! @param[in] theOldSolid old solid identifier + //! @param[in] theNewSolid new solid identifier + Standard_EXPORT void RebindSolidRef(const BRepGraph_SolidRefId theRefId, + const BRepGraph_SolidId theOldSolid, + const BRepGraph_SolidId theNewSolid); + + //! Rebind a child reference to point to a new child node. + //! @param[in] theRefId child reference identifier + //! @param[in] theOldChild old child node identifier + //! @param[in] theNewChild new child node identifier + Standard_EXPORT void RebindChildRef(const BRepGraph_ChildRefId theRefId, + const BRepGraph_NodeId theOldChild, + const BRepGraph_NodeId theNewChild); + + //! Rebind an occurrence reference to point to a new occurrence. + //! @param[in] theRefId occurrence reference identifier + //! @param[in] theOldOccurrence old occurrence identifier + //! @param[in] theNewOccurrence new occurrence identifier + Standard_EXPORT void RebindOccurrenceRef(const BRepGraph_OccurrenceRefId theRefId, + const BRepGraph_OccurrenceId theOldOccurrence, + const BRepGraph_OccurrenceId theNewOccurrence); + + //! Reverse the order of coedges in a wire. + //! @param[in] theWireId wire identifier + Standard_EXPORT void ReverseWireCoEdges(const BRepGraph_WireId theWireId); + + //! Replace the coedge list of a wire with a new set. + //! @param[in] theWireId wire identifier + //! @param[in] theCoEdgeIds new coedge identifiers + Standard_EXPORT void SetWireCoEdges(const BRepGraph_WireId theWireId, + const NCollection_Array1& theCoEdgeIds); + + //! Replace the wire reference list of a face with a new set. + //! @param[in] theFaceId face identifier + //! @param[in] theWireRefIds new wire reference identifiers + Standard_EXPORT void SetFaceWireRefs( + const BRepGraph_FaceId theFaceId, + const NCollection_Array1& theWireRefIds); + + //! Replace the face reference list of a shell with a new set. + //! @param[in] theShellId shell identifier + //! @param[in] theFaceRefIds new face reference identifiers + Standard_EXPORT void SetShellFaceRefs( + const BRepGraph_ShellId theShellId, + const NCollection_Array1& theFaceRefIds); + + //! Replace the shell reference list of a solid with a new set. + //! @param[in] theSolidId solid identifier + //! @param[in] theShellRefIds new shell reference identifiers + Standard_EXPORT void SetSolidShellRefs( + const BRepGraph_SolidId theSolidId, + const NCollection_Array1& theShellRefIds); + + //! Replace the solid reference list of a compsolid with a new set. + //! @param[in] theCompSolidId compsolid identifier + //! @param[in] theSolidRefIds new solid reference identifiers + Standard_EXPORT void SetCompSolidSolidRefs( + const BRepGraph_CompSolidId theCompSolidId, + const NCollection_Array1& theSolidRefIds); + + //! Replace the child reference list of a compound with a new set. + //! @param[in] theCompoundId compound identifier + //! @param[in] theChildRefIds new child reference identifiers + Standard_EXPORT void SetCompoundChildRefs( + const BRepGraph_CompoundId theCompoundId, + const NCollection_Array1& theChildRefIds); + + //! Replace the occurrence reference list of a product with a new set. + //! @param[in] theProductId product identifier + //! @param[in] theOccurrenceRefIds new occurrence reference identifiers + Standard_EXPORT void SetProductOccurrenceRefs( + const BRepGraph_ProductId theProductId, + const NCollection_Array1& theOccurrenceRefIds); //! Return the BaseRef portion of any ref entry by generic RefId. //! @param[in] theRefId generic reference identifier @@ -688,25 +1209,16 @@ public: //! Return the mutable BaseRef portion of any ref entry by generic RefId. //! @param[in] theRefId generic reference identifier - //! @return mutable reference to the BaseRef base of the ref entry - Standard_EXPORT BRepGraphInc::BaseRef& ChangeBaseRef(const BRepGraph_RefId theRefId); + //! @return mutable pointer to the BaseRef base of the ref entry, or nullptr if not found + [[nodiscard]] Standard_EXPORT BRepGraphInc::BaseRef* ChangeBaseRef( + const BRepGraph_RefId theRefId); - //! Return the per-kind transitional reference UID vector. - [[nodiscard]] Standard_EXPORT const NCollection_DynamicArray& RefUIDs( - const BRepGraph_RefId::Kind theKind) const; + //! Resolve an active node UID through storage reverse maps. + [[nodiscard]] Standard_EXPORT BRepGraph_NodeId FindNodeIdByUID(const BRepGraph_UID& theUID) const; - //! Return the per-kind transitional reference UID vector (mutable). - Standard_EXPORT NCollection_DynamicArray& ChangeRefUIDs( - const BRepGraph_RefId::Kind theKind); - - //! Clear all transitional reference UID vectors. - Standard_EXPORT void ResetAllRefUIDs(); - - //! Returns the reverse index for parent-child relationship queries. - [[nodiscard]] const BRepGraphInc_ReverseIndex& ReverseIndex() const { return myReverseIdx; } - - //! Returns a mutable reference to the reverse index. - BRepGraphInc_ReverseIndex& ChangeReverseIndex() { return myReverseIdx; } + //! Resolve an active reference UID through storage reverse maps. + [[nodiscard]] Standard_EXPORT BRepGraph_RefId + FindRefIdByUID(const BRepGraph_RefUID& theUID) const; //! Returns the node id bound to the given TShape, or nullptr if not bound. [[nodiscard]] const BRepGraph_NodeId* FindNodeByTShape(const TopoDS_TShape* theTShape) const @@ -755,7 +1267,7 @@ public: template void ForEachTShapeBinding(FuncT&& theFunc) const { - for (NCollection_DataMap::Iterator anIt( + for (NCollection_FlatDataMap::Iterator anIt( myTShapeToNodeId); anIt.More(); anIt.Next()) @@ -769,7 +1281,7 @@ public: template void ForEachOriginalBinding(FuncT&& theFunc) const { - for (NCollection_DataMap::Iterator anIt(myOriginalShapes); + for (NCollection_FlatDataMap::Iterator anIt(myOriginalShapes); anIt.More(); anIt.Next()) { @@ -777,58 +1289,236 @@ public: } } - [[nodiscard]] bool GetIsDone() const { return myIsDone; } + //! Return the generation-validated node-to-shape reconstruction cache. + [[nodiscard]] const NCollection_FlatDataMap& CurrentShapes() const + { + return myCurrentShapes; + } - void SetIsDone(const bool theVal) { myIsDone = theVal; } + //! Return the mutable generation-validated node-to-shape reconstruction cache. + NCollection_FlatDataMap& ChangeCurrentShapes() + { + return myCurrentShapes; + } + + //! Return the mutex protecting the reconstruction cache. + [[nodiscard]] std::shared_mutex& CurrentShapesMutex() const { return myCurrentShapesMutex; } + + //! Clear the generation-validated shape reconstruction cache. + Standard_EXPORT void ClearCurrentShapes(); + + //! Remove one entry from the generation-validated shape reconstruction cache. + Standard_EXPORT void UnbindCurrentShape(const BRepGraph_NodeId theNode); + + //! Clear deferred invalidation queues and release their batch allocator. + Standard_EXPORT void ClearDeferredQueues(); //! Clear all storage. Standard_EXPORT void Clear(); - //! Build reverse indices from entity and relationship tables. - //! Call after population is complete. - Standard_EXPORT void BuildReverseIndex(); + //! Prepare fixed-size destination ranges for indexed load. + //! + //! This is an internal backend preparation API intended for persistence read + //! paths that know final section sizes in advance. It clears previous content, + //! pre-sizes defs/refs/reps and UID vectors, and initializes relation tables + //! exactly once. The load path then restores serialized relation lists and + //! calls RebuildDerivedRelations() once to refresh derived incoming maps. + //! @param theCounts final per-section slot counts. + Standard_EXPORT void PrepareForLoad(const BRepGraphInc_Load::Counts& theCounts); - //! Incrementally update reverse indices for entities appended after a previous - //! BuildReverseIndex(). Only processes entities and refs from the old counts to the - //! current vector lengths - the caller must snapshot ChildRef / SolidRef counts before - //! any Append so this remains O(delta), not O(total). - Standard_EXPORT void BuildDeltaReverseIndex(const uint32_t theOldNbEdges, - const uint32_t theOldNbWires, - const uint32_t theOldNbFaces, - const uint32_t theOldNbShells, - const uint32_t theOldNbSolids, - const uint32_t theOldNbCompounds, - const uint32_t theOldNbCompSolids, - const uint32_t theOldNbChildRefs, - const uint32_t theOldNbSolidRefs); + //! Override active-slot counters after a trusted indexed load path. + //! + //! This is intended for persistence backends that already touched every slot + //! during load and therefore know exact active counts without rescanning + //! storage after relation construction. + //! @param theCounts trusted active per-section counts. + Standard_EXPORT void SetActiveCounts(const BRepGraphInc_Load::Counts& theCounts); - //! Debug: verify reverse index consistency against entity tables. - //! @return true if all forward refs have matching reverse entries - Standard_EXPORT bool ValidateReverseIndex() const; + //! Recount active-slot counters from current `IsRemoved` flags without rebuilding indexes. + Standard_EXPORT void RecountActiveCounts(); + + //! Rebuild centralized relation tables from entity and reference endpoints. + //! This is intended for raw load, compact, and explicit repair paths only; + //! editor mutations maintain relation containers incrementally. + Standard_EXPORT void RebuildDerivedRelations(); + + //! Rebuild relation maps after a trusted load already restored active counts. + Standard_EXPORT void RebuildDerivedRelationsPreservingActiveCounts(); + + //! Debug: verify relation-table consistency against entity/reference endpoints. + //! @return true if all relations are consistent + Standard_EXPORT bool ValidateRelations() const; + + //! Verify coedge ordering consistency for a specific wire. + //! @param[in] theWireId wire identifier + //! @return true if the coedge order is valid + Standard_EXPORT bool ValidateWireCoEdgeOrder(const BRepGraph_WireId theWireId) const; + + //! Verify coedge ordering consistency for all wires. + //! @return true if all wire coedge orders are valid + Standard_EXPORT bool ValidateWireCoEdgeOrders() const; + + //! Result of wire coedge order canonicalization. + enum class WireCoEdgeOrderStatus + { + Connected, //!< Stored order was already connected. + Reordered, //!< Stored coedges were reordered into an exactly connected chain. + ToleranceOrdered, //!< Stored coedges were ordered using tolerance-equivalent endpoints. + Partial, //!< Stored coedges were grouped into best-effort connected runs. + InvalidInput //!< Wire or coedge ownership/input data is invalid. + }; + + //! Canonicalize the coedge ordering of a wire and report the achieved order quality. + //! @param[in] theWireId wire identifier + //! @return canonicalization status + Standard_EXPORT WireCoEdgeOrderStatus + CanonicalizeWireCoEdgeOrderStatus(const BRepGraph_WireId theWireId); + + //! Canonicalize the coedge ordering of a wire to a consistent form. + //! @param[in] theWireId wire identifier + //! @return true if canonicalization succeeded + Standard_EXPORT bool CanonicalizeWireCoEdgeOrder(const BRepGraph_WireId theWireId); + + //! Rebuild UID reverse indexes (UID->NodeId, RefUID->RefId) + //! from the current UID vectors. Clears indexes and resets allocators before rebuilding. + //! Called after Compact, Load, etc. where UID vectors have been modified externally. + Standard_EXPORT void RebuildUIDReverseIndexes(); + + //! Mark UID reverse indexes stale after bulk UID-vector replacement. + Standard_EXPORT void MarkUIDReverseIndexesDirty(); + + //! Lazily rebuild the node UID reverse index if it is stale. + Standard_EXPORT void EnsureUIDReverseIndex() const; + + //! Lazily rebuild the reference UID reverse index if it is stale. + Standard_EXPORT void EnsureRefUIDReverseIndex() const; private: friend class BRepGraphInc_Populate; friend class BRepGraph; - friend class BRepGraphInc_ReverseIndex; - //! @brief Template store for topology entity kinds. - //! Groups the entity vector, per-kind UID vector, and active count - //! into a single struct, eliminating repeated boilerplate. + Standard_EXPORT void ClearStorageForReuse(); + Standard_EXPORT void ClearUIDIndexes(); + Standard_EXPORT void ClearShapeCache(); + Standard_EXPORT void ClearRelations(); + + BRepGraphInc::FaceRelations& ChangeFaceRelationsInternal(const BRepGraph_FaceId theId) + { + return myFaceRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::WireRelations& ChangeWireRelationsInternal(const BRepGraph_WireId theId) + { + return myWireRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::EdgeRelations& ChangeEdgeRelationsInternal(const BRepGraph_EdgeId theId) + { + return myEdgeRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::ShellRelations& ChangeShellRelationsInternal(const BRepGraph_ShellId theId) + { + return myShellRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::SolidRelations& ChangeSolidRelationsInternal(const BRepGraph_SolidId theId) + { + return mySolidRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::CompoundRelations& ChangeCompoundRelationsInternal(const BRepGraph_CompoundId theId) + { + return myCompoundRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::CompSolidRelations& ChangeCompSolidRelationsInternal( + const BRepGraph_CompSolidId theId) + { + return myCompSolidRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::VertexRelations& ChangeVertexRelationsInternal(const BRepGraph_VertexId theId) + { + return myVertexRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::ProductRelations& ChangeProductRelationsInternal(const BRepGraph_ProductId theId) + { + return myProductRelations.ChangeValue(static_cast(theId.Index)); + } + + BRepGraphInc::OccurrenceRelations& ChangeOccurrenceRelationsInternal( + const BRepGraph_OccurrenceId theId) + { + return myOccurrenceRelations.ChangeValue(static_cast(theId.Index)); + } + + Standard_EXPORT NCollection_LinearVector& ChangeCompoundRefsOfNodeInternal( + const BRepGraph_NodeId theNode); + + Standard_EXPORT NCollection_LinearVector& + ChangeOccurrenceRefsOfNodeInternal(const BRepGraph_NodeId theNode); + + Standard_EXPORT void rebuildDerivedRelationsInternal(const bool theRecountActiveCounts); + + //! Return true if the typed entity has at least one parent compound (internal). + template + [[nodiscard]] bool HasCompoundParentTyped(const T theId) const; + + //! Set or clear the "has parent compound" flag for a typed entity (internal). + template + void SetHasCompoundParentTyped(const T theId, const bool theVal); + + //! Return true if the typed entity has at least one parent occurrence (internal). + template + [[nodiscard]] bool HasOccurrenceParentTyped(const T theId) const; + + //! Set or clear the "has parent occurrence" flag for a typed entity (internal). + template + void SetHasOccurrenceParentTyped(const T theId, const bool theVal); + + //! Set or clear the "has parent compound" flag for a generic NodeId (internal dispatch). + Standard_EXPORT void SetHasCompoundParent(const BRepGraph_NodeId theNode, bool theVal); + + //! Set or clear the "has parent occurrence" flag for a generic NodeId (internal dispatch). + Standard_EXPORT void SetHasOccurrenceParent(const BRepGraph_NodeId theNode, bool theVal); + + //! Template store for topology entity kinds. template struct DefStore { using TypeId = typename EntityT::TypeId; using ValueType = EntityT; - NCollection_DynamicArray Entities; - NCollection_DynamicArray UIDs; - uint32_t NbActive = 0; + //! Entity representations stored by typed id index. + NCollection_DynamicArray Entities; - DefStore() = default; + //! Bit-flag plane for soft-removal status. + BRepGraphInc_BitFlags RemovedFlags; + + //! Bit-flag plane for ownership status. + BRepGraphInc_BitFlags OwnedFlags; + + //! Bit-flag plane for active MutGuard tracking. + BRepGraphInc_BitFlags GuardFlags; + + //! Bit-flag plane: node has at least one parent compound (ChildRef). + BRepGraphInc_BitFlags HasCompoundParentFlags; + + //! Bit-flag plane: node has at least one parent occurrence (OccurrenceRef). + BRepGraphInc_BitFlags HasOccurrenceParentFlags; + + //! Number of non-removed entities currently present in the store. + uint32_t NbActive = 0; + + //! Per-kind monotonic UID counter. Valid UIDs start at 1. + std::atomic NextUIDCounter{1}; + + DefStore() = delete; DefStore(const int theBlockSize, const occ::handle& theAlloc) - : Entities(theBlockSize, theAlloc), - UIDs(theBlockSize, theAlloc) + : Entities(theBlockSize, theAlloc) { } @@ -845,11 +1535,16 @@ private: } //! Append a default-constructed entity and return its typed slot id. - TypeId Append(const occ::handle& theAlloc) + TypeId Append() { const TypeId anId(static_cast(Entities.Size())); ++NbActive; - Entities.Appended().InitVectors(theAlloc); + Entities.Appended(); + RemovedFlags.Resize(static_cast(anId.Index) + 1); + OwnedFlags.Resize(static_cast(anId.Index) + 1); + GuardFlags.Resize(static_cast(anId.Index) + 1); + HasCompoundParentFlags.Resize(static_cast(anId.Index) + 1); + HasOccurrenceParentFlags.Resize(static_cast(anId.Index) + 1); return anId; } @@ -857,7 +1552,9 @@ private: { Standard_ASSERT_VOID(NbActive > 0u, "DefStore::DecrementActive: underflow"); if (NbActive > 0u) + { --NbActive; + } } bool MarkRemoved(const TypeId theId) @@ -867,121 +1564,66 @@ private: return false; } - EntityT& anEntity = Change(theId); - if (anEntity.IsRemoved) + if (RemovedFlags.Test(theId.Index)) { return false; } - anEntity.IsRemoved = true; + RemovedFlags.Set(theId.Index); DecrementActive(); return true; } - void Clear() + void Clear(const bool theReleaseMemory = false) { - Entities.Clear(); - UIDs.Clear(); + Entities.Clear(theReleaseMemory); + RemovedFlags.ClearAll(); + OwnedFlags.ClearAll(); + GuardFlags.ClearAll(); + HasCompoundParentFlags.ClearAll(); + HasOccurrenceParentFlags.ClearAll(); NbActive = 0; + // Note: NextUIDCounter is NOT reset. UIDs stay monotonic across Clear() cycles. + // Generation + GraphGUID protect against stale UID aliasing. } }; - //! @brief Template store for representation entity kinds. - //! Groups the representation vector and active count into a single struct. - template - struct RepStore - { - using TypeId = typename RepT::TypeId; - using ValueType = RepT; - - NCollection_DynamicArray Entities; - uint32_t NbActive = 0; - - RepStore() = default; - - RepStore(const int theBlockSize, const occ::handle& theAlloc) - : Entities(theBlockSize, theAlloc) - { - } - - uint32_t Nb() const { return static_cast(Entities.Size()); } - - const RepT& Get(const TypeId theId) const - { - return Entities.Value(static_cast(theId.Index)); - } - - RepT& Change(const TypeId theId) - { - return Entities.ChangeValue(static_cast(theId.Index)); - } - - //! Append a default-constructed rep and return its typed slot id. - TypeId Append() - { - const TypeId anId(static_cast(Entities.Size())); - ++NbActive; - Entities.Appended(); - return anId; - } - - void DecrementActive() - { - Standard_ASSERT_VOID(NbActive > 0u, "RepStore::DecrementActive: underflow"); - if (NbActive > 0u) - --NbActive; - } - - bool MarkRemoved(const TypeId theId) - { - if (!theId.IsValid(Nb())) - { - return false; - } - - RepT& aRep = Change(theId); - if (aRep.IsRemoved) - { - return false; - } - aRep.IsRemoved = true; - DecrementActive(); - return true; - } - - void EraseLast() - { - Standard_ASSERT_VOID(NbActive > 0u, "RepStore::EraseLast: underflow"); - if (NbActive > 0u) - { - Entities.EraseLast(); - --NbActive; - } - } - - void Clear() - { - Entities.Clear(); - NbActive = 0; - } - }; - - //! @brief Template store for transitional reference entry kinds. - //! Groups reference vectors and per-kind UID vectors into a single struct. + //! Template store for transitional reference kinds. template struct RefStore { using TypeId = typename RefT::TypeId; using ValueType = RefT; - NCollection_DynamicArray Refs; - NCollection_DynamicArray UIDs; - uint32_t NbActive = 0; + //! Reference representations stored by typed id index. + NCollection_DynamicArray Refs; - RefStore() = default; + //! Bit-flag plane for soft-removal status. + BRepGraphInc_BitFlags RemovedFlags; + + //! Bit-flag plane for ownership status. + BRepGraphInc_BitFlags OwnedFlags; + + //! Bit-flag plane for active MutGuard tracking. + BRepGraphInc_BitFlags GuardFlags; + + //! Bit-flag plane: ref has at least one parent compound (unused for refs, kept for macro + //! uniformity). + BRepGraphInc_BitFlags HasCompoundParentFlags; + + //! Bit-flag plane: ref has at least one parent occurrence (unused for refs, kept for macro + //! uniformity). + BRepGraphInc_BitFlags HasOccurrenceParentFlags; + + //! Number of non-removed references currently present in the store. + uint32_t NbActive = 0; + + //! Per-kind monotonic UID counter. Valid UIDs start at 1. + std::atomic NextUIDCounter{1}; + + RefStore() = delete; RefStore(const int theBlockSize, const occ::handle& theAlloc) - : Refs(theBlockSize, theAlloc), - UIDs(theBlockSize, theAlloc) + : Refs(theBlockSize, theAlloc) { } @@ -1000,6 +1642,11 @@ private: const TypeId anId(static_cast(Refs.Size())); ++NbActive; Refs.Appended(); + RemovedFlags.Resize(static_cast(anId.Index) + 1); + OwnedFlags.Resize(static_cast(anId.Index) + 1); + GuardFlags.Resize(static_cast(anId.Index) + 1); + HasCompoundParentFlags.Resize(static_cast(anId.Index) + 1); + HasOccurrenceParentFlags.Resize(static_cast(anId.Index) + 1); return anId; } @@ -1007,7 +1654,9 @@ private: { Standard_ASSERT_VOID(NbActive > 0u, "RefStore::DecrementActive: underflow"); if (NbActive > 0u) + { --NbActive; + } } bool MarkRemoved(const TypeId theId) @@ -1017,64 +1666,308 @@ private: return false; } - RefT& aRef = Change(theId); - if (aRef.IsRemoved) + if (RemovedFlags.Test(theId.Index)) { return false; } - aRef.IsRemoved = true; + RemovedFlags.Set(theId.Index); DecrementActive(); return true; } - void Clear() + void Clear(const bool theReleaseMemory = false) { - Refs.Clear(); - UIDs.Clear(); + Refs.Clear(theReleaseMemory); + RemovedFlags.ClearAll(); + OwnedFlags.ClearAll(); + GuardFlags.ClearAll(); + HasCompoundParentFlags.ClearAll(); + HasOccurrenceParentFlags.ClearAll(); + NbActive = 0; + // Note: NextUIDCounter is NOT reset. UIDs stay monotonic across Clear() cycles. + } + }; + + //! Primary allocator for backend arrays, stores, and transient backend maps. + occ::handle myAllocator = new NCollection_IncAllocator; + + //! Backend-owned root products and deferred invalidation queues. + NCollection_LinearVector myRootProductIds; + NCollection_LinearVector myDeferredModified; + NCollection_LinearVector myDeferredRefModified; + + //! Vertex definition store. + DefStore myVertices; + + //! Edge definition store. + DefStore myEdges; + + //! Coedge definition store. + DefStore myCoEdges; + + //! Wire definition store. + DefStore myWires; + + //! Face definition store. + DefStore myFaces; + + //! Shell definition store. + DefStore myShells; + + //! Solid definition store. + DefStore mySolids; + + //! Compound definition store. + DefStore myCompounds; + + //! CompSolid definition store. + DefStore myCompSolids; + + //! Product definition store. + DefStore myProducts; + + //! Occurrence definition store. + DefStore myOccurrences; + + //! Shell reference store. + RefStore myShellRefs; + + //! Face reference store. + RefStore myFaceRefs; + + //! Wire reference store. + RefStore myWireRefs; + + //! Vertex reference store. + RefStore myVertexRefs; + + //! Solid reference store. + RefStore mySolidRefs; + + //! Child reference store. + RefStore myChildRefs; + + //! Occurrence reference store. + RefStore myOccurrenceRefs; + + //! Centralized relation tables parallel to entity stores. + NCollection_DynamicArray myFaceRelations; + NCollection_DynamicArray myWireRelations; + NCollection_DynamicArray myEdgeRelations; + NCollection_DynamicArray myShellRelations; + NCollection_DynamicArray mySolidRelations; + NCollection_DynamicArray myCompoundRelations; + NCollection_DynamicArray myCompSolidRelations; + NCollection_DynamicArray myVertexRelations; + NCollection_DynamicArray myProductRelations; + NCollection_DynamicArray myOccurrenceRelations; + + //! Sparse incoming compound child refs keyed by referenced node. + NCollection_DataMap> + myNodeToCompounds; + + //! Sparse incoming product occurrence refs keyed by occurrence child node. + NCollection_DataMap> + myNodeToOccurrences; + + //! Representation-use store with removal tracking. + template + struct RepStore + { + using TypeId = typename UseT::TypeId; + + NCollection_DynamicArray Uses; + BRepGraphInc_BitFlags RemovedFlags; + uint32_t NbActive = 0; + + RepStore() = delete; + + RepStore(const int theBlockSize, const occ::handle& theAlloc) + : Uses(theBlockSize, theAlloc) + { + } + + uint32_t Nb() const { return static_cast(Uses.Size()); } + + const UseT& Get(const TypeId theId) const + { + return Uses.Value(static_cast(theId.Index)); + } + + UseT& Change(const TypeId theId) { return Uses.ChangeValue(static_cast(theId.Index)); } + + TypeId Append() + { + const TypeId anId(static_cast(Uses.Size())); + ++NbActive; + Uses.Appended(); + RemovedFlags.Resize(static_cast(anId.Index) + 1); + return anId; + } + + void DecrementActive() + { + Standard_ASSERT_VOID(NbActive > 0u, "RepStore::DecrementActive: underflow"); + if (NbActive > 0u) + { + --NbActive; + } + } + + bool MarkRemoved(const TypeId theId) + { + if (!theId.IsValid(Nb())) + { + return false; + } + if (RemovedFlags.Test(theId.Index)) + { + return false; + } + RemovedFlags.Set(theId.Index); + DecrementActive(); + return true; + } + + bool IsRemoved(const TypeId theId) const + { + return theId.IsValid(Nb()) && RemovedFlags.Test(theId.Index); + } + + void Clear(const bool theReleaseMemory = false) + { + Uses.Clear(theReleaseMemory); + RemovedFlags.ClearAll(); NbActive = 0; } }; - // Topology entity stores - DefStore myVertices; - DefStore myEdges; - DefStore myCoEdges; - DefStore myWires; - DefStore myFaces; - DefStore myShells; - DefStore mySolids; - DefStore myCompounds; - DefStore myCompSolids; - DefStore myProducts; - DefStore myOccurrences; + //! Edge 3D curve use store. + RepStore myEdgeCurves3D; - // Transitional reference entry stores - RefStore myShellRefs; - RefStore myFaceRefs; - RefStore myWireRefs; - RefStore myCoEdgeRefs; - RefStore myVertexRefs; - RefStore mySolidRefs; - RefStore myChildRefs; - RefStore myOccurrenceRefs; + //! Edge 3D polygon use store. + RepStore myEdgePolygons3D; - // Representation entity stores - RepStore mySurfaces; - RepStore myCurves3D; - RepStore myCurves2D; - RepStore myTriangulationsRep; - RepStore myPolygons3D; - RepStore myPolygons2D; - RepStore myPolygonsOnTri; + //! CoEdge 2D curve use store. + RepStore myCoEdgeCurves2D; - BRepGraphInc_ReverseIndex myReverseIdx; + //! CoEdge 2D polygon use store. + RepStore myCoEdgePolygons2D; - NCollection_DataMap myTShapeToNodeId; - NCollection_DataMap myOriginalShapes; + //! CoEdge polygon-on-triangulation use store. + RepStore myCoEdgePolygonsOnTri; - occ::handle myAllocator; + //! Face surface use store. + RepStore myFaceSurfaces; - bool myIsDone = false; + //! Face triangulation use store. + RepStore myFaceTriangulations; + + //! UID reverse indexes: eagerly maintained on allocate/remove, rebuilt on compact/load. + mutable NCollection_FlatDataMap myUIDToNodeId; + mutable std::shared_mutex myUIDToNodeIdMutex; + mutable NCollection_FlatDataMap myRefUIDToRefId; + mutable std::shared_mutex myRefUIDToRefIdMutex; + mutable bool myUIDToNodeIdDirty = false; + mutable bool myRefUIDToRefIdDirty = false; + + //! Bindings from reconstructed / source OCCT shapes back to backend ids. + NCollection_FlatDataMap myTShapeToNodeId; + NCollection_FlatDataMap myOriginalShapes; + + //! Persistent backend identity state. + std::atomic myGeneration{0}; + Standard_GUID myGraphGUID; + + //! Transient mutation-control state used by EditorView invalidation paths. + std::atomic myDeferredMode{false}; + std::atomic myPropagationWave{0}; + uint32_t myRemoveSubgraphDepth = 0; + + //! Transient generation-validated shape reconstruction cache. + mutable NCollection_FlatDataMap myCurrentShapes; + mutable std::shared_mutex myCurrentShapesMutex; + +private: + //! Trait mapping a typed identifier to its store's bit-flag planes. + //! One specialization per typed ID type provides O(1) compile-time dispatch. + template + struct TypedStorePlanes; + + template + friend struct TypedStorePlanes; + + template + [[nodiscard]] bool isInRange(const T theId) const; + + template + [[nodiscard]] bool dispatchItemId(const BRepGraph_ItemId& theId, FuncT&& theFunc) const; + +public: + //! Return true if the entity identified by the given typed ID is soft-removed. + //! @param[in] theId typed entity identifier + template + [[nodiscard]] bool IsRemoved(const T theId) const; + + //! Set or clear the soft-removal flag for the entity identified by the given typed ID. + //! @param[in] theId typed entity identifier + //! @param[in] theVal true to mark removed, false to mark active + template + void SetRemoved(const T theId, const bool theVal); + + //! Return true if the entity identified by the given typed ID has a registered owner. + //! @param[in] theId typed entity identifier + template + [[nodiscard]] bool IsOwned(const T theId) const; + + //! Set or clear the ownership flag for the entity identified by the given typed ID. + //! @param[in] theId typed entity identifier + //! @param[in] theVal true to mark owned, false to mark unowned + template + void SetOwned(const T theId, const bool theVal); + + //! Return true if the entity identified by the given typed ID has an active MutGuard. + //! @param[in] theId typed entity identifier + template + [[nodiscard]] bool IsGuarded(const T theId) const; + + //! Register an active MutGuard on the entity identified by the given typed ID. + //! @param[in] theId typed entity identifier + template + void SetGuarded(const T theId); + + //! Deregister an active MutGuard from the entity identified by the given typed ID. + //! @param[in] theId typed entity identifier + template + void ClearGuarded(const T theId); + + //! Return true if the node identified by the given generic NodeId has a parent compound. + //! Dispatches by node kind to the appropriate per-kind bitset. + //! @param[in] theNode generic node identifier + [[nodiscard]] Standard_EXPORT bool HasCompoundParent(const BRepGraph_NodeId theNode) const; + + //! Return true if the node identified by the given generic NodeId has a parent occurrence. + //! Dispatches by node kind to the appropriate per-kind bitset. + //! @param[in] theNode generic node identifier + [[nodiscard]] Standard_EXPORT bool HasOccurrenceParent(const BRepGraph_NodeId theNode) const; + + //! Return true if the entity identified by the given generic item id has an active MutGuard. + //! @param[in] theId generic item identifier (node, reference, or representation) + [[nodiscard]] bool IsGuarded(const BRepGraph_ItemId& theId) const; + + //! Register an active MutGuard on the entity identified by the given generic item id. + //! @param[in] theId generic item identifier (node, reference, or representation) + void SetGuarded(const BRepGraph_ItemId& theId); + + //! Deregister an active MutGuard from the entity identified by the given generic item id. + //! @param[in] theId generic item identifier (node, reference, or representation) + void ClearGuarded(const BRepGraph_ItemId& theId); + + //! Return true if any entity in any store has an active MutGuard. + //! Used to assert no guards are active before Clear(). + [[nodiscard]] Standard_EXPORT bool HasAnyGuard() const; }; +#include + #endif // _BRepGraphInc_Storage_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.lxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.lxx new file mode 100644 index 0000000000..dfe7ef6d3c --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_Storage.lxx @@ -0,0 +1,375 @@ +// 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. + +//! @name TypedStorePlanes specializations for entity and reference stores. +//! +//! Each store (e.g. myVertices, myEdges) is a DefStore or RefStore that owns +//! per-entity bit-flag planes. The specialization exposes those planes through +//! a uniform static interface so that generic template methods (IsRemoved, +//! SetRemoved, IsGuarded, ...) can operate on any typed ID without a virtual +//! dispatch. +//! +//! Parameter T is the typed identifier (e.g. BRepGraph_VertexId). +//! Parameter F is the member store field name (e.g. myVertices). +#define OCCT_BG_STORE_PLANES(T, F) \ + template <> \ + struct BRepGraphInc_Storage::TypedStorePlanes \ + { \ + static uint32_t Nb(const BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.Nb(); \ + } \ + static uint32_t& NbActive(BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.NbActive; \ + } \ + static BRepGraphInc_BitFlags& Removed(BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.RemovedFlags; \ + } \ + static const BRepGraphInc_BitFlags& Removed(const BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.RemovedFlags; \ + } \ + static BRepGraphInc_BitFlags& Owned(BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.OwnedFlags; \ + } \ + static const BRepGraphInc_BitFlags& Owned(const BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.OwnedFlags; \ + } \ + static BRepGraphInc_BitFlags& Guard(BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.GuardFlags; \ + } \ + static const BRepGraphInc_BitFlags& Guard(const BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.GuardFlags; \ + } \ + static BRepGraphInc_BitFlags& HasCompoundParent(BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.HasCompoundParentFlags; \ + } \ + static const BRepGraphInc_BitFlags& HasCompoundParent(const BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.HasCompoundParentFlags; \ + } \ + static BRepGraphInc_BitFlags& HasOccurrenceParent(BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.HasOccurrenceParentFlags; \ + } \ + static const BRepGraphInc_BitFlags& HasOccurrenceParent( \ + const BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.HasOccurrenceParentFlags; \ + } \ + }; + +OCCT_BG_STORE_PLANES(BRepGraph_VertexId, myVertices) +OCCT_BG_STORE_PLANES(BRepGraph_EdgeId, myEdges) +OCCT_BG_STORE_PLANES(BRepGraph_CoEdgeId, myCoEdges) +OCCT_BG_STORE_PLANES(BRepGraph_WireId, myWires) +OCCT_BG_STORE_PLANES(BRepGraph_FaceId, myFaces) +OCCT_BG_STORE_PLANES(BRepGraph_ShellId, myShells) +OCCT_BG_STORE_PLANES(BRepGraph_SolidId, mySolids) +OCCT_BG_STORE_PLANES(BRepGraph_CompoundId, myCompounds) +OCCT_BG_STORE_PLANES(BRepGraph_CompSolidId, myCompSolids) +OCCT_BG_STORE_PLANES(BRepGraph_ProductId, myProducts) +OCCT_BG_STORE_PLANES(BRepGraph_OccurrenceId, myOccurrences) + +OCCT_BG_STORE_PLANES(BRepGraph_VertexRefId, myVertexRefs) +OCCT_BG_STORE_PLANES(BRepGraph_ShellRefId, myShellRefs) +OCCT_BG_STORE_PLANES(BRepGraph_FaceRefId, myFaceRefs) +OCCT_BG_STORE_PLANES(BRepGraph_WireRefId, myWireRefs) +OCCT_BG_STORE_PLANES(BRepGraph_SolidRefId, mySolidRefs) +OCCT_BG_STORE_PLANES(BRepGraph_ChildRefId, myChildRefs) +OCCT_BG_STORE_PLANES(BRepGraph_OccurrenceRefId, myOccurrenceRefs) + +#undef OCCT_BG_STORE_PLANES + +//! @name TypedStorePlanes specializations for representation-use stores. +//! +//! Representation stores (e.g. myFaceSurfaces, myEdgeCurves3D) track geometric +//! or triangulation data attached to topology entities. They have Removed +//! flags and an NbActive counter but no Owned, Guard, HasCompoundParent, or +//! HasOccurrenceParent planes. Those methods are intentionally omitted -- +//! calling IsOwned/IsGuarded/HasCompoundParentTyped/HasOccurrenceParentTyped +//! on a representation ID will produce a compile error, which is the desired +//! behavior since representation entities have no ownership or guard lifecycle. +#define OCCT_BG_REP_PLANES(T, F) \ + template <> \ + struct BRepGraphInc_Storage::TypedStorePlanes \ + { \ + static uint32_t Nb(const BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.Nb(); \ + } \ + static uint32_t& NbActive(BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.NbActive; \ + } \ + static BRepGraphInc_BitFlags& Removed(BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.RemovedFlags; \ + } \ + static const BRepGraphInc_BitFlags& Removed(const BRepGraphInc_Storage& theStorage) \ + { \ + return theStorage.F.RemovedFlags; \ + } \ + }; + +OCCT_BG_REP_PLANES(BRepGraph_FaceSurfaceRepId, myFaceSurfaces) +OCCT_BG_REP_PLANES(BRepGraph_FaceTriangulationRepId, myFaceTriangulations) +OCCT_BG_REP_PLANES(BRepGraph_EdgeCurve3DRepId, myEdgeCurves3D) +OCCT_BG_REP_PLANES(BRepGraph_EdgePolygon3DRepId, myEdgePolygons3D) +OCCT_BG_REP_PLANES(BRepGraph_CoEdgeCurve2DRepId, myCoEdgeCurves2D) +OCCT_BG_REP_PLANES(BRepGraph_CoEdgePolygon2DRepId, myCoEdgePolygons2D) +OCCT_BG_REP_PLANES(BRepGraph_CoEdgePolygonOnTriRepId, myCoEdgePolygonsOnTri) + +#undef OCCT_BG_REP_PLANES + +//================================================================================================= + +template +bool BRepGraphInc_Storage::isInRange(const T theId) const +{ + return theId.IsValid(TypedStorePlanes::Nb(*this)) + && TypedStorePlanes::Removed(*this).IsValidIndex(theId.Index); +} + +//================================================================================================= + +template +bool BRepGraphInc_Storage::dispatchItemId(const BRepGraph_ItemId& theId, FuncT&& theFunc) const +{ + switch (theId.ItemDomain()) + { + case BRepGraph_ItemId::Domain::Node: + switch (static_cast(theId.RawKind())) + { + case BRepGraph_NodeId::Kind::Vertex: + return std::forward(theFunc)(BRepGraph_VertexId(theId.Index())); + case BRepGraph_NodeId::Kind::Edge: + return std::forward(theFunc)(BRepGraph_EdgeId(theId.Index())); + case BRepGraph_NodeId::Kind::CoEdge: + return std::forward(theFunc)(BRepGraph_CoEdgeId(theId.Index())); + case BRepGraph_NodeId::Kind::Wire: + return std::forward(theFunc)(BRepGraph_WireId(theId.Index())); + case BRepGraph_NodeId::Kind::Face: + return std::forward(theFunc)(BRepGraph_FaceId(theId.Index())); + case BRepGraph_NodeId::Kind::Shell: + return std::forward(theFunc)(BRepGraph_ShellId(theId.Index())); + case BRepGraph_NodeId::Kind::Solid: + return std::forward(theFunc)(BRepGraph_SolidId(theId.Index())); + case BRepGraph_NodeId::Kind::Compound: + return std::forward(theFunc)(BRepGraph_CompoundId(theId.Index())); + case BRepGraph_NodeId::Kind::CompSolid: + return std::forward(theFunc)(BRepGraph_CompSolidId(theId.Index())); + case BRepGraph_NodeId::Kind::Product: + return std::forward(theFunc)(BRepGraph_ProductId(theId.Index())); + case BRepGraph_NodeId::Kind::Occurrence: + return std::forward(theFunc)(BRepGraph_OccurrenceId(theId.Index())); + } + break; + case BRepGraph_ItemId::Domain::Reference: + switch (static_cast(theId.RawKind())) + { + case BRepGraph_RefId::Kind::Shell: + return std::forward(theFunc)(BRepGraph_ShellRefId(theId.Index())); + case BRepGraph_RefId::Kind::Face: + return std::forward(theFunc)(BRepGraph_FaceRefId(theId.Index())); + case BRepGraph_RefId::Kind::Wire: + return std::forward(theFunc)(BRepGraph_WireRefId(theId.Index())); + case BRepGraph_RefId::Kind::Vertex: + return std::forward(theFunc)(BRepGraph_VertexRefId(theId.Index())); + case BRepGraph_RefId::Kind::Solid: + return std::forward(theFunc)(BRepGraph_SolidRefId(theId.Index())); + case BRepGraph_RefId::Kind::Child: + return std::forward(theFunc)(BRepGraph_ChildRefId(theId.Index())); + case BRepGraph_RefId::Kind::Occurrence: + return std::forward(theFunc)(BRepGraph_OccurrenceRefId(theId.Index())); + } + break; + default: + break; + } + return false; +} + +//================================================================================================= + +template +bool BRepGraphInc_Storage::IsRemoved(const T theId) const +{ + return isInRange(theId) && TypedStorePlanes::Removed(*this).Test(theId.Index); +} + +//================================================================================================= + +template +void BRepGraphInc_Storage::SetRemoved(const T theId, const bool theVal) +{ + if (!isInRange(theId)) + { + return; + } + auto& aF = TypedStorePlanes::Removed(*this); + const bool aWasRemoved = aF.Test(theId.Index); + if (aWasRemoved == theVal) + { + return; + } + uint32_t& aNbActive = TypedStorePlanes::NbActive(*this); + if (theVal) + { + aF.Set(theId.Index); + Standard_ASSERT_VOID(aNbActive > 0u, + "BRepGraphInc_Storage::SetRemoved: active count underflow"); + if (aNbActive > 0u) + { + --aNbActive; + } + } + else + { + aF.Clear(theId.Index); + ++aNbActive; + } +} + +//================================================================================================= + +template +bool BRepGraphInc_Storage::IsOwned(const T theId) const +{ + return isInRange(theId) && TypedStorePlanes::Owned(*this).Test(theId.Index); +} + +//================================================================================================= + +template +void BRepGraphInc_Storage::SetOwned(const T theId, const bool theVal) +{ + if (!isInRange(theId)) + { + return; + } + auto& aF = TypedStorePlanes::Owned(*this); + theVal ? aF.Set(theId.Index) : aF.Clear(theId.Index); +} + +//================================================================================================= + +template +bool BRepGraphInc_Storage::IsGuarded(const T theId) const +{ + return isInRange(theId) && TypedStorePlanes::Guard(*this).Test(theId.Index); +} + +//================================================================================================= + +template +void BRepGraphInc_Storage::SetGuarded(const T theId) +{ + if (isInRange(theId)) + { + TypedStorePlanes::Guard(*this).Set(theId.Index); + } +} + +//================================================================================================= + +template +void BRepGraphInc_Storage::ClearGuarded(const T theId) +{ + if (isInRange(theId)) + { + TypedStorePlanes::Guard(*this).Clear(theId.Index); + } +} + +//================================================================================================= + +template +bool BRepGraphInc_Storage::HasCompoundParentTyped(const T theId) const +{ + return isInRange(theId) && TypedStorePlanes::HasCompoundParent(*this).Test(theId.Index); +} + +//================================================================================================= + +template +void BRepGraphInc_Storage::SetHasCompoundParentTyped(const T theId, const bool theVal) +{ + if (!isInRange(theId)) + { + return; + } + auto& aF = TypedStorePlanes::HasCompoundParent(*this); + theVal ? aF.Set(theId.Index) : aF.Clear(theId.Index); +} + +//================================================================================================= + +template +bool BRepGraphInc_Storage::HasOccurrenceParentTyped(const T theId) const +{ + return isInRange(theId) && TypedStorePlanes::HasOccurrenceParent(*this).Test(theId.Index); +} + +//================================================================================================= + +template +void BRepGraphInc_Storage::SetHasOccurrenceParentTyped(const T theId, const bool theVal) +{ + if (!isInRange(theId)) + { + return; + } + auto& aF = TypedStorePlanes::HasOccurrenceParent(*this); + theVal ? aF.Set(theId.Index) : aF.Clear(theId.Index); +} + +//================================================================================================= + +inline bool BRepGraphInc_Storage::IsGuarded(const BRepGraph_ItemId& theId) const +{ + return dispatchItemId(theId, [this](const auto theTypedId) { return IsGuarded(theTypedId); }); +} + +//================================================================================================= + +inline void BRepGraphInc_Storage::SetGuarded(const BRepGraph_ItemId& theId) +{ + if (!dispatchItemId(theId, [this](const auto theTypedId) { + SetGuarded(theTypedId); + return true; + })) + { + Standard_ASSERT_VOID(false, "BRepGraphInc_Storage::SetGuarded: invalid item id"); + } +} + +//================================================================================================= + +inline void BRepGraphInc_Storage::ClearGuarded(const BRepGraph_ItemId& theId) +{ + if (!dispatchItemId(theId, [this](const auto theTypedId) { + ClearGuarded(theTypedId); + return true; + })) + { + Standard_ASSERT_VOID(false, "BRepGraphInc_Storage::ClearGuarded: invalid item id"); + } +} diff --git a/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_WireOrder.pxx b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_WireOrder.pxx new file mode 100644 index 0000000000..dd6ce8a0ad --- /dev/null +++ b/src/ModelingData/TKBRep/BRepGraphInc/BRepGraphInc_WireOrder.pxx @@ -0,0 +1,753 @@ +// 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. + +#ifndef _BRepGraphInc_WireOrder_HeaderFile +#define _BRepGraphInc_WireOrder_HeaderFile + +#include +#include +#include +#include + +#include + +namespace BRepGraphInc_WireOrder +{ + +constexpr size_t THE_INLINE_COEDGES = 128; +constexpr size_t THE_INLINE_ENDPOINTS = THE_INLINE_COEDGES * 2; +constexpr size_t THE_KDTREE_THRESHOLD = 128; + +inline bool testFlag(const BRepGraphInc_BitFlags& theFlags, const size_t theIndex) +{ + return theFlags.Test(static_cast(theIndex)); +} + +inline void setFlag(BRepGraphInc_BitFlags& theFlags, const size_t theIndex) +{ + theFlags.Set(static_cast(theIndex)); +} + +struct CoEdgeEndpointData +{ + BRepGraph_CoEdgeId CoEdgeId; + BRepGraph_VertexId StartVertex; + BRepGraph_VertexId EndVertex; + double StartTolerance = 0.0; + double EndTolerance = 0.0; +}; + +inline bool CoEdgeIdsAreUnique(const NCollection_Array1& theCoEdgeIds) +{ + const size_t aNbCoEdges = theCoEdgeIds.Size(); + for (size_t anOuterIdx = 0; anOuterIdx < aNbCoEdges; ++anOuterIdx) + { + const BRepGraph_CoEdgeId anId = theCoEdgeIds.At(anOuterIdx); + for (size_t anInnerIdx = anOuterIdx + 1; anInnerIdx < aNbCoEdges; ++anInnerIdx) + { + if (theCoEdgeIds.At(anInnerIdx) == anId) + { + return false; + } + } + } + return true; +} + +inline bool coEdgeOrientedVertices(const BRepGraphInc_Storage& theStorage, + const BRepGraph_CoEdgeId theCoEdgeId, + BRepGraph_VertexId& theStartVertex, + BRepGraph_VertexId& theEndVertex) +{ + if (!theCoEdgeId.IsValid(theStorage.NbCoEdges()) || theStorage.IsRemoved(theCoEdgeId)) + { + return false; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(theCoEdgeId); + if (!aCoEdge.ChildEdgeId.IsValid(theStorage.NbEdges()) + || theStorage.IsRemoved(aCoEdge.ChildEdgeId)) + { + return false; + } + + const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(aCoEdge.ChildEdgeId); + const BRepGraph_VertexRefId aStartRef = + (aCoEdge.Orientation == TopAbs_FORWARD) ? anEdge.StartVertexRefId : anEdge.EndVertexRefId; + const BRepGraph_VertexRefId anEndRef = + (aCoEdge.Orientation == TopAbs_FORWARD) ? anEdge.EndVertexRefId : anEdge.StartVertexRefId; + if (!aStartRef.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(aStartRef) + || !anEndRef.IsValid(theStorage.NbVertexRefs()) || theStorage.IsRemoved(anEndRef)) + { + return false; + } + + const BRepGraph_VertexId aStartVertex = theStorage.VertexRef(aStartRef).ChildVertexId; + const BRepGraph_VertexId anEndVertex = theStorage.VertexRef(anEndRef).ChildVertexId; + if (!aStartVertex.IsValid(theStorage.NbVertices()) || theStorage.IsRemoved(aStartVertex) + || !anEndVertex.IsValid(theStorage.NbVertices()) || theStorage.IsRemoved(anEndVertex)) + { + return false; + } + + theStartVertex = aStartVertex; + theEndVertex = anEndVertex; + return true; +} + +inline bool collectCoEdgeEndpointData(const BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theExpectedWireId, + const NCollection_Array1& theInput, + NCollection_Array1& theData) +{ + if (theData.Size() != theInput.Size()) + { + return false; + } + + for (size_t anIdx = 0; anIdx < theInput.Size(); ++anIdx) + { + const BRepGraph_CoEdgeId aCoEdgeId = theInput.At(anIdx); + if (!aCoEdgeId.IsValid(theStorage.NbCoEdges()) || theStorage.IsRemoved(aCoEdgeId)) + { + return false; + } + + const BRepGraphInc::CoEdgeDef& aCoEdge = theStorage.CoEdge(aCoEdgeId); + if ((theExpectedWireId.IsValid() && aCoEdge.ParentWireId != theExpectedWireId) + || !aCoEdge.ChildEdgeId.IsValid(theStorage.NbEdges()) + || theStorage.IsRemoved(aCoEdge.ChildEdgeId)) + { + return false; + } + + const BRepGraphInc::EdgeDef& anEdge = theStorage.Edge(aCoEdge.ChildEdgeId); + BRepGraph_VertexId aStartVertex; + BRepGraph_VertexId anEndVertex; + if (!coEdgeOrientedVertices(theStorage, aCoEdgeId, aStartVertex, anEndVertex)) + { + return false; + } + + CoEdgeEndpointData aData; + aData.CoEdgeId = aCoEdgeId; + aData.StartVertex = aStartVertex; + aData.EndVertex = anEndVertex; + aData.StartTolerance = std::max( + {theStorage.Vertex(aStartVertex).Tolerance, anEdge.Tolerance, Precision::Confusion()}); + aData.EndTolerance = std::max( + {theStorage.Vertex(anEndVertex).Tolerance, anEdge.Tolerance, Precision::Confusion()}); + theData.ChangeAt(anIdx) = aData; + } + return true; +} + +inline bool containsClass(const NCollection_Array1& theClasses, + const size_t theNbClasses, + const BRepGraph_VertexId theClass) +{ + for (size_t anIdx = 0; anIdx < theNbClasses; ++anIdx) + { + if (theClasses.At(anIdx) == theClass) + { + return true; + } + } + return false; +} + +inline bool buildConnectedWireOrderByClasses( + const NCollection_Array1& theInput, + const NCollection_Array1& theStarts, + const NCollection_Array1& theEnds, + NCollection_LinearVector& theOrdered) +{ + theOrdered.Clear(false); + if (theInput.IsEmpty()) + { + return true; + } + if (theStarts.Size() != theInput.Size() || theEnds.Size() != theInput.Size()) + { + return false; + } + + const size_t aNbInput = theInput.Size(); + NCollection_LocalArray aClassStorage(aNbInput * 2); + NCollection_Array1 aClasses = aClassStorage.ToArray1(); + size_t aNbClasses = 0; + for (size_t anIdx = 0; anIdx < aNbInput; ++anIdx) + { + if (!containsClass(aClasses, aNbClasses, theStarts.At(anIdx))) + { + aClasses.ChangeAt(aNbClasses) = theStarts.At(anIdx); + ++aNbClasses; + } + if (!containsClass(aClasses, aNbClasses, theEnds.At(anIdx))) + { + aClasses.ChangeAt(aNbClasses) = theEnds.At(anIdx); + ++aNbClasses; + } + } + + BRepGraph_VertexId aStartClass = theStarts.At(0); + size_t aNbOpenStarts = 0; + size_t aNbOpenEnds = 0; + for (size_t aClassIdx = 0; aClassIdx < aNbClasses; ++aClassIdx) + { + const BRepGraph_VertexId aClass = aClasses.At(aClassIdx); + size_t aNbOut = 0; + size_t aNbIn = 0; + for (size_t anIdx = 0; anIdx < aNbInput; ++anIdx) + { + if (theStarts.At(anIdx) == aClass) + { + ++aNbOut; + } + if (theEnds.At(anIdx) == aClass) + { + ++aNbIn; + } + } + if (aNbOut == aNbIn + 1) + { + ++aNbOpenStarts; + aStartClass = aClass; + } + else if (aNbIn == aNbOut + 1) + { + ++aNbOpenEnds; + } + else if (aNbOut != aNbIn) + { + return false; + } + } + + if ((aNbOpenStarts != 0 || aNbOpenEnds != 0) && (aNbOpenStarts != 1 || aNbOpenEnds != 1)) + { + return false; + } + + BRepGraphInc_BitFlags aUsed; + aUsed.Resize(aNbInput); + + NCollection_LocalArray aClassStack(aNbInput + 1); + NCollection_LocalArray anEdgeStack(aNbInput); + NCollection_LocalArray aCircuitEdges(aNbInput); + size_t aNbClassStack = 0; + size_t aNbEdgeStack = 0; + size_t aNbCircuitEdge = 0; + aClassStack[aNbClassStack] = aStartClass; + ++aNbClassStack; + + while (aNbClassStack != 0) + { + const BRepGraph_VertexId aCurrentClass = aClassStack[aNbClassStack - 1]; + size_t aNextIdx = aNbInput; + for (size_t anIdx = 0; anIdx < aNbInput; ++anIdx) + { + if (!testFlag(aUsed, anIdx) && theStarts.At(anIdx) == aCurrentClass) + { + aNextIdx = anIdx; + break; + } + } + + if (aNextIdx != aNbInput) + { + setFlag(aUsed, aNextIdx); + anEdgeStack[aNbEdgeStack] = aNextIdx; + ++aNbEdgeStack; + aClassStack[aNbClassStack] = theEnds.At(aNextIdx); + ++aNbClassStack; + continue; + } + + --aNbClassStack; + if (aNbEdgeStack != 0) + { + --aNbEdgeStack; + aCircuitEdges[aNbCircuitEdge] = anEdgeStack[aNbEdgeStack]; + ++aNbCircuitEdge; + } + } + + if (aNbCircuitEdge != aNbInput) + { + theOrdered.Clear(false); + return false; + } + + for (size_t anIdx = aNbCircuitEdge; anIdx > 0; --anIdx) + { + theOrdered.Append(theInput.At(aCircuitEdges[anIdx - 1])); + } + return true; +} + +inline bool classOrderIsConnected(const NCollection_Array1& theStarts, + const NCollection_Array1& theEnds) +{ + if (theStarts.Size() != theEnds.Size()) + { + return false; + } + + BRepGraph_VertexId aPrevEnd; + bool hasPrev = false; + for (size_t anIdx = 0; anIdx < theStarts.Size(); ++anIdx) + { + if (hasPrev && theStarts.At(anIdx) != aPrevEnd) + { + return false; + } + aPrevEnd = theEnds.At(anIdx); + hasPrev = true; + } + return true; +} + +inline void copyCoEdgeOrder(const NCollection_Array1& theInput, + NCollection_LinearVector& theOrdered) +{ + theOrdered.Clear(false); + for (const BRepGraph_CoEdgeId& aCoEdgeId : theInput) + { + theOrdered.Append(aCoEdgeId); + } +} + +inline bool buildExactVertexClasses(const NCollection_Array1& theData, + NCollection_Array1& theStarts, + NCollection_Array1& theEnds) +{ + if (theStarts.Size() != theData.Size() || theEnds.Size() != theData.Size()) + { + return false; + } + for (size_t anIdx = 0; anIdx < theData.Size(); ++anIdx) + { + const CoEdgeEndpointData& aData = theData.At(anIdx); + theStarts.ChangeAt(anIdx) = aData.StartVertex; + theEnds.ChangeAt(anIdx) = aData.EndVertex; + } + return true; +} + +inline size_t vertexClassIndex(const NCollection_Array1& theVertices, + const size_t theNbVertices, + const BRepGraph_VertexId theVertex) +{ + for (size_t anIdx = 0; anIdx < theNbVertices; ++anIdx) + { + if (theVertices.At(anIdx) == theVertex) + { + return anIdx; + } + } + return theNbVertices; +} + +inline BRepGraph_VertexId rootClass(const NCollection_Array1& theVertices, + NCollection_Array1& theParents, + const size_t theNbVertices, + const BRepGraph_VertexId theClass) +{ + BRepGraph_VertexId aRoot = theClass; + size_t aRootIdx = vertexClassIndex(theVertices, theNbVertices, aRoot); + size_t aNbHops = 0; + while (aRootIdx != theNbVertices && theParents.At(aRootIdx) != aRoot && aNbHops < theNbVertices) + { + aRoot = theParents.At(aRootIdx); + aRootIdx = vertexClassIndex(theVertices, theNbVertices, aRoot); + ++aNbHops; + } + if (aRootIdx == theNbVertices || aNbHops == theNbVertices) + { + return theClass; + } + + BRepGraph_VertexId aCurrent = theClass; + size_t aCurrentIdx = vertexClassIndex(theVertices, theNbVertices, aCurrent); + aNbHops = 0; + while (aCurrentIdx != theNbVertices && theParents.At(aCurrentIdx) != aRoot + && aNbHops < theNbVertices) + { + const BRepGraph_VertexId aNext = theParents.At(aCurrentIdx); + theParents.ChangeAt(aCurrentIdx) = aRoot; + aCurrent = aNext; + aCurrentIdx = vertexClassIndex(theVertices, theNbVertices, aCurrent); + ++aNbHops; + } + return aRoot; +} + +inline void uniteClasses(const NCollection_Array1& theVertices, + NCollection_Array1& theParents, + const size_t theNbVertices, + const BRepGraph_VertexId theFirst, + const BRepGraph_VertexId theSecond) +{ + const BRepGraph_VertexId aFirstRoot = rootClass(theVertices, theParents, theNbVertices, theFirst); + const BRepGraph_VertexId aSecondRoot = + rootClass(theVertices, theParents, theNbVertices, theSecond); + if (aFirstRoot != aSecondRoot) + { + const size_t aSecondRootIdx = vertexClassIndex(theVertices, theNbVertices, aSecondRoot); + if (aSecondRootIdx != theNbVertices) + { + theParents.ChangeAt(aSecondRootIdx) = aFirstRoot; + } + } +} + +inline BRepGraph_VertexId addOrUpdateVertexClass( + const BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexId theVertex, + const double theTolerance, + NCollection_Array1& theVertices, + NCollection_Array1& theTolerances, + NCollection_Array1& theParents, + size_t& theNbVertices) +{ + for (size_t anIdx = 0; anIdx < theNbVertices; ++anIdx) + { + if (theVertices.At(anIdx) == theVertex) + { + theTolerances.ChangeAt(anIdx) = std::max(theTolerances.At(anIdx), theTolerance); + return theVertex; + } + } + + if (theNbVertices >= theVertices.Size()) + { + return BRepGraph_VertexId(); + } + + theVertices.ChangeAt(theNbVertices) = theVertex; + theTolerances.ChangeAt(theNbVertices) = + std::max(theTolerance, theStorage.Vertex(theVertex).Tolerance); + theParents.ChangeAt(theNbVertices) = theVertex; + ++theNbVertices; + return theVertex; +} + +inline bool buildToleranceVertexClasses(const BRepGraphInc_Storage& theStorage, + const NCollection_Array1& theData, + NCollection_Array1& theStarts, + NCollection_Array1& theEnds) +{ + if (theStarts.Size() != theData.Size() || theEnds.Size() != theData.Size()) + { + return false; + } + if (theData.IsEmpty()) + { + return true; + } + + const size_t aNbMaxVertices = theData.Size() * 2; + NCollection_LocalArray aVertexStorage(aNbMaxVertices); + NCollection_LocalArray aToleranceStorage(aNbMaxVertices); + NCollection_LocalArray aParentStorage(aNbMaxVertices); + NCollection_Array1 aVertices = aVertexStorage.ToArray1(); + NCollection_Array1 aTolerances = aToleranceStorage.ToArray1(); + NCollection_Array1 aParents = aParentStorage.ToArray1(); + size_t aNbVertices = 0; + + for (size_t anIdx = 0; anIdx < theData.Size(); ++anIdx) + { + const CoEdgeEndpointData& aData = theData.At(anIdx); + const BRepGraph_VertexId aStartClass = addOrUpdateVertexClass(theStorage, + aData.StartVertex, + aData.StartTolerance, + aVertices, + aTolerances, + aParents, + aNbVertices); + const BRepGraph_VertexId anEndClass = addOrUpdateVertexClass(theStorage, + aData.EndVertex, + aData.EndTolerance, + aVertices, + aTolerances, + aParents, + aNbVertices); + if (!aStartClass.IsValid(theStorage.NbVertices()) + || !anEndClass.IsValid(theStorage.NbVertices())) + { + return false; + } + + theStarts.ChangeAt(anIdx) = aStartClass; + theEnds.ChangeAt(anIdx) = anEndClass; + } + + if (aNbVertices >= THE_KDTREE_THRESHOLD) + { + NCollection_LocalArray aPointStorage(aNbVertices); + NCollection_Array1 aPoints = aPointStorage.ToArray1(); + for (size_t anIdx = 0; anIdx < aNbVertices; ++anIdx) + { + aPoints.ChangeAt(anIdx) = theStorage.Vertex(aVertices.At(anIdx)).Point; + } + + NCollection_KDTree aTree; + aTree.Build(&aPoints.ChangeAt(0), aNbVertices); + for (size_t anOuter = 0; anOuter < aNbVertices; ++anOuter) + { + const gp_Pnt& aPoint = aPoints.At(anOuter); + aTree.ForEachInRange(aPoint, aTolerances.At(anOuter), [&](size_t theResultIdx) { + const size_t anInner = theResultIdx - 1; + if (anInner == anOuter || anInner >= aNbVertices) + { + return; + } + const double aTol = std::max(aTolerances.At(anOuter), aTolerances.At(anInner)); + if (aPoint.Distance(aPoints.At(anInner)) <= aTol) + { + uniteClasses(aVertices, + aParents, + aNbVertices, + aVertices.At(anOuter), + aVertices.At(anInner)); + } + }); + } + } + else + { + for (size_t anOuter = 0; anOuter < aNbVertices; ++anOuter) + { + const gp_Pnt& aPoint = theStorage.Vertex(aVertices.At(anOuter)).Point; + for (size_t anInner = anOuter + 1; anInner < aNbVertices; ++anInner) + { + const double aTol = std::max(aTolerances.At(anOuter), aTolerances.At(anInner)); + if (aPoint.Distance(theStorage.Vertex(aVertices.At(anInner)).Point) <= aTol) + { + uniteClasses(aVertices, + aParents, + aNbVertices, + aVertices.At(anOuter), + aVertices.At(anInner)); + } + } + } + } + + for (size_t anIdx = 0; anIdx < theData.Size(); ++anIdx) + { + theStarts.ChangeAt(anIdx) = rootClass(aVertices, aParents, aNbVertices, theStarts.At(anIdx)); + theEnds.ChangeAt(anIdx) = rootClass(aVertices, aParents, aNbVertices, theEnds.At(anIdx)); + } + return true; +} + +inline void buildBestEffortRunsByClasses(const NCollection_Array1& theInput, + const NCollection_Array1& theStarts, + const NCollection_Array1& theEnds, + NCollection_LinearVector& theOrdered) +{ + theOrdered.Clear(false); + if (theStarts.Size() != theInput.Size() || theEnds.Size() != theInput.Size()) + { + return; + } + + const size_t aNbInput = theInput.Size(); + BRepGraphInc_BitFlags aUsed; + aUsed.Resize(aNbInput); + + NCollection_LocalArray aRunStartOffsets(aNbInput); + NCollection_LocalArray aRunLengths(aNbInput); + NCollection_LocalArray aRunFirstEdges(aNbInput); + NCollection_LocalArray aFlatRunEdges(aNbInput); + size_t aNbRuns = 0; + size_t aNbFlatRunEdges = 0; + + size_t aNbUsed = 0; + while (aNbUsed < aNbInput) + { + size_t aRunStart = aNbInput; + for (size_t anIdx = 0; anIdx < aNbInput; ++anIdx) + { + if (testFlag(aUsed, anIdx)) + { + continue; + } + + bool hasIncoming = false; + for (size_t anOtherIdx = 0; anOtherIdx < aNbInput; ++anOtherIdx) + { + if (!testFlag(aUsed, anOtherIdx) && anOtherIdx != anIdx + && theEnds.At(anOtherIdx) == theStarts.At(anIdx)) + { + hasIncoming = true; + break; + } + } + if (!hasIncoming) + { + aRunStart = anIdx; + break; + } + } + + if (aRunStart == aNbInput) + { + for (size_t anIdx = 0; anIdx < aNbInput; ++anIdx) + { + if (!testFlag(aUsed, anIdx)) + { + aRunStart = anIdx; + break; + } + } + } + if (aRunStart == aNbInput) + { + break; + } + + size_t aCurrentIdx = aRunStart; + size_t aRunLength = 0; + aRunStartOffsets[aNbRuns] = aNbFlatRunEdges; + aRunFirstEdges[aNbRuns] = aRunStart; + while (aCurrentIdx != aNbInput) + { + setFlag(aUsed, aCurrentIdx); + ++aNbUsed; + ++aRunLength; + aFlatRunEdges[aNbFlatRunEdges] = aCurrentIdx; + ++aNbFlatRunEdges; + + const BRepGraph_VertexId aCurrentEnd = theEnds.At(aCurrentIdx); + size_t aNextIdx = aNbInput; + for (size_t anIdx = 0; anIdx < aNbInput; ++anIdx) + { + if (!testFlag(aUsed, anIdx) && theStarts.At(anIdx) == aCurrentEnd) + { + aNextIdx = anIdx; + break; + } + } + aCurrentIdx = aNextIdx; + } + aRunLengths[aNbRuns] = aRunLength; + ++aNbRuns; + } + + BRepGraphInc_BitFlags aRunUsed; + aRunUsed.Resize(aNbRuns); + for (size_t aSelectedCount = 0; aSelectedCount < aNbRuns; ++aSelectedCount) + { + size_t aBestRun = aNbRuns; + for (size_t aRunIdx = 0; aRunIdx < aNbRuns; ++aRunIdx) + { + if (testFlag(aRunUsed, aRunIdx)) + { + continue; + } + if (aBestRun == aNbRuns || aRunLengths[aRunIdx] > aRunLengths[aBestRun] + || (aRunLengths[aRunIdx] == aRunLengths[aBestRun] + && aRunFirstEdges[aRunIdx] < aRunFirstEdges[aBestRun])) + { + aBestRun = aRunIdx; + } + } + if (aBestRun == aNbRuns) + { + break; + } + setFlag(aRunUsed, aBestRun); + + const size_t aRunStartOffset = aRunStartOffsets[aBestRun]; + const size_t aRunLength = aRunLengths[aBestRun]; + for (size_t anIdx = 0; anIdx < aRunLength; ++anIdx) + { + const size_t anEdgeIdx = aFlatRunEdges[aRunStartOffset + anIdx]; + theOrdered.Append(theInput.At(anEdgeIdx)); + } + } +} + +inline BRepGraphInc_Storage::WireCoEdgeOrderStatus BuildCoEdgeOrder( + const BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theExpectedWireId, + const NCollection_Array1& theInput, + NCollection_LinearVector& theOrdered) +{ + using Status = BRepGraphInc_Storage::WireCoEdgeOrderStatus; + + theOrdered.Clear(false); + if (!CoEdgeIdsAreUnique(theInput)) + { + return Status::InvalidInput; + } + if (theInput.IsEmpty()) + { + return Status::Connected; + } + + const size_t aNbCoEdges = theInput.Size(); + NCollection_LocalArray aCoEdgeDataStorage(aNbCoEdges); + NCollection_Array1 aCoEdgeData = aCoEdgeDataStorage.ToArray1(); + if (!collectCoEdgeEndpointData(theStorage, theExpectedWireId, theInput, aCoEdgeData)) + { + return Status::InvalidInput; + } + + NCollection_LocalArray aExactStartStorage(aNbCoEdges); + NCollection_LocalArray aExactEndStorage(aNbCoEdges); + NCollection_Array1 aExactStarts = aExactStartStorage.ToArray1(); + NCollection_Array1 aExactEnds = aExactEndStorage.ToArray1(); + if (!buildExactVertexClasses(aCoEdgeData, aExactStarts, aExactEnds)) + { + return Status::InvalidInput; + } + + if (classOrderIsConnected(aExactStarts, aExactEnds)) + { + copyCoEdgeOrder(theInput, theOrdered); + return Status::Connected; + } + if (buildConnectedWireOrderByClasses(theInput, aExactStarts, aExactEnds, theOrdered)) + { + return Status::Reordered; + } + + NCollection_LocalArray aToleranceStartStorage(aNbCoEdges); + NCollection_LocalArray aToleranceEndStorage(aNbCoEdges); + NCollection_Array1 aToleranceStarts = aToleranceStartStorage.ToArray1(); + NCollection_Array1 aToleranceEnds = aToleranceEndStorage.ToArray1(); + if (!buildToleranceVertexClasses(theStorage, aCoEdgeData, aToleranceStarts, aToleranceEnds)) + { + return Status::InvalidInput; + } + if (classOrderIsConnected(aToleranceStarts, aToleranceEnds)) + { + copyCoEdgeOrder(theInput, theOrdered); + return Status::ToleranceOrdered; + } + if (buildConnectedWireOrderByClasses(theInput, aToleranceStarts, aToleranceEnds, theOrdered)) + { + return Status::ToleranceOrdered; + } + + buildBestEffortRunsByClasses(theInput, aToleranceStarts, aToleranceEnds, theOrdered); + return theOrdered.Size() == theInput.Size() ? Status::Partial : Status::InvalidInput; +} + +} // namespace BRepGraphInc_WireOrder + +#endif // _BRepGraphInc_WireOrder_HeaderFile diff --git a/src/ModelingData/TKBRep/BRepGraphInc/FILES.cmake b/src/ModelingData/TKBRep/BRepGraphInc/FILES.cmake index fac32de359..e3b53cffa9 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/FILES.cmake +++ b/src/ModelingData/TKBRep/BRepGraphInc/FILES.cmake @@ -1,16 +1,22 @@ set(OCCT_BRepGraphInc_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_BRepGraphInc_FILES + BRepGraphInc_BitFlags.hxx + BRepGraphInc_BoundaryBuilder.pxx + BRepGraphInc_WireOrder.pxx + BRepGraphInc_Load.hxx + BRepGraphInc_ParityOrientation.hxx BRepGraphInc_Definition.hxx BRepGraphInc_Reference.hxx + BRepGraphInc_Relations.hxx BRepGraphInc_Representation.hxx + BRepGraphInc_RepId.hxx BRepGraphInc_Instance.hxx BRepGraphInc_Populate.cxx BRepGraphInc_Populate.hxx BRepGraphInc_Reconstruct.cxx BRepGraphInc_Reconstruct.hxx - BRepGraphInc_ReverseIndex.cxx - BRepGraphInc_ReverseIndex.hxx BRepGraphInc_Storage.cxx BRepGraphInc_Storage.hxx + BRepGraphInc_Storage.lxx ) diff --git a/src/ModelingData/TKBRep/BRepGraphInc/README.md b/src/ModelingData/TKBRep/BRepGraphInc/README.md index 10bcbe5259..ce07c9ff58 100644 --- a/src/ModelingData/TKBRep/BRepGraphInc/README.md +++ b/src/ModelingData/TKBRep/BRepGraphInc/README.md @@ -1,27 +1,48 @@ # BRepGraphInc -BRepGraphInc is the incidence-table backend used by BRepGraph. +BRepGraphInc is the incidence-table backend used by BRepGraph. It is also an +intentional low-level API for callers that need direct storage, load, or +reconstruction control. -INTERNAL USE ONLY: this package is the backend runtime model behind the -BRepGraph facade. External code should treat BRepGraphInc storage/layout APIs -as unstable implementation details unless explicitly surfaced by facade views. +Use the `BRepGraph` views (`Shapes()`, `Topo()`, `Refs()`, `Editor()`, `Mesh()`) +for ordinary graph workflows. Use `BRepGraphInc` directly when the caller owns +the backend invariants: typed-id bounds, soft-removal flags, relation tables, +UID vectors, generation counters, and cache invalidation. -It provides the runtime source of truth for topology entities, assembly entities, context references, reverse indices, reconstruction support, and identity mapping. +Direct storage mutation is valid only if the caller also maintains the matching +side effects: -BRepGraphInc is the backend runtime model that powers BRepGraph. +- update relation tables through storage attach/detach helpers, or call the + storage relation rebuild only from whole-load / whole-rebuild code +- allocate or rebuild UID / RefUID / RepUID vectors and reverse UID indexes +- update active counters and soft-removal bit flags consistently +- invalidate or avoid stale shape caches after topology/representation changes +- run `ValidateRelations()` and, for public boundaries, `BRepGraph_Validate` -External code should normally enter through `BRepGraph_Builder::Perform()`, `BRepGraph::Shapes()`, `BRepGraph::Topo()`, `BRepGraph::Refs()`, and the other facade views. A subset of `BRepGraphInc::*` structs is intentionally exposed read-only through those views; direct storage-level access (`BRepGraph_Data`, `myIncStorage`) is reserved for backend maintenance, low-level infrastructure, and focused tests. +The facade does these things automatically; backend users get performance and +control in exchange for maintaining the contract explicitly. + +Derived TopoDS semantics that are not core graph state are available through +algorithm runtime services: + +- `BRepGraphAlgo_Parameters` +- `BRepGraphAlgo_Regularity` +- `BRepGraph_LayerTopoSupplement` + +These services do not make derived data part of backend storage. Core +reconstruction does not replay parameter or regularity cache values. +persisted core incidence model. ## What This Backend Owns - Topology entity tables (Vertex, Edge, CoEdge, Wire, Face, Shell, Solid, Compound, CompSolid) - Assembly entity tables (Product, Occurrence) -- Representation entity tables (SurfaceRep, Curve3DRep, Curve2DRep, TriangulationRep, Polygon3DRep, Polygon2DRep, PolygonOnTriRep) -- Reference entry tables (ShellRef, FaceRef, WireRef, CoEdgeRef, VertexRef, SolidRef, ChildRef, OccurrenceRef) with BaseRef identity, orientation, and location -- Reverse adjacency indices (including product->occurrences) +- Representation entity tables (FaceSurfaceRep, EdgeCurve3DRep, CoEdgeCurve2DRep, FaceTriangulationRep, EdgePolygon3DRep, CoEdgePolygon2DRep, CoEdgePolygonOnTriRep) +- Reference entry tables (ShellRef, FaceRef, WireRef, VertexRef, SolidRef, ChildRef, OccurrenceRef) with BaseRef identity, orientation, and location +- Central relation tables and sparse incoming relation maps - TShape to NodeId mapping - Original shape map -- Per-kind UID vectors (10 entity kinds + 8 ref kinds) +- Per-kind UID vectors (11 entity kinds + 7 ref kinds) ## Architecture @@ -33,7 +54,7 @@ flowchart TB S --> E[Topology Entity Tables] S --> AS[Assembly Entity Tables] - S --> RX[Reverse Index] + S --> RX[Relation Tables] S --> TM[TShape to NodeId] S --> OR[Original Shapes] S --> UID[UID Vectors] @@ -65,7 +86,7 @@ flowchart LR F -->|WireUsage| W W -->|CoEdgeUsage| CE - CE -->|EdgeDefId| E + CE -->|ChildEdgeId| E E -->|Start/End VertexUsage| V SH -->|FaceUsage| F SO -->|ShellUsage| SH @@ -74,99 +95,164 @@ flowchart LR CO -->|ChildUsage| F CS -->|SolidUsage| SO - PR -->|OccurrenceUsage| OC - PR -->|ShapeRootId| SO - OC -->|ProductDefId| PR - OC -.->|ParentOccurrenceDefId| OC + PR -->|OccurrenceRef| OC + OC -->|ChildNodeId| PR + OC -->|ChildNodeId| SO ``` Notes: -- Intrinsic data lives on entities; context data (orientation/location) lives on Ref tables +- Intrinsic data lives on entities; context data for shell/face/wire/vertex/solid/child/occurrence usages lives on Ref tables - CoEdge owns PCurve data for each edge-face binding (Weiler half-edge pattern) -- ProductDef: `ShapeRootId` (topology root for parts; invalid for assemblies), `OccurrenceRefIds` -- OccurrenceDef: `ProductDefId`, `ParentProductDefId`, `ParentOccurrenceDefId` (tree-structured placement chain), `Placement` +- Product relations: ordered `OccurrenceRefIds` +- OccurrenceRef: `ParentProductId`, `ChildOccurrenceId`, `LocalLocation` +- OccurrenceDef: `ChildNodeId` (topology root for parts, Product for assemblies) ## Entity Hierarchy ```mermaid graph TD - Product["ProductDef
ShapeRootId, RootOrientation, RootLocation"] + Product["ProductDef"] + OccurrenceRef["OccurrenceRef
ParentProductId, ChildOccurrenceId,
LocalLocation
"] + Occurrence["OccurrenceDef
ChildNodeId"] - Compound["CompoundDef
ChildRefIds[]"] - CompSolid["CompSolidDef
SolidRefIds[]"] - Solid["SolidDef
ShellRefIds[], AuxChildRefIds[]"] - Shell["ShellDef
IsClosed, FaceRefIds[], AuxChildRefIds[]"] + Compound["CompoundDef"] + CompSolid["CompSolidDef"] + Solid["SolidDef"] + Shell["ShellDef
(closure is derived)"] - Face["FaceDef
SurfaceRepId, TriangulationRepIds,
ActiveTriangulationIndex, WireRefIds[],
VertexRefIds[], Tolerance, NaturalRestriction
"] + Face["FaceDef
SurfaceRepId, TriangulationRepId,
Tolerance
"] - Wire["WireDef
CoEdgeRefIds[], IsClosed"] + Wire["WireDef
(closure is derived)"] - CoEdge["CoEdgeDef
EdgeDefId, FaceDefId, Orientation,
Curve2DRepId, Polygon2DRepId,
ParamFirst/Last, UV1/UV2,
SeamPairId, SeamContinuity
"] + CoEdge["CoEdgeDef
ParentWireId, ChildEdgeId, FaceId,
Orientation, Curve2DRepId,
Polygon2DRepId, PolygonOnTriRepId
"] - Edge["EdgeDef
Curve3DRepId, Polygon3DRepId,
StartVertexRefId, EndVertexRefId,
InternalVertexRefIds[],
ParamFirst/Last, Tolerance,
SameParameter, SameRange,
IsDegenerate, IsClosed,
Regularities[]
"] + Edge["EdgeDef
Curve3DRepId, Polygon3DRepId,
StartVertexRefId, EndVertexRefId,
Tolerance
(degeneracy, closure, SameParameter,
SameRange are derived queries)
"] - Vertex["VertexDef
Point (def frame), Tolerance,
PointsOnCurve[],
PointsOnPCurve[],
PointsOnSurface[]
"] + Vertex["VertexDef
Point (def frame), Tolerance"] - SurfRep["SurfaceRep
Geom_Surface"] - C3DRep["Curve3DRep
Geom_Curve"] - C2DRep["Curve2DRep
Geom2d_Curve"] - TriRep["TriangulationRep
Poly_Triangulation"] + SurfRep["FaceSurfaceRep
Geom_Surface"] + C3DRep["EdgeCurve3DRep
Geom_Curve"] + C2DRep["CoEdgeCurve2DRep
Geom2d_Curve"] + TriRep["FaceTriangulationRep
Poly_Triangulation"] - Product -->|"ShapeRootId"| Compound - Product -->|"ShapeRootId"| Solid + Product -->|"ProductRelations.OccurrenceRefIds"| OccurrenceRef + OccurrenceRef -->|"ChildOccurrenceId"| Occurrence + Occurrence -->|"ChildNodeId"| Product + Occurrence -->|"ChildNodeId"| Compound + Occurrence -->|"ChildNodeId"| Solid Compound -->|"ChildRefId"| Solid CompSolid -->|"SolidRefId"| Solid Solid -->|"ShellRefId"| Shell Shell -->|"FaceRefId"| Face Face -->|"WireRefId"| Wire - Wire -->|"CoEdgeRefId"| CoEdge - CoEdge -->|"EdgeIdx"| Edge + Wire -->|"CoEdgeId"| CoEdge + CoEdge -->|"ChildEdgeId"| Edge Edge -->|"StartVertexRefId"| Vertex Face -.->|"SurfaceRepId"| SurfRep - Face -.->|"TriangulationRepIds"| TriRep + Face -.->|"TriangulationRepId"| TriRep Edge -.->|"Curve3DRepId"| C3DRep CoEdge -.->|"Curve2DRepId"| C2DRep - CoEdge -.->|"SeamPairId"| CoEdge + CoEdge -.->|"same edge/face, opposite orientation"| CoEdge ``` ## Reference Entry Model -Reference entries are the typed incidence edges connecting parent entities to child definitions. Each ref kind has its own entry table and RefId space, managed by `RefStore` in Storage. +Reference entries are typed incidence edges connecting parent entities to child +definitions. Each ref kind has its own entry table and RefId space, managed by +`RefStore` in Storage. ### BaseRef Common header for all reference entries: -- `RefId`: typed address (Kind + Index) into the ref entry vector -- `ParentId`: NodeId of the owning parent entity - `OwnGen`: generation counter for change tracking (incremented on ref mutation) -- `IsRemoved`: soft-delete flag + +Removal state is stored in the owning storage bit vectors. Ref parent and child +endpoints live on the ref record itself. ### Ref Types -Concrete ref entry types extend BaseRef with context data: +Concrete ref entry types extend BaseRef with endpoint and context data: -- `ShellRef`, `FaceRef`, `WireRef`, `CoEdgeRef`, `VertexRef`, `SolidRef`, `ChildRef`, `OccurrenceRef` -- Each adds: `DefId` (target entity index), `Orientation`, `LocalLocation` +- `ShellRef`, `FaceRef`, `WireRef`, `VertexRef`, `SolidRef`, `ChildRef`, `OccurrenceRef` +- Normal topology refs add typed parent and/or child IDs plus `Orientation`. +- `ChildRef` and `OccurrenceRef` additionally carry `LocalLocation`. -### Entity RefId Vectors +### Relation Tables -Entities store typed RefId vectors instead of inline ref arrays: +Ordered parent-to-child lists are stored in storage-owned relation tables, not +inside definition structs: -- **SolidDef**: `ShellRefIds[]`, `AuxChildRefIds[]` -- **ShellDef**: `FaceRefIds[]`, `AuxChildRefIds[]` -- **FaceDef**: `WireRefIds[]`, `VertexRefIds[]` -- **WireDef**: `CoEdgeRefIds[]` -- **EdgeDef**: `StartVertexRefId`, `EndVertexRefId`, `InternalVertexRefIds[]` -- **CompoundDef**: `ChildRefIds[]` -- **CompSolidDef**: `SolidRefIds[]` -- **ProductDef**: `OccurrenceRefIds[]` +- **SolidRelations**: `ShellRefIds[]` +- **ShellRelations**: `FaceRefIds[]` +- **FaceRelations**: `WireRefIds[]` +- **WireRelations**: `CoEdgeIds[]` +- **EdgeRelations**: `CoEdgeIds[]` +- **VertexRelations**: `EdgeIds[]` +- **CompoundRelations**: `ChildRefIds[]` +- **CompSolidRelations**: `SolidRefIds[]` +- **ProductRelations**: `OccurrenceRefIds[]` +- **OccurrenceRelations**: `ParentOccurrenceRefIds[]` + +Incoming parent traversal reads derived incoming lists on relation structs or +sparse node-keyed maps (`myNodeToCompounds`, `myNodeToOccurrences`). ### RefStore -`RefStore` in Storage groups per-kind ref entry vector + UID vector + active count. Provides `Get()`, `Change()`, `Append()`, `DecrementActive()` (for soft-delete tracking via `BaseRef.IsRemoved`) -- same pattern as `DefStore`. +`RefStore` in Storage groups per-kind ref entry vector + UID vector + active count. Provides `Get()`, `Change()`, `Append()`, `DecrementActive()` -- same pattern as `DefStore`. Soft-delete state is tracked via `RefStore::RemovedFlags` bit planes in Storage, not on the `BaseRef` struct. + +## Indexed Load Preparation Contract + +The current backend grows all entity/ref/rep stores through `Append()`. That is correct for +graph construction and editing, but it is the main blocker for single-file indexed loading and +parallel persistence read paths. + +For ODE-style indexed load, the backend needs an internal preparation phase before any record +representations are written: + +1. determine final counts for every persisted section +2. pre-size outer storage ranges for defs, refs, reps, and UID vectors +3. initialize relation table slots exactly once +4. fill records by typed index +5. run one whole-graph relation rebuild after all canonical forward data is present + +### Why relation storage is prepared separately + +`DefStore` owns only the outer entity vector and per-kind UID vector. +Definition structs do not own child relation lists. Indexed load prepares the +definition/ref/rep ranges first, then prepares relation arrays and fills the +canonical ordered relation lists from serialized relation sections. + +### RefStore and RepStore preparation + +`RefStore` currently has no inner vector members, so preparation is simpler: + +- pre-size ref records +- pre-size ref UID vectors +- fill parent ids, target ids, location, and orientation by typed index + +`RepStore` should also be prepared to final size up front, but representation loading should +be split into two sub-phases: + +1. metadata fill: + `OwnGen`, `IsRemoved`, and rep cross-links such as `PolygonOnTriRep.TriangulationRepId` +2. representation decode: + geometry and mesh object reconstruction + +That split is important for ODE read because geometry and mesh representation decode is the expensive +part and is the best parallelization target. + +### Relation load contract + +Derived incoming relation lists, sparse maps, and edge caches are maintained at +whole-load granularity. Indexed loaders fill definitions, refs, coedges, and +ordered relation lists first, then perform one final storage relation rebuild. + +Editor and builder mutation paths must not call whole relation rebuilds as +repair. They update affected relation entries incrementally with targeted +attach/detach/rebind operations. ## Build Pipeline @@ -175,11 +261,8 @@ flowchart LR I[TopoDS input] --> P1[Phase 1: Hierarchy traversal] P1 --> P2[Phase 2: Parallel face extraction] P2 --> P3[Phase 3: Sequential register and dedup] - P3 --> P3a[Phase 3a: Compound face fixup] - P3a --> P3b[Phase 3b: Edge regularities] - P3b --> P3c[Phase 3c: Vertex point reps] - P3c --> P4[Phase 4: Reverse index build] - P4 --> D[Storage IsDone] + P3 --> P3a[Phase 3a: Natural restriction synthesis] + P3a --> D[Storage IsDone] ``` | Phase | Mode | What happens | @@ -187,18 +270,15 @@ flowchart LR | **Phase 1** | Sequential | Traverse hierarchy. Create container entities (Compound, CompSolid, Solid, Shell). Collect face contexts. | | **Phase 2** | Parallel | Extract per-face geometry: surface, PCurves, triangulations, vertices, edges. | | **Phase 3** | Sequential | Register faces, wires, edges, CoEdges with TShape deduplication. Link faces to shells. | -| **Phase 3a** | Sequential | Resolve deferred Compound->Face ChildUsage indices via TShape lookup. | -| **Phase 3b** | Optional | Edge regularities (controlled by `Options.ExtractRegularities`). | -| **Phase 3c** | Optional | Vertex point representations (controlled by `Options.ExtractVertexPointReps`). | -| **Phase 4** | Sequential | Build reverse indices for O(1) upward navigation. | +| **Phase 3a** | Sequential | Synthesize wire boundaries for faces with natural restrictions (no explicit wire). Creates vertices, edges, wires, and coedges for surface boundary curves. | Backend entry point: `BRepGraphInc_Populate::Perform()`. -`BRepGraphInc_Populate::Options` only controls backend extraction passes. Graph-level assembly policy such as root Product creation is owned by `BRepGraph_Builder::BuildOptions` in the facade layer. +`BRepGraphInc_Populate::Options` only controls backend extraction passes. Graph-level assembly policy such as root Product creation is owned by `BRepGraph::ShapesView::Options` in the facade layer. -`BRepGraphInc_Populate::Append()` supports incremental addition to an already-populated storage, with TShape dedup against existing entities. Used by `BRepGraph_Builder::AppendFlattened()` (face-only) and `AppendFull()` (full hierarchy). +`BRepGraphInc_Populate::Append()` supports incremental addition to an already-populated storage, with TShape dedup against existing entities. Used by `BRepGraph::ShapesView::Add()` for full-hierarchy ingestion; flattened ingestion uses `BRepGraphInc_Populate::AppendFlattened()`. -For normal graph construction, use `BRepGraph_Builder::Perform()` instead. The facade owns the public lifecycle, view initialization, mutation boundary behavior, and cache coordination on top of this backend pipeline. +For normal graph construction, use `BRepGraph::ShapesView::Add()` instead. The facade owns the public lifecycle, view initialization, mutation boundary behavior, and cache coordination on top of this backend pipeline. ### Geometry: Definition-Frame Storage @@ -224,11 +304,12 @@ Multi-pass matching in `extractStoredPCurves()`: | Ref Type | What it stores | |---------------|---------------| -| `FaceRef.LocalLocation` | face.Location() relative to shell | -| `WireRef.LocalLocation` | wire.Location() relative to face | -| `CoEdgeRef.LocalLocation` | edge.Location() relative to wire | -| `ShellRef.LocalLocation` | shell.Location() relative to solid | -| `VertexRef.LocalLocation` | vertex.Location() relative to edge | +| `ChildRef.LocalLocation` | child shape placement inside a compound | +| `OccurrenceRef.LocalLocation` | product occurrence placement | + +Normal topology refs (`ShellRef`, `FaceRef`, `WireRef`, `VertexRef`, `SolidRef`) +and `CoEdgeDef` do not store placement. TopoDS locations from normal topology are +baked into definitions while populating the graph. ### Deduplication @@ -274,20 +355,19 @@ anEdge.Location(Identity); // Reset after attachment ### Special Cases -- **Seam edges**: Two CoEdges with opposite Orientation, linked by `SeamPairId`. Both PCurves attached via `UpdateEdge(E, PC1, PC2, S, L, tol)`. +- **Seam edges**: Two CoEdges with the same edge and face and opposite Orientation. Both PCurves are attached to their CoEdges. - **Degenerate edges**: `MakeEdge()` + `Degenerated(true)`, no 3D curve. -- **IsClosed/NaturalRestriction**: Set AFTER sub-shapes are added (Add can reset flags). +- **IsClosed**: Set AFTER sub-shapes are added (Add can reset flags). -## Reverse Indices +## Relation Tables | Map | Purpose | |-----|---------| | edge -> wires | Wire membership | -| edge -> faces | Face adjacency (from CoEdge.FaceDefId) | +| edge -> faces | Face adjacency from `CoEdgeDef::FaceId` | | edge -> coedges | CoEdge lookup by parent edge | | edge face count | Cached O(1) face count per edge | | vertex -> edges | Vertex incidence | -| coedge -> wires | CoEdge-to-wire membership | | wire -> faces | Wire-to-face membership | | face -> shells | Face-to-shell membership | | shell -> solids | Shell-to-solid membership | @@ -303,9 +383,9 @@ anEdge.Location(Identity); // Reset after attachment 1. **Entity ID**: for each entity vector slot i: `Id.Index == i` and `Id.Kind` matches vector kind 2. **Mapping**: TShape to NodeId must resolve to existing, type-correct entity -3. **Reverse-index**: required reverse rows must exist for forward refs used by query paths +3. **Relations**: forward lists, incoming lists, sparse maps, and ref endpoints agree 4. **Removal**: IsRemoved entities must be filtered from normal traversals -5. **Mutation boundary**: entities, reverse indices, cache invalidation, and history are coherent after each operation +5. **Mutation boundary**: entities, relation tables, cache invalidation, and history are coherent after each operation 6. **Assembly**: occurrence cross-references valid; self-referencing rejected; builder-owned root Product creation remains coherent when enabled by build policy ## Memory and Performance @@ -319,19 +399,19 @@ All public Storage accessors use strongly-typed ids (`BRepGraph_VertexId`, `BRep ### Allocator Propagation -All containers use the graph's `NCollection_IncAllocator` for O(1) bump-pointer allocation and bulk-free destruction: +Storage containers use the graph's `NCollection_IncAllocator` where the +container type supports allocator propagation: -- **Storage**: all entity tables, UID vectors, and DataMaps receive the allocator -- **ReverseIndex**: `SetAllocator()` called before reverse-index `Build()`. Inner vectors constructed with allocator via `preSize()`. - -Contract: `SetAllocator()` must be called before reverse-index `Build()`/`BuildDelta()` calls. +- **Storage**: entity tables, UID vectors, caches, and transient maps use the storage-owned allocator +- **Relation tables**: outer slots stay stable, inner `NCollection_LinearVector` + lists grow compactly and are updated incrementally by storage helpers ### Other Performance Notes -- Edge-to-face reverse index uses sort-dedup (stack-allocated for typical 1-4 coedges per edge) - `Append()` allocates UIDs incrementally (O(M) instead of O(N+M)) - Post-passes are optional via `BRepGraphInc_Populate::Options` -- `NbFacesOfEdge()` is O(1) (reads `myEdgeToFaces.Value(idx).Length()` directly) +- `EdgeRelations` stores only `CoEdgeIds`; edge wire/face adjacency is derived + from active coedges. ## TopoDS vs GraphInc Comparison (Box) @@ -354,8 +434,15 @@ Key difference: TopoDS expresses context through shape occurrences. GraphInc kee |------|---------| | `BRepGraphInc_Definition.hxx` | Entity struct definitions | | `BRepGraphInc_Reference.hxx` | Context reference definitions | +| `BRepGraphInc_Relations.hxx` | Relation structs and list helpers | | `BRepGraphInc_Storage.hxx/.cxx` | Typed storage and ownership | | `BRepGraphInc_Populate.hxx/.cxx` | TopoDS -> incidence build and append | | `BRepGraphInc_Reconstruct.hxx/.cxx` | Incidence -> TopoDS reconstruction | -| `BRepGraphInc_ReverseIndex.hxx/.cxx` | Reverse adjacency services | -| `BRepGraph_WireExplorer.hxx` | Wire traversal in connection order (in BRepGraph package) | +| `BRepGraphInc_Storage.lxx` | Late-include implementation: `TypedStorePlanes` specializations and template method definitions | +| `BRepGraphInc_BitFlags.hxx` | Bit-plane storage for per-entity boolean flags | +| `BRepGraphInc_BoundaryBuilder.pxx` | Boundary construction helpers (`.pxx` late-include extension) | +| `BRepGraphInc_Instance.hxx` | Instance location handling | +| `BRepGraphInc_Load.hxx` | Indexed load preparation | +| `BRepGraphInc_ParityOrientation.hxx` | Orientation and parity logic | +| `BRepGraphInc_RepId.hxx` | Strongly-typed representation ID types | +| `BRepGraphInc_Representation.hxx` | Geometry representation records | diff --git a/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx index 6d02490a5e..17004bd8a1 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraphInc_Test.cxx @@ -19,41 +19,60 @@ #include #include #include +#include #include -#include +#include #include "BRepGraph_RefTestTools.hxx" +#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 #include +#include +#include +#include #include #include +#include #include +#include + #include #include +#include static_assert(std::is_same_v); static_assert(std::is_same_v); -static_assert(std::is_same_v); static double computeArea(const TopoDS_Shape& theShape) { @@ -69,9 +88,9 @@ static double computeVolume(const TopoDS_Shape& theShape) return aProps.Mass(); } -static int countSubShapes(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType) +static uint32_t countSubShapes(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType) { - int aCount = 0; + uint32_t aCount = 0; for (TopExp_Explorer anExp(theShape, theType); anExp.More(); anExp.Next()) { ++aCount; @@ -79,6 +98,62 @@ static int countSubShapes(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType return aCount; } +template +static uint32_t countIterator(theIteratorType theIterator) +{ + uint32_t aCount = 0; + for (; theIterator.More(); theIterator.Next()) + { + ++aCount; + } + return aCount; +} + +template +static bool containsId(const NCollection_LinearVector& theIds, const theIdType theId) +{ + for (const theIdType& anId : theIds) + { + if (anId == theId) + { + return true; + } + } + return false; +} + +static BRepGraph_VertexId addStorageVertex(BRepGraphInc_Storage& theStorage, + const gp_Pnt& thePoint, + const double theTolerance) +{ + const BRepGraph_VertexId aVertexId = theStorage.AppendVertex(); + BRepGraphInc::VertexDef& aVertex = theStorage.ChangeVertex(aVertexId); + aVertex.Point = thePoint; + aVertex.Tolerance = theTolerance; + return aVertexId; +} + +static BRepGraph_EdgeId addStorageEdge(BRepGraphInc_Storage& theStorage, + const BRepGraph_VertexId theStartVertex, + const BRepGraph_VertexId theEndVertex, + const double theTolerance = 0.0) +{ + const BRepGraph_EdgeId anEdgeId = theStorage.AppendEdge(); + const BRepGraph_VertexRefId aStartRef = theStorage.AppendVertexRef(); + const BRepGraph_VertexRefId anEndRef = theStorage.AppendVertexRef(); + + theStorage.ChangeVertexRef(aStartRef).ParentEdgeId = anEdgeId; + theStorage.ChangeVertexRef(aStartRef).ChildVertexId = theStartVertex; + theStorage.ChangeVertexRef(anEndRef).ParentEdgeId = anEdgeId; + theStorage.ChangeVertexRef(anEndRef).ChildVertexId = theEndVertex; + + BRepGraphInc::EdgeDef& anEdge = theStorage.ChangeEdge(anEdgeId); + anEdge.StartVertexRefId = aStartRef; + anEdge.EndVertexRefId = anEndRef; + anEdge.Tolerance = theTolerance; + return anEdgeId; +} + // ============================================================ // Entity count validation // ============================================================ @@ -88,25 +163,44 @@ TEST(BRepGraphIncTest, Box_EntityCounts_MatchDefCounts) BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - // Build BRepGraph for parity checks. BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); - // Build incidence storage. - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + // Entity counts for a box: 8 vertices, 12 edges, 6 faces, 1 shell, 1 solid. + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8u); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12u); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6u); + EXPECT_EQ(aGraph.Topo().Shells().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1u); +} - // Entity counts must match Def counts. - EXPECT_EQ(aStorage.NbVertices(), aGraph.Topo().Vertices().Nb()); - EXPECT_EQ(aStorage.NbEdges(), aGraph.Topo().Edges().Nb()); - EXPECT_EQ(aStorage.NbWires(), aGraph.Topo().Wires().Nb()); - EXPECT_EQ(aStorage.NbFaces(), aGraph.Topo().Faces().Nb()); - EXPECT_EQ(aStorage.NbShells(), aGraph.Topo().Shells().Nb()); - EXPECT_EQ(aStorage.NbSolids(), aGraph.Topo().Solids().Nb()); +TEST(BRepGraphIncTest, ParityOrientationRejectsInternalExternal) +{ +#ifndef No_Exception + EXPECT_THROW( + { + [[maybe_unused]] const BRepGraphInc::ParityOrientation aParity = + BRepGraphInc::ParityOrientation(TopAbs_INTERNAL); + }, + Standard_ProgramError); + EXPECT_THROW( + { + [[maybe_unused]] const BRepGraphInc::ParityOrientation aParity = + BRepGraphInc::ParityOrientation(TopAbs_EXTERNAL); + }, + Standard_ProgramError); + + BRepGraphInc::ParityOrientation anOrientation; + EXPECT_THROW(anOrientation = TopAbs_INTERNAL, Standard_ProgramError); + EXPECT_THROW(anOrientation = TopAbs_EXTERNAL, Standard_ProgramError); +#endif + + BRepGraphInc::ParityOrientation anOrientation2; + anOrientation2 = TopAbs_FORWARD; + EXPECT_EQ(TopAbs_Orientation(anOrientation2), TopAbs_FORWARD); + anOrientation2 = TopAbs_REVERSED; + EXPECT_EQ(TopAbs_Orientation(anOrientation2), TopAbs_REVERSED); } TEST(BRepGraphIncTest, Cylinder_EntityCounts_MatchDefCounts) @@ -115,21 +209,15 @@ TEST(BRepGraphIncTest, Cylinder_EntityCounts_MatchDefCounts) const TopoDS_Shape& aCyl = aCylMaker.Shape(); BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCyl); - ASSERT_TRUE(aGraph.IsDone()); + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCyl, false); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCyl, false); - ASSERT_TRUE(aStorage.GetIsDone()); - - EXPECT_EQ(aStorage.NbVertices(), aGraph.Topo().Vertices().Nb()); - EXPECT_EQ(aStorage.NbEdges(), aGraph.Topo().Edges().Nb()); - EXPECT_EQ(aStorage.NbWires(), aGraph.Topo().Wires().Nb()); - EXPECT_EQ(aStorage.NbFaces(), aGraph.Topo().Faces().Nb()); - EXPECT_EQ(aStorage.NbShells(), aGraph.Topo().Shells().Nb()); - EXPECT_EQ(aStorage.NbSolids(), aGraph.Topo().Solids().Nb()); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 2u); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 3u); + EXPECT_EQ(aGraph.Topo().Wires().Nb(), 3u); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 3u); + EXPECT_EQ(aGraph.Topo().Shells().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1u); } TEST(BRepGraphIncTest, Sphere_EntityCounts_MatchDefCounts) @@ -138,123 +226,102 @@ TEST(BRepGraphIncTest, Sphere_EntityCounts_MatchDefCounts) const TopoDS_Shape& aSph = aSphMaker.Shape(); BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aSph); - ASSERT_TRUE(aGraph.IsDone()); + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aSph, false); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aSph, false); - ASSERT_TRUE(aStorage.GetIsDone()); - - EXPECT_EQ(aStorage.NbVertices(), aGraph.Topo().Vertices().Nb()); - EXPECT_EQ(aStorage.NbEdges(), aGraph.Topo().Edges().Nb()); - EXPECT_EQ(aStorage.NbWires(), aGraph.Topo().Wires().Nb()); - EXPECT_EQ(aStorage.NbFaces(), aGraph.Topo().Faces().Nb()); -} - -TEST(BRepGraphIncTest, Storage_MarkRemovedRep_DecrementsActiveCountsAndIsIdempotent) -{ - BRepGraphInc_Storage aStorage; - - (void)aStorage.AppendSurfaceRep(); - (void)aStorage.AppendCurve3DRep(); - (void)aStorage.AppendCurve2DRep(); - (void)aStorage.AppendTriangulationRep(); - (void)aStorage.AppendPolygon3DRep(); - (void)aStorage.AppendPolygon2DRep(); - (void)aStorage.AppendPolygonOnTriRep(); - - EXPECT_EQ(aStorage.NbActiveSurfaces(), 1); - EXPECT_EQ(aStorage.NbActiveCurves3D(), 1); - EXPECT_EQ(aStorage.NbActiveCurves2D(), 1); - EXPECT_EQ(aStorage.NbActiveTriangulations(), 1); - EXPECT_EQ(aStorage.NbActivePolygons3D(), 1); - EXPECT_EQ(aStorage.NbActivePolygons2D(), 1); - EXPECT_EQ(aStorage.NbActivePolygonsOnTri(), 1); - - EXPECT_TRUE(aStorage.MarkRemovedRep(BRepGraph_SurfaceRepId::Start())); - EXPECT_TRUE(aStorage.MarkRemovedRep(BRepGraph_Curve3DRepId::Start())); - EXPECT_TRUE(aStorage.MarkRemovedRep(BRepGraph_Curve2DRepId::Start())); - EXPECT_TRUE(aStorage.MarkRemovedRep(BRepGraph_TriangulationRepId::Start())); - EXPECT_TRUE(aStorage.MarkRemovedRep(BRepGraph_Polygon3DRepId::Start())); - EXPECT_TRUE(aStorage.MarkRemovedRep(BRepGraph_Polygon2DRepId::Start())); - EXPECT_TRUE(aStorage.MarkRemovedRep(BRepGraph_PolygonOnTriRepId::Start())); - - EXPECT_TRUE(aStorage.SurfaceRep(BRepGraph_SurfaceRepId::Start()).IsRemoved); - EXPECT_TRUE(aStorage.Curve3DRep(BRepGraph_Curve3DRepId::Start()).IsRemoved); - EXPECT_TRUE(aStorage.Curve2DRep(BRepGraph_Curve2DRepId::Start()).IsRemoved); - EXPECT_TRUE(aStorage.TriangulationRep(BRepGraph_TriangulationRepId::Start()).IsRemoved); - EXPECT_TRUE(aStorage.Polygon3DRep(BRepGraph_Polygon3DRepId::Start()).IsRemoved); - EXPECT_TRUE(aStorage.Polygon2DRep(BRepGraph_Polygon2DRepId::Start()).IsRemoved); - EXPECT_TRUE(aStorage.PolygonOnTriRep(BRepGraph_PolygonOnTriRepId::Start()).IsRemoved); - - EXPECT_EQ(aStorage.NbActiveSurfaces(), 0); - EXPECT_EQ(aStorage.NbActiveCurves3D(), 0); - EXPECT_EQ(aStorage.NbActiveCurves2D(), 0); - EXPECT_EQ(aStorage.NbActiveTriangulations(), 0); - EXPECT_EQ(aStorage.NbActivePolygons3D(), 0); - EXPECT_EQ(aStorage.NbActivePolygons2D(), 0); - EXPECT_EQ(aStorage.NbActivePolygonsOnTri(), 0); - - EXPECT_FALSE(aStorage.MarkRemovedRep(BRepGraph_SurfaceRepId::Start())); - EXPECT_FALSE(aStorage.MarkRemovedRep(BRepGraph_Curve3DRepId::Start())); - EXPECT_FALSE(aStorage.MarkRemovedRep(BRepGraph_Curve2DRepId::Start())); - EXPECT_FALSE(aStorage.MarkRemovedRep(BRepGraph_TriangulationRepId::Start())); - EXPECT_FALSE(aStorage.MarkRemovedRep(BRepGraph_Polygon3DRepId::Start())); - EXPECT_FALSE(aStorage.MarkRemovedRep(BRepGraph_Polygon2DRepId::Start())); - EXPECT_FALSE(aStorage.MarkRemovedRep(BRepGraph_PolygonOnTriRepId::Start())); - EXPECT_FALSE(aStorage.MarkRemovedRep(BRepGraph_RepId())); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 2u); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 3u); + EXPECT_EQ(aGraph.Topo().Wires().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Shells().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1u); } TEST(BRepGraphIncTest, Storage_AppendAccess_UsesTypedIds) { BRepGraphInc_Storage aStorage; - const BRepGraph_VertexId aVertexId = aStorage.AppendVertex(); - const BRepGraph_FaceRefId aFaceRefId = aStorage.AppendFaceRef(); - const BRepGraph_SurfaceRepId aSurfaceRepId = aStorage.AppendSurfaceRep(); + const BRepGraph_VertexId aVertexId = aStorage.AppendVertex(); + const BRepGraph_FaceRefId aFaceRefId = aStorage.AppendFaceRef(); + const BRepGraph_FaceSurfaceRepId aSurfaceRepId = aStorage.AppendFaceSurfaceRep(); EXPECT_EQ(aVertexId.Index, 0); EXPECT_EQ(aFaceRefId.Index, 0); EXPECT_EQ(aSurfaceRepId.Index, 0); - EXPECT_FALSE(aStorage.Vertex(aVertexId).IsRemoved); - EXPECT_FALSE(aStorage.FaceRef(aFaceRefId).IsRemoved); - EXPECT_FALSE(aStorage.SurfaceRep(aSurfaceRepId).IsRemoved); + EXPECT_FALSE(aStorage.IsRemoved(aVertexId)); + EXPECT_FALSE(aStorage.IsRemoved(aFaceRefId)); - aStorage.ChangeVertex(aVertexId).Tolerance = 1.25; - aStorage.ChangeFaceRef(aFaceRefId).Orientation = TopAbs_REVERSED; - aStorage.ChangeSurfaceRep(aSurfaceRepId).OwnGen = 7; + aStorage.ChangeVertex(aVertexId).Tolerance = 1.25; + aStorage.ChangeFaceRef(aFaceRefId).Orientation = TopAbs_REVERSED; EXPECT_DOUBLE_EQ(aStorage.Vertex(aVertexId).Tolerance, 1.25); EXPECT_EQ(aStorage.FaceRef(aFaceRefId).Orientation, TopAbs_REVERSED); - EXPECT_EQ(aStorage.SurfaceRep(aSurfaceRepId).OwnGen, 7u); + EXPECT_TRUE(aStorage.FaceRef(aFaceRefId).Orientation.IsReversed); + + aStorage.ChangeFaceRef(aFaceRefId).Orientation = TopAbs_FORWARD; + EXPECT_EQ(aStorage.FaceRef(aFaceRefId).Orientation, TopAbs_FORWARD); + EXPECT_FALSE(aStorage.FaceRef(aFaceRefId).Orientation.IsReversed); } TEST(BRepGraphIncTest, Storage_GenericIdDispatch_UsesTypedHelpers) { BRepGraphInc_Storage aStorage; - const BRepGraph_VertexId aVertexId = aStorage.AppendVertex(); - const BRepGraph_FaceRefId aFaceRefId = aStorage.AppendFaceRef(); - const BRepGraph_SurfaceRepId aSurfaceRepId = aStorage.AppendSurfaceRep(); + const BRepGraph_VertexId aVertexId = aStorage.AppendVertex(); + const BRepGraph_FaceRefId aFaceRefId = aStorage.AppendFaceRef(); + std::ignore = aStorage.AppendFaceSurfaceRep(); - aStorage.ChangeBaseRef(BRepGraph_RefId(aFaceRefId)).OwnGen = 11; - - EXPECT_EQ(aStorage.BaseRef(BRepGraph_RefId(aFaceRefId)).OwnGen, 11u); + aStorage.ChangeFaceRef(aFaceRefId).Orientation = TopAbs_REVERSED; + EXPECT_EQ(aStorage.FaceRef(aFaceRefId).Orientation, TopAbs_REVERSED); EXPECT_TRUE(aStorage.MarkRemoved(BRepGraph_NodeId(aVertexId))); EXPECT_TRUE(aStorage.MarkRemovedRef(BRepGraph_RefId(aFaceRefId))); - EXPECT_TRUE(aStorage.MarkRemovedRep(BRepGraph_RepId(aSurfaceRepId))); - EXPECT_TRUE(aStorage.Vertex(aVertexId).IsRemoved); - EXPECT_TRUE(aStorage.FaceRef(aFaceRefId).IsRemoved); - EXPECT_TRUE(aStorage.SurfaceRep(aSurfaceRepId).IsRemoved); + EXPECT_TRUE(aStorage.IsRemoved(aVertexId)); + EXPECT_TRUE(aStorage.IsRemoved(aFaceRefId)); EXPECT_EQ(aStorage.NbActiveVertices(), 0); EXPECT_EQ(aStorage.NbActiveFaceRefs(), 0); - EXPECT_EQ(aStorage.NbActiveSurfaces(), 0); +} + +TEST(BRepGraphIncTest, Storage_SetRemoved_TransitionsUpdateActiveCounts) +{ + BRepGraphInc_Storage aStorage; + + const BRepGraph_VertexId aVertexId = aStorage.AppendVertex(); + const BRepGraph_FaceRefId aFaceRefId = aStorage.AppendFaceRef(); + + EXPECT_EQ(aStorage.NbActiveVertices(), 1); + EXPECT_EQ(aStorage.NbActiveFaceRefs(), 1); + + aStorage.SetRemoved(aVertexId, true); + aStorage.SetRemoved(aFaceRefId, true); + + EXPECT_TRUE(aStorage.IsRemoved(aVertexId)); + EXPECT_TRUE(aStorage.IsRemoved(aFaceRefId)); + EXPECT_EQ(aStorage.NbActiveVertices(), 0); + EXPECT_EQ(aStorage.NbActiveFaceRefs(), 0); + + aStorage.SetRemoved(aVertexId, true); + aStorage.SetRemoved(aFaceRefId, true); + + EXPECT_EQ(aStorage.NbActiveVertices(), 0); + EXPECT_EQ(aStorage.NbActiveFaceRefs(), 0); + + aStorage.SetRemoved(aVertexId, false); + aStorage.SetRemoved(aFaceRefId, false); + + EXPECT_FALSE(aStorage.IsRemoved(aVertexId)); + EXPECT_FALSE(aStorage.IsRemoved(aFaceRefId)); + EXPECT_EQ(aStorage.NbActiveVertices(), 1); + EXPECT_EQ(aStorage.NbActiveFaceRefs(), 1); + + aStorage.SetRemoved(aVertexId, false); + aStorage.SetRemoved(aFaceRefId, false); + + EXPECT_EQ(aStorage.NbActiveVertices(), 1); + EXPECT_EQ(aStorage.NbActiveFaceRefs(), 1); } // ============================================================ @@ -267,11 +334,11 @@ TEST(BRepGraphIncTest, Box_RoundTrip_AreaPreserved) const TopoDS_Shape& aBox = aBoxMaker.Shape(); const double anOrigArea = computeArea(aBox); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); const double aReconArea = computeArea(aRecon); @@ -284,11 +351,11 @@ TEST(BRepGraphIncTest, Cylinder_RoundTrip_AreaPreserved) const TopoDS_Shape& aCyl = aCylMaker.Shape(); const double anOrigArea = computeArea(aCyl); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCyl, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCyl, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); const double aReconArea = computeArea(aRecon); @@ -301,11 +368,11 @@ TEST(BRepGraphIncTest, Sphere_RoundTrip_AreaPreserved) const TopoDS_Shape& aSph = aSphMaker.Shape(); const double anOrigArea = computeArea(aSph); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aSph, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aSph, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); const double aReconArea = computeArea(aRecon); @@ -322,11 +389,11 @@ TEST(BRepGraphIncTest, Box_RoundTrip_VolumePreserved) const TopoDS_Shape& aBox = aBoxMaker.Shape(); const double anOrigVol = computeVolume(aBox); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); const double aReconVol = computeVolume(aRecon); @@ -339,11 +406,11 @@ TEST(BRepGraphIncTest, Cylinder_RoundTrip_VolumePreserved) const TopoDS_Shape& aCyl = aCylMaker.Shape(); const double anOrigVol = computeVolume(aCyl); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCyl, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCyl, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); const double aReconVol = computeVolume(aRecon); @@ -359,11 +426,11 @@ TEST(BRepGraphIncTest, Box_RoundTrip_SubShapeCounts) BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); EXPECT_EQ(countSubShapes(aRecon, TopAbs_FACE), countSubShapes(aBox, TopAbs_FACE)); @@ -377,11 +444,11 @@ TEST(BRepGraphIncTest, Cylinder_RoundTrip_SubShapeCounts) BRepPrimAPI_MakeCylinder aCylMaker(5.0, 15.0); const TopoDS_Shape& aCyl = aCylMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCyl, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCyl, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); EXPECT_EQ(countSubShapes(aRecon, TopAbs_FACE), countSubShapes(aCyl, TopAbs_FACE)); @@ -391,56 +458,50 @@ TEST(BRepGraphIncTest, Cylinder_RoundTrip_SubShapeCounts) } // ============================================================ -// Reverse index consistency +// Relation table consistency // ============================================================ -TEST(BRepGraphIncTest, Box_ReverseIndex_EdgesToWires) +TEST(BRepGraphIncTest, Box_Relations_EdgesToWires) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); // Every edge must appear in at least one wire. - const int aNbEdges = aStorage.NbEdges(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - const NCollection_DynamicArray* aWires = - aStorage.ReverseIndex().WiresOfEdge(anEdgeId); - EXPECT_TRUE(aWires != nullptr) << "Edge " << anEdgeId.Index << " not in any wire"; - if (aWires != nullptr) - { - EXPECT_GE(aWires->Length(), 1); - } + EXPECT_GE(countIterator(aGraph.Topo().Edges().WiresOf(anEdgeId)), 1) + << "Edge " << anEdgeId.Index << " not in any wire"; } } -TEST(BRepGraphIncTest, Box_ReverseIndex_EdgesToFaces) +TEST(BRepGraphIncTest, Box_Relations_EdgesToFaces) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); // Every edge must appear in at least one face (via EdgeFaceGeom). - const int aNbEdges = aStorage.NbEdges(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - if (aStorage.Edge(anEdgeId).IsDegenerate) + // Skip degenerate edges (e.g. sphere poles with no 3D curve). + BRepGraph_CacheDerivedState::EdgeEntry anEdgeState; + [[maybe_unused]] const bool isEdgeStateComputed = + BRepGraph_CacheDerivedState::ComputeEdgeStatus(aGraph, anEdgeId, anEdgeState); + if (anEdgeState.Status == BRepGraph_CacheDerivedState::EdgeGeometryStatus::DegenerateOnSurface) { continue; } - const NCollection_DynamicArray* aFaces = - aStorage.ReverseIndex().FacesOfEdge(anEdgeId); - EXPECT_TRUE(aFaces != nullptr) << "Edge " << anEdgeId.Index << " not in any face"; - if (aFaces != nullptr) - { - EXPECT_GE(aFaces->Length(), 1); - } + EXPECT_GE(countIterator(aGraph.Topo().Edges().FacesOf(anEdgeId)), 1) + << "Edge " << anEdgeId.Index << " not in any face"; } } @@ -453,20 +514,20 @@ TEST(BRepGraphIncTest, Box_ParallelPopulate_SameEntityCounts) BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraphInc_Storage aSerial; - BRepGraphInc_Populate::Perform(aSerial, aBox, false); - ASSERT_TRUE(aSerial.GetIsDone()); + BRepGraph aSerial; + std::ignore = BRepGraphInc_Populate::Perform(aSerial, aBox, false); + ASSERT_FALSE(aSerial.IsEmpty()); - BRepGraphInc_Storage aParallel; - BRepGraphInc_Populate::Perform(aParallel, aBox, true); - ASSERT_TRUE(aParallel.GetIsDone()); + BRepGraph aParallel; + std::ignore = BRepGraphInc_Populate::Perform(aParallel, aBox, true); + ASSERT_FALSE(aParallel.IsEmpty()); - EXPECT_EQ(aParallel.NbVertices(), aSerial.NbVertices()); - EXPECT_EQ(aParallel.NbEdges(), aSerial.NbEdges()); - EXPECT_EQ(aParallel.NbWires(), aSerial.NbWires()); - EXPECT_EQ(aParallel.NbFaces(), aSerial.NbFaces()); - EXPECT_EQ(aParallel.NbShells(), aSerial.NbShells()); - EXPECT_EQ(aParallel.NbSolids(), aSerial.NbSolids()); + EXPECT_EQ(aParallel.Topo().Vertices().Nb(), aSerial.Topo().Vertices().Nb()); + EXPECT_EQ(aParallel.Topo().Edges().Nb(), aSerial.Topo().Edges().Nb()); + EXPECT_EQ(aParallel.Topo().Wires().Nb(), aSerial.Topo().Wires().Nb()); + EXPECT_EQ(aParallel.Topo().Faces().Nb(), aSerial.Topo().Faces().Nb()); + EXPECT_EQ(aParallel.Topo().Shells().Nb(), aSerial.Topo().Shells().Nb()); + EXPECT_EQ(aParallel.Topo().Solids().Nb(), aSerial.Topo().Solids().Nb()); } // ============================================================ @@ -475,11 +536,11 @@ TEST(BRepGraphIncTest, Box_ParallelPopulate_SameEntityCounts) TEST(BRepGraphIncTest, NullShape_NoEntities) { - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, TopoDS_Shape(), false); - EXPECT_FALSE(aStorage.GetIsDone()); - EXPECT_EQ(aStorage.NbVertices(), 0); - EXPECT_EQ(aStorage.NbEdges(), 0); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, TopoDS_Shape(), false); + EXPECT_TRUE(aGraph.IsEmpty()); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 0); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 0); } // ============================================================ @@ -498,18 +559,18 @@ TEST(BRepGraphIncTest, Compound_RoundTrip_SubShapeCounts) BRepPrimAPI_MakeBox aBoxMaker2(5.0, 5.0, 5.0); aBB.Add(aCompound, aBoxMaker2.Shape()); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompound, false); + ASSERT_FALSE(aGraph.IsEmpty()); // Two solids, two shells. - EXPECT_EQ(aStorage.NbSolids(), 2); - EXPECT_EQ(aStorage.NbShells(), 2); - EXPECT_EQ(aStorage.NbCompounds(), 1); - EXPECT_EQ(aStorage.NbFaces(), 12); + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 2); + EXPECT_EQ(aGraph.Topo().Shells().Nb(), 2); + EXPECT_EQ(aGraph.Topo().Compounds().Nb(), 1); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 12); // Round-trip reconstruct via compound. - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_CompoundId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_CompoundId::Start()); ASSERT_FALSE(aRecon.IsNull()); EXPECT_EQ(countSubShapes(aRecon, TopAbs_SOLID), 2); EXPECT_EQ(countSubShapes(aRecon, TopAbs_FACE), 12); @@ -524,24 +585,19 @@ TEST(BRepGraphIncTest, Box_CoEdgeCount) BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); // A box has 12 edges, each shared by 2 faces => 24 CoEdge entries total. // (No seam edges on a box.) - int aCoEdgeCount = 0; - const int aNbEdges = aStorage.NbEdges(); + size_t aCoEdgeCount = 0; + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - const NCollection_DynamicArray* aCoEdgeIdxs = - aStorage.ReverseIndex().CoEdgesOfEdge(anEdgeId); - if (aCoEdgeIdxs != nullptr) - { - aCoEdgeCount += aCoEdgeIdxs->Length(); - } + aCoEdgeCount += aGraph.Topo().Edges().Relations(anEdgeId).CoEdgeIds.Size(); } - EXPECT_EQ(aCoEdgeCount, 24); + EXPECT_EQ(aCoEdgeCount, 24u); } TEST(BRepGraphIncTest, Cylinder_HasSeamEdges) @@ -549,34 +605,34 @@ TEST(BRepGraphIncTest, Cylinder_HasSeamEdges) BRepPrimAPI_MakeCylinder aCylMaker(5.0, 15.0); const TopoDS_Shape& aCyl = aCylMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCyl, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCyl, false); + ASSERT_FALSE(aGraph.IsEmpty()); // A cylinder has seam edges: 2 CoEdges of the same edge on the same face with // opposite orientations. Count seam pairs once via the FORWARD half. - int aSeamPairCount = 0; - const int aNbEdges = aStorage.NbEdges(); + size_t aSeamPairCount = 0; + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - const NCollection_DynamicArray* aCoEdgeIdxs = - aStorage.ReverseIndex().CoEdgesOfEdge(anEdgeId); - if (aCoEdgeIdxs == nullptr) + const NCollection_LinearVector& aCoEdgeIds = + aGraph.Topo().Edges().Relations(anEdgeId).CoEdgeIds; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIds) { - continue; - } - for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCoEdgeIdxs) - { - const BRepGraphInc::CoEdgeDef& aCE = aStorage.CoEdge(aCoEdgeId); + const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeId); if (aCE.Orientation != TopAbs_FORWARD) + { continue; - // Look for a sibling CoEdge with the same FaceDefId and opposite orientation. - for (const BRepGraph_CoEdgeId& aOtherId : *aCoEdgeIdxs) + } + // Look for a sibling CoEdge with the same face and opposite orientation. + for (const BRepGraph_CoEdgeId& aOtherId : aCoEdgeIds) { if (aOtherId == aCoEdgeId) + { continue; - const BRepGraphInc::CoEdgeDef& aOther = aStorage.CoEdge(aOtherId); - if (aOther.FaceDefId == aCE.FaceDefId && aOther.Orientation != aCE.Orientation) + } + const BRepGraphInc::CoEdgeDef& aOther = aGraph.Topo().CoEdges().Definition(aOtherId); + if (aOther.FaceId == aCE.FaceId && aOther.Orientation != aCE.Orientation) { ++aSeamPairCount; break; @@ -585,37 +641,31 @@ TEST(BRepGraphIncTest, Cylinder_HasSeamEdges) } } // A cylinder has 1 seam edge on its lateral face. - EXPECT_GE(aSeamPairCount, 1) << "Cylinder should have at least 1 seam edge pair"; + EXPECT_GE(aSeamPairCount, 1u) << "Cylinder should have at least 1 seam edge pair"; } -TEST(BRepGraphIncTest, Cylinder_SeamEdge_ReverseIndex_NoDuplicateFace) +TEST(BRepGraphIncTest, Cylinder_SeamEdge_Relations_NoDuplicateFace) { BRepPrimAPI_MakeCylinder aCylMaker(5.0, 15.0); const TopoDS_Shape& aCyl = aCylMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCyl, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCyl, false); + ASSERT_FALSE(aGraph.IsEmpty()); - // For seam edges (two PCurve entries with the same FaceDefId but opposite - // orientations), the reverse index must contain each face only once. - const int aNbEdges = aStorage.NbEdges(); + // For seam edges (two PCurve entries with the same face but opposite + // orientations), the relation tables must contain each face only once. + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - const NCollection_DynamicArray* aFaces = - aStorage.ReverseIndex().FacesOfEdge(anEdgeId); - if (aFaces == nullptr) + NCollection_LinearVector aSeenFaces; + for (BRepGraph_FacesOfEdge aFaceIt = aGraph.Topo().Edges().FacesOf(anEdgeId); aFaceIt.More(); + aFaceIt.Next()) { - continue; - } - for (int i = 0; i < aFaces->Length(); ++i) - { - for (int j = i + 1; j < aFaces->Length(); ++j) - { - EXPECT_NE(aFaces->Value(i), aFaces->Value(j)) - << "Duplicate face " << aFaces->Value(i).Index << " in FacesOfEdge(" << anEdgeId.Index - << ")"; - } + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + EXPECT_FALSE(containsId(aSeenFaces, aFaceId)) + << "Duplicate face " << aFaceId.Index << " in FacesOfEdge(" << anEdgeId.Index << ")"; + aSeenFaces.Append(aFaceId); } } } @@ -629,17 +679,20 @@ TEST(BRepGraphIncTest, Sphere_DegenerateEdges_Preserved) BRepPrimAPI_MakeSphere aSphMaker(8.0); const TopoDS_Shape& aSph = aSphMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aSph, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aSph, false); + ASSERT_FALSE(aGraph.IsEmpty()); // A sphere has degenerate edges at the poles (no 3D curve, collapsed to a point). - int aDegenerateCount = 0; - const int aNbEdges = aStorage.NbEdges(); + uint32_t aDegenerateCount = 0; + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - const BRepGraphInc::EdgeDef& anEdge = aStorage.Edge(anEdgeId); - if (anEdge.IsDegenerate) + const BRepGraphInc::EdgeDef& anEdge = aGraph.Topo().Edges().Definition(anEdgeId); + BRepGraph_CacheDerivedState::EdgeEntry anEdgeState; + [[maybe_unused]] const bool isEdgeStateComputed = + BRepGraph_CacheDerivedState::ComputeEdgeStatus(aGraph, anEdgeId, anEdgeState); + if (anEdgeState.Status == BRepGraph_CacheDerivedState::EdgeGeometryStatus::DegenerateOnSurface) { ++aDegenerateCount; EXPECT_FALSE(anEdge.Curve3DRepId.IsValid()) @@ -649,7 +702,7 @@ TEST(BRepGraphIncTest, Sphere_DegenerateEdges_Preserved) EXPECT_GE(aDegenerateCount, 2) << "Sphere should have at least 2 degenerate edges (poles)"; // Round-trip: reconstructed sphere area must still match. - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); const double anOrigArea = computeArea(aSph); @@ -681,11 +734,11 @@ TEST(BRepGraphIncTest, Compound_TranslatedChildren_VolumePreserved) const double anOrigVol = computeVolume(aCompound); EXPECT_NEAR(anOrigVol, 2000.0, Precision::Confusion()); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompound, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_CompoundId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_CompoundId::Start()); ASSERT_FALSE(aRecon.IsNull()); const double aReconVol = computeVolume(aRecon); @@ -694,16 +747,210 @@ TEST(BRepGraphIncTest, Compound_TranslatedChildren_VolumePreserved) EXPECT_EQ(countSubShapes(aRecon, TopAbs_FACE), 12); } +TEST(BRepGraphIncTest, Populate_BakesNormalChildLocationsIntoDefinitions) +{ + const TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(10.0, 0.0, 0.0)); + const TopLoc_Location aLoc(aTrsf); + const TopoDS_Edge aMovedEdge = TopoDS::Edge(anEdge.Moved(aLoc)); + + BRep_Builder aBuilder; + TopoDS_Wire aWire; + aBuilder.MakeWire(aWire); + aBuilder.Add(aWire, anEdge); + aBuilder.Add(aWire, aMovedEdge); + + BRepGraph aGraph; + ASSERT_NE(BRepGraphInc_Populate::Perform(aGraph, aWire, false), + BRepGraphInc_Populate::BuildStatus::Failed); + ASSERT_FALSE(aGraph.IsEmpty()); + + EXPECT_EQ(aGraph.Topo().Wires().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 2u); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 4u); + + double aMinX = RealLast(); + double aMaxX = -RealLast(); + for (uint32_t aVertexIdx = 0; aVertexIdx < aGraph.Topo().Vertices().Nb(); ++aVertexIdx) + { + const gp_Pnt& aPoint = + aGraph.Topo().Vertices().Definition(BRepGraph_VertexId(aVertexIdx)).Point; + aMinX = std::min(aMinX, aPoint.X()); + aMaxX = std::max(aMaxX, aPoint.X()); + } + + EXPECT_NEAR(aMinX, 0.0, Precision::Confusion()); + EXPECT_NEAR(aMaxX, 11.0, Precision::Confusion()); + + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_WireId::Start()); + ASSERT_FALSE(aRecon.IsNull()); + EXPECT_EQ(countSubShapes(aRecon, TopAbs_EDGE), 2); + EXPECT_EQ(countSubShapes(aRecon, TopAbs_VERTEX), 4); +} + +TEST(BRepGraphIncTest, Populate_BakesStackedNormalLocationsIntoDefinitions) +{ + gp_Trsf anEdgeTrsf; + anEdgeTrsf.SetTranslation(gp_Vec(2.0, 0.0, 0.0)); + const TopLoc_Location anEdgeLoc(anEdgeTrsf); + + BRepBuilderAPI_MakeWire aWireBuilder; + aWireBuilder.Add(TopoDS::Edge( + BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge().Moved(anEdgeLoc))); + aWireBuilder.Add(TopoDS::Edge( + BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge().Moved(anEdgeLoc))); + aWireBuilder.Add(TopoDS::Edge( + BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 1.0, 0.0)).Edge().Moved(anEdgeLoc))); + aWireBuilder.Add(TopoDS::Edge( + BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge().Moved(anEdgeLoc))); + ASSERT_TRUE(aWireBuilder.IsDone()); + + gp_Trsf aWireTrsf; + aWireTrsf.SetTranslation(gp_Vec(0.0, 3.0, 0.0)); + const TopoDS_Wire aMovedWire = + TopoDS::Wire(aWireBuilder.Wire().Moved(TopLoc_Location(aWireTrsf))); + + TopoDS_Face aFace = BRepBuilderAPI_MakeFace(aMovedWire).Face(); + + gp_Trsf aFaceTrsf; + aFaceTrsf.SetTranslation(gp_Vec(0.0, 0.0, 5.0)); + const TopoDS_Face aMovedFace = TopoDS::Face(aFace.Moved(TopLoc_Location(aFaceTrsf))); + + BRep_Builder aBuilder; + TopoDS_Shell aShell; + aBuilder.MakeShell(aShell); + aBuilder.Add(aShell, aMovedFace); + + gp_Trsf aShellTrsf; + aShellTrsf.SetTranslation(gp_Vec(7.0, 0.0, 0.0)); + const TopoDS_Shell aMovedShell = TopoDS::Shell(aShell.Moved(TopLoc_Location(aShellTrsf))); + + TopoDS_Solid aSolid; + aBuilder.MakeSolid(aSolid); + aBuilder.Add(aSolid, aMovedShell); + + gp_Trsf aSolidTrsf; + aSolidTrsf.SetTranslation(gp_Vec(0.0, 11.0, 0.0)); + const TopoDS_Solid aMovedSolid = TopoDS::Solid(aSolid.Moved(TopLoc_Location(aSolidTrsf))); + + BRepGraph aGraph; + ASSERT_NE(BRepGraphInc_Populate::Perform(aGraph, aMovedSolid, false), + BRepGraphInc_Populate::BuildStatus::Failed); + ASSERT_FALSE(aGraph.IsEmpty()); + + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Shells().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Wires().Nb(), 1u); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 4u); + + const TopLoc_Location anIdentity; + for (uint32_t aRefIdx = 0; aRefIdx < aGraph.Refs().Shells().Nb(); ++aRefIdx) + { + EXPECT_TRUE( + aGraph.Refs().Gen().LocalLocation(BRepGraph_ShellRefId(aRefIdx)).IsEqual(anIdentity)); + } + for (uint32_t aRefIdx = 0; aRefIdx < aGraph.Refs().Faces().Nb(); ++aRefIdx) + { + EXPECT_TRUE( + aGraph.Refs().Gen().LocalLocation(BRepGraph_FaceRefId(aRefIdx)).IsEqual(anIdentity)); + } + for (uint32_t aRefIdx = 0; aRefIdx < aGraph.Refs().Wires().Nb(); ++aRefIdx) + { + EXPECT_TRUE( + aGraph.Refs().Gen().LocalLocation(BRepGraph_WireRefId(aRefIdx)).IsEqual(anIdentity)); + } + for (uint32_t aRefIdx = 0; aRefIdx < aGraph.Refs().Vertices().Nb(); ++aRefIdx) + { + EXPECT_TRUE( + aGraph.Refs().Gen().LocalLocation(BRepGraph_VertexRefId(aRefIdx)).IsEqual(anIdentity)); + } + for (uint32_t aRefIdx = 0; aRefIdx < aGraph.Refs().Solids().Nb(); ++aRefIdx) + { + EXPECT_TRUE( + aGraph.Refs().Gen().LocalLocation(BRepGraph_SolidRefId(aRefIdx)).IsEqual(anIdentity)); + } + + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); + ASSERT_FALSE(aRecon.IsNull()); + + double aMinX = RealLast(); + double aMaxX = -RealLast(); + double aMinY = RealLast(); + double aMaxY = -RealLast(); + double aMinZ = RealLast(); + double aMaxZ = -RealLast(); + for (TopExp_Explorer anExp(aRecon, TopAbs_VERTEX); anExp.More(); anExp.Next()) + { + const gp_Pnt aPoint = BRep_Tool::Pnt(TopoDS::Vertex(anExp.Current())); + aMinX = std::min(aMinX, aPoint.X()); + aMaxX = std::max(aMaxX, aPoint.X()); + aMinY = std::min(aMinY, aPoint.Y()); + aMaxY = std::max(aMaxY, aPoint.Y()); + aMinZ = std::min(aMinZ, aPoint.Z()); + aMaxZ = std::max(aMaxZ, aPoint.Z()); + } + + EXPECT_NEAR(aMinX, 9.0, Precision::Confusion()); + EXPECT_NEAR(aMaxX, 10.0, Precision::Confusion()); + EXPECT_NEAR(aMinY, 14.0, Precision::Confusion()); + EXPECT_NEAR(aMaxY, 15.0, Precision::Confusion()); + EXPECT_NEAR(aMinZ, 5.0, Precision::Confusion()); + EXPECT_NEAR(aMaxZ, 5.0, Precision::Confusion()); +} + +TEST(BRepGraphIncTest, Populate_RootCompoundLocationIsPreservedByChildRefs) +{ + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0))); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(25.0, 0.0, 0.0)); + const TopLoc_Location aLoc(aTrsf); + const TopoDS_Compound aMovedCompound = TopoDS::Compound(aCompound.Moved(aLoc)); + + BRepGraph aGraph; + ASSERT_NE(BRepGraphInc_Populate::Perform(aGraph, aMovedCompound, false), + BRepGraphInc_Populate::BuildStatus::Failed); + ASSERT_FALSE(aGraph.IsEmpty()); + + ASSERT_EQ(aGraph.Topo().Compounds().Nb(), 1u); + const NCollection_LinearVector& aChildRefs = + aGraph.Topo().Compounds().Relations(BRepGraph_CompoundId::Start()).ChildRefIds; + ASSERT_EQ(aChildRefs.Size(), 1u); + const BRepGraph_ChildRefId aChildRef = aChildRefs.Value(0); + EXPECT_TRUE(aGraph.Refs().Gen().LocalLocation(aChildRef).IsEqual(aLoc)); + + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_CompoundId::Start()); + ASSERT_FALSE(aRecon.IsNull()); + + double aMinX = RealLast(); + double aMaxX = -RealLast(); + for (TopExp_Explorer anExp(aRecon, TopAbs_VERTEX); anExp.More(); anExp.Next()) + { + const gp_Pnt aPoint = BRep_Tool::Pnt(TopoDS::Vertex(anExp.Current())); + aMinX = std::min(aMinX, aPoint.X()); + aMaxX = std::max(aMaxX, aPoint.X()); + } + + EXPECT_NEAR(aMinX, 25.0, Precision::Confusion()); + EXPECT_NEAR(aMaxX, 26.0, Precision::Confusion()); +} + TEST(BRepGraphIncTest, Cylinder_RoundTrip_BRepDump) { BRepPrimAPI_MakeCylinder aCylMaker(5.0, 20.0); const TopoDS_Shape& aCyl = aCylMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCyl, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCyl, false); + ASSERT_FALSE(aGraph.IsEmpty()); - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_SolidId::Start()); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); ASSERT_FALSE(aRecon.IsNull()); // Dump both shapes and compare @@ -745,12 +992,10 @@ TEST(BRepGraphIncTest, Cylinder_RoundTrip_BRepDump) EXPECT_NEAR(aReconArea, anOrigArea, Precision::Confusion()); } -// ============================================================ -// Edge internal vertices -// ============================================================ +// Supplemental edge/face topology is no longer stored in BRepGraphInc_Storage. +// Live preservation is covered by BRepGraph_LayerTopoSupplement tests. -// Helper: create an edge with a line segment and add vertices with given orientations. -static TopoDS_Edge makeEdgeWithInternalVertex() +static TopoDS_Edge makeIncEdgeWithInternalVertex() { BRep_Builder aBB; BRepBuilderAPI_MakeEdge aMakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); @@ -762,8 +1007,7 @@ static TopoDS_Edge makeEdgeWithInternalVertex() return anEdge; } -// Helper: build a face containing the given edge (needed for graph population). -static TopoDS_Shape wrapEdgeInFace(const TopoDS_Edge& theEdge) +static TopoDS_Shape wrapIncEdgeInFace(const TopoDS_Edge& theEdge) { occ::handle aPlane = new Geom_Plane(gp_Pln()); BRep_Builder aBB; @@ -776,372 +1020,135 @@ static TopoDS_Shape wrapEdgeInFace(const TopoDS_Edge& theEdge) return aFace; } -TEST(BRepGraphIncTest, EdgeInternalVertex_Captured) +static TopoDS_Face makeIncFaceWithDirectVertex() { - TopoDS_Edge anEdge = makeEdgeWithInternalVertex(); - TopoDS_Shape aFace = wrapEdgeInFace(anEdge); + BRep_Builder aBB; - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aFace, false); - ASSERT_TRUE(aStorage.GetIsDone()); - ASSERT_GE(aStorage.NbEdges(), 1); + TopoDS_Vertex aV0; + TopoDS_Vertex aV1; + TopoDS_Vertex aV2; + TopoDS_Vertex aV3; + aBB.MakeVertex(aV0, gp_Pnt(0, 0, 0), Precision::Confusion()); + aBB.MakeVertex(aV1, gp_Pnt(10, 0, 0), Precision::Confusion()); + aBB.MakeVertex(aV2, gp_Pnt(10, 10, 0), Precision::Confusion()); + aBB.MakeVertex(aV3, gp_Pnt(0, 10, 0), Precision::Confusion()); - // Find the edge entity and check InternalVertices. - bool aFound = false; - const int aNbEdges = aStorage.NbEdges(); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdgeEnt = aStorage.Edge(anEdgeId); - if (anEdgeEnt.InternalVertexRefIds.Length() == 1) - { - aFound = true; - const BRepGraphInc::VertexRef& aIntVRef = - aStorage.VertexRef(anEdgeEnt.InternalVertexRefIds.Value(0)); - EXPECT_GE(aIntVRef.VertexDefId.Index, 0); - EXPECT_EQ(aIntVRef.Orientation, TopAbs_INTERNAL); - // Verify the vertex point. - const BRepGraph_VertexId aVtxId = aIntVRef.VertexDefId; - const BRepGraphInc::VertexDef& aVtxEnt = aStorage.Vertex(aVtxId); - EXPECT_NEAR(aVtxEnt.Point.X(), 5.0, Precision::Confusion()); - break; - } - } - EXPECT_TRUE(aFound) << "No edge with InternalVertices found"; -} - -TEST(BRepGraphIncTest, EdgeInternalVertex_RoundTrip) -{ - TopoDS_Edge anEdge = makeEdgeWithInternalVertex(); - TopoDS_Shape aFace = wrapEdgeInFace(anEdge); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aFace, false); - ASSERT_TRUE(aStorage.GetIsDone()); - - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_FaceId::Start()); - ASSERT_FALSE(aRecon.IsNull()); - - // Find the edge in the reconstructed face and verify internal vertex. - bool aFoundInternal = false; - for (TopExp_Explorer anEdgeExp(aRecon, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) - { - const TopoDS_Edge& aReconEdge = TopoDS::Edge(anEdgeExp.Current()); - for (TopoDS_Iterator aVIt(aReconEdge, false); aVIt.More(); aVIt.Next()) - { - if (aVIt.Value().ShapeType() == TopAbs_VERTEX - && aVIt.Value().Orientation() == TopAbs_INTERNAL) - { - aFoundInternal = true; - const TopoDS_Vertex& aVtx = TopoDS::Vertex(aVIt.Value()); - EXPECT_NEAR(BRep_Tool::Pnt(aVtx).X(), 5.0, Precision::Confusion()); - } - } - } - EXPECT_TRUE(aFoundInternal) << "Internal vertex not found in reconstructed edge"; -} - -TEST(BRepGraphIncTest, EdgeExternalVertex_Captured) -{ - BRep_Builder aBB; - BRepBuilderAPI_MakeEdge aMakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Edge anEdge = aMakeEdge.Edge(); - - TopoDS_Vertex anExtVtx; - aBB.MakeVertex(anExtVtx, gp_Pnt(7, 0, 0), Precision::Confusion()); - aBB.Add(anEdge, anExtVtx.Oriented(TopAbs_EXTERNAL)); - - TopoDS_Shape aFace = wrapEdgeInFace(anEdge); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aFace, false); - ASSERT_TRUE(aStorage.GetIsDone()); - - bool aFound = false; - const int aNbEdges = aStorage.NbEdges(); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdgeEnt = aStorage.Edge(anEdgeId); - if (anEdgeEnt.InternalVertexRefIds.Length() == 1) - { - aFound = true; - EXPECT_EQ(aStorage.VertexRef(anEdgeEnt.InternalVertexRefIds.Value(0)).Orientation, - TopAbs_EXTERNAL); - break; - } - } - EXPECT_TRUE(aFound) << "No edge with EXTERNAL vertex found"; -} - -TEST(BRepGraphIncTest, EdgeNoInternalVertices_EmptyVector) -{ - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - const TopoDS_Shape& aBox = aBoxMaker.Shape(); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); - - const int aNbEdges = aStorage.NbEdges(); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - EXPECT_EQ(aStorage.Edge(anEdgeId).InternalVertexRefIds.Length(), 0) - << "Edge " << anEdgeId.Index << " should have no internal vertices"; - } -} - -TEST(BRepGraphIncTest, EdgeMultipleInternalVertices_AllCaptured) -{ - BRep_Builder aBB; - BRepBuilderAPI_MakeEdge aMakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Edge anEdge = aMakeEdge.Edge(); - - TopoDS_Vertex aVtx1, aVtx2; - aBB.MakeVertex(aVtx1, gp_Pnt(3, 0, 0), Precision::Confusion()); - aBB.MakeVertex(aVtx2, gp_Pnt(7, 0, 0), Precision::Confusion()); - aBB.Add(anEdge, aVtx1.Oriented(TopAbs_INTERNAL)); - aBB.Add(anEdge, aVtx2.Oriented(TopAbs_EXTERNAL)); - - TopoDS_Shape aFace = wrapEdgeInFace(anEdge); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aFace, false); - ASSERT_TRUE(aStorage.GetIsDone()); - - bool aFound = false; - const int aNbEdges = aStorage.NbEdges(); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdgeEnt = aStorage.Edge(anEdgeId); - if (anEdgeEnt.InternalVertexRefIds.Length() == 2) - { - aFound = true; - // Check both orientations are preserved. - bool aHasInternal = false, aHasExternal = false; - for (int j = 0; j < 2; ++j) - { - if (aStorage.VertexRef(anEdgeEnt.InternalVertexRefIds.Value(j)).Orientation - == TopAbs_INTERNAL) - { - aHasInternal = true; - } - if (aStorage.VertexRef(anEdgeEnt.InternalVertexRefIds.Value(j)).Orientation - == TopAbs_EXTERNAL) - { - aHasExternal = true; - } - } - EXPECT_TRUE(aHasInternal); - EXPECT_TRUE(aHasExternal); - break; - } - } - EXPECT_TRUE(aFound) << "No edge with 2 internal vertices found"; -} - -// ============================================================ -// Face direct vertices -// ============================================================ - -TEST(BRepGraphIncTest, FaceDirectVertex_Internal_Captured) -{ - BRep_Builder aBB; - occ::handle aPlane = new Geom_Plane(gp_Pln()); - TopoDS_Face aFace; - aBB.MakeFace(aFace, aPlane, Precision::Confusion()); - - // Add a wire so the face is valid for population. - BRepBuilderAPI_MakeEdge aME(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Wire aWire; - aBB.MakeWire(aWire); - aBB.Add(aWire, aME.Edge()); - aBB.Add(aFace, aWire); - - // Add a direct vertex child with INTERNAL orientation. - TopoDS_Vertex aVtx; - aBB.MakeVertex(aVtx, gp_Pnt(5, 5, 0), Precision::Confusion()); - aBB.Add(aFace, aVtx.Oriented(TopAbs_INTERNAL)); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aFace, false); - ASSERT_TRUE(aStorage.GetIsDone()); - ASSERT_GE(aStorage.NbFaces(), 1); - - const BRepGraphInc::FaceDef& aFaceEnt = aStorage.Face(BRepGraph_FaceId::Start()); - EXPECT_EQ(aFaceEnt.VertexRefIds.Length(), 1); - if (aFaceEnt.VertexRefIds.Length() == 1) - { - const BRepGraphInc::VertexRef& aFaceVRef = aStorage.VertexRef(aFaceEnt.VertexRefIds.Value(0)); - EXPECT_GE(aFaceVRef.VertexDefId.Index, 0); - EXPECT_EQ(aFaceVRef.Orientation, TopAbs_INTERNAL); - } -} - -TEST(BRepGraphIncTest, FaceDirectVertex_RoundTrip) -{ - BRep_Builder aBB; - occ::handle aPlane = new Geom_Plane(gp_Pln()); - TopoDS_Face aFace; - aBB.MakeFace(aFace, aPlane, Precision::Confusion()); - - BRepBuilderAPI_MakeEdge aME(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Wire aWire; - aBB.MakeWire(aWire); - aBB.Add(aWire, aME.Edge()); - aBB.Add(aFace, aWire); - - TopoDS_Vertex aVtx; - aBB.MakeVertex(aVtx, gp_Pnt(5, 5, 0), Precision::Confusion()); - aBB.Add(aFace, aVtx.Oriented(TopAbs_INTERNAL)); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aFace, false); - ASSERT_TRUE(aStorage.GetIsDone()); - - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_FaceId::Start()); - ASSERT_FALSE(aRecon.IsNull()); - - // Verify vertex is a direct child of the face (not inside a wire). - bool aFoundDirect = false; - for (TopoDS_Iterator aIt(aRecon); aIt.More(); aIt.Next()) - { - if (aIt.Value().ShapeType() == TopAbs_VERTEX && aIt.Value().Orientation() == TopAbs_INTERNAL) - { - aFoundDirect = true; - const TopoDS_Vertex& aReconVtx = TopoDS::Vertex(aIt.Value()); - EXPECT_NEAR(BRep_Tool::Pnt(aReconVtx).X(), 5.0, Precision::Confusion()); - EXPECT_NEAR(BRep_Tool::Pnt(aReconVtx).Y(), 5.0, Precision::Confusion()); - } - } - EXPECT_TRUE(aFoundDirect) << "Direct internal vertex not found in reconstructed face"; -} - -TEST(BRepGraphIncTest, FaceExternalVertex_Captured) -{ - BRep_Builder aBB; - occ::handle aPlane = new Geom_Plane(gp_Pln()); - TopoDS_Face aFace; - aBB.MakeFace(aFace, aPlane, Precision::Confusion()); - - BRepBuilderAPI_MakeEdge aME(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Wire aWire; - aBB.MakeWire(aWire); - aBB.Add(aWire, aME.Edge()); - aBB.Add(aFace, aWire); - - TopoDS_Vertex aVtx; - aBB.MakeVertex(aVtx, gp_Pnt(5, 5, 0), Precision::Confusion()); - aBB.Add(aFace, aVtx.Oriented(TopAbs_EXTERNAL)); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aFace, false); - ASSERT_TRUE(aStorage.GetIsDone()); - ASSERT_GE(aStorage.NbFaces(), 1); - - const BRepGraphInc::FaceDef& aFaceEnt = aStorage.Face(BRepGraph_FaceId::Start()); - EXPECT_EQ(aFaceEnt.VertexRefIds.Length(), 1); - if (aFaceEnt.VertexRefIds.Length() == 1) - { - EXPECT_EQ(aStorage.VertexRef(aFaceEnt.VertexRefIds.Value(0)).Orientation, TopAbs_EXTERNAL); - } -} - -TEST(BRepGraphIncTest, FaceNoDirectVertices_EmptyVector) -{ - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - const TopoDS_Shape& aBox = aBoxMaker.Shape(); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); - - const int aNbFaces = aStorage.NbFaces(); - for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) - { - EXPECT_EQ(aStorage.Face(aFaceId).VertexRefIds.Length(), 0) - << "Face " << aFaceId.Index << " should have no direct vertex children"; - } -} - -TEST(BRepGraphIncTest, FaceWithWiresAndVertices_BothCaptured) -{ - BRep_Builder aBB; - occ::handle aPlane = new Geom_Plane(gp_Pln()); - TopoDS_Face aFace; - aBB.MakeFace(aFace, aPlane, Precision::Confusion()); - - BRepBuilderAPI_MakeEdge aME(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Wire aWire; - aBB.MakeWire(aWire); - aBB.Add(aWire, aME.Edge()); - aBB.Add(aFace, aWire); - - TopoDS_Vertex aVtx; - aBB.MakeVertex(aVtx, gp_Pnt(5, 5, 0), Precision::Confusion()); - aBB.Add(aFace, aVtx.Oriented(TopAbs_INTERNAL)); - - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aFace, false); - ASSERT_TRUE(aStorage.GetIsDone()); - ASSERT_GE(aStorage.NbFaces(), 1); - - const BRepGraphInc::FaceDef& aFaceEnt = aStorage.Face(BRepGraph_FaceId::Start()); - EXPECT_GE(BRepGraph_TestTools::CountWireRefsOfFace(aStorage, BRepGraph_FaceId::Start()), 1); - EXPECT_EQ(aFaceEnt.VertexRefIds.Length(), 1); -} - -// ============================================================ -// Integration: round-trip with internal vertices -// ============================================================ - -TEST(BRepGraphIncTest, CompoundWithInternalVertices_RoundTrip_SubShapeCounts) -{ - BRep_Builder aBB; - TopoDS_Compound aCompound; - aBB.MakeCompound(aCompound); - - // Face with a direct internal vertex. - occ::handle aPlane = new Geom_Plane(gp_Pln()); - TopoDS_Face aFace; - aBB.MakeFace(aFace, aPlane, Precision::Confusion()); - - BRepBuilderAPI_MakeEdge aME(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Edge anEdge = aME.Edge(); - // Add internal vertex to the edge. - TopoDS_Vertex anEdgeIntVtx; - aBB.MakeVertex(anEdgeIntVtx, gp_Pnt(5, 0, 0), Precision::Confusion()); - aBB.Add(anEdge, anEdgeIntVtx.Oriented(TopAbs_INTERNAL)); + TopoDS_Edge aE0; + TopoDS_Edge aE1; + TopoDS_Edge aE2; + TopoDS_Edge aE3; + aBB.MakeEdge(aE0); + aBB.MakeEdge(aE1); + aBB.MakeEdge(aE2); + aBB.MakeEdge(aE3); + aBB.Add(aE0, aV0.Oriented(TopAbs_FORWARD)); + aBB.Add(aE0, aV1.Oriented(TopAbs_REVERSED)); + aBB.Add(aE1, aV1.Oriented(TopAbs_FORWARD)); + aBB.Add(aE1, aV2.Oriented(TopAbs_REVERSED)); + aBB.Add(aE2, aV2.Oriented(TopAbs_FORWARD)); + aBB.Add(aE2, aV3.Oriented(TopAbs_REVERSED)); + aBB.Add(aE3, aV3.Oriented(TopAbs_FORWARD)); + aBB.Add(aE3, aV0.Oriented(TopAbs_REVERSED)); TopoDS_Wire aWire; aBB.MakeWire(aWire); - aBB.Add(aWire, anEdge); + aBB.Add(aWire, aE0); + aBB.Add(aWire, aE1); + aBB.Add(aWire, aE2); + aBB.Add(aWire, aE3); + aWire.Closed(true); + + TopoDS_Face aFace; + aBB.MakeFace(aFace); aBB.Add(aFace, aWire); - // Add direct vertex to the face. - TopoDS_Vertex aFaceIntVtx; - aBB.MakeVertex(aFaceIntVtx, gp_Pnt(5, 5, 0), Precision::Confusion()); - aBB.Add(aFace, aFaceIntVtx.Oriented(TopAbs_INTERNAL)); + TopoDS_Vertex aLooseVertex; + aBB.MakeVertex(aLooseVertex, gp_Pnt(5, 5, 0), Precision::Confusion()); + aBB.Add(aFace, aLooseVertex.Oriented(TopAbs_INTERNAL)); + return aFace; +} - aBB.Add(aCompound, aFace); +static TopoDS_Face makeIncPlainFace() +{ + return BRepBuilderAPI_MakeFace(gp_Pln(), 0.0, 10.0, 0.0, 10.0).Face(); +} - // Count original sub-shapes. - int anOrigFaces = countSubShapes(aCompound, TopAbs_FACE); - int anOrigEdges = countSubShapes(aCompound, TopAbs_EDGE); - int anOrigVertices = countSubShapes(aCompound, TopAbs_VERTEX); +static TopoDS_Shell makeIncPlainShell() +{ + BRep_Builder aBB; + TopoDS_Shell aShell; + aBB.MakeShell(aShell); + aBB.Add(aShell, makeIncPlainFace()); + return aShell; +} - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); +static TopoDS_Solid makeIncPlainSolid() +{ + BRep_Builder aBB; + TopoDS_Solid aSolid; + aBB.MakeSolid(aSolid); + aBB.Add(aSolid, makeIncPlainShell()); + return aSolid; +} - TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aStorage, BRepGraph_CompoundId::Start()); +static uint32_t countDirectChildren(const TopoDS_Shape& theShape, + const TopAbs_ShapeEnum theType, + const TopAbs_Orientation theOrientation) +{ + uint32_t aCount = 0; + for (TopoDS_Iterator aChildIt(theShape, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + if (aChild.ShapeType() == theType && aChild.Orientation() == theOrientation) + { + ++aCount; + } + } + return aCount; +} + +TEST(BRepGraphIncTest, Populate_RegistersSupplementEdgeVerticesIntoLayer) +{ + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + + std::ignore = BRepGraphInc_Populate::Perform(aGraph, + wrapIncEdgeInFace(makeIncEdgeWithInternalVertex()), + false); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_EdgeId aEdgeId; + if (aGraph.Topo().Edges().Nb() > 0) + { + aEdgeId = BRepGraph_EdgeId::Start(); + } + ASSERT_TRUE(aEdgeId.IsValid()); + + const NCollection_LinearVector& anAttached = aSupplement->AttachedTo(aEdgeId); + ASSERT_EQ(anAttached.Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aSupplement->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::EdgeInternalVertex); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_VERTEX); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); +} + +TEST(BRepGraphIncTest, Reconstruct_ReplaysSupplementEdgeVerticesFromLayer) +{ + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + + std::ignore = BRepGraphInc_Populate::Perform(aGraph, + wrapIncEdgeInFace(makeIncEdgeWithInternalVertex()), + false); + ASSERT_FALSE(aGraph.IsEmpty()); + + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_FaceId::Start()); ASSERT_FALSE(aRecon.IsNull()); - EXPECT_EQ(countSubShapes(aRecon, TopAbs_FACE), anOrigFaces); - EXPECT_EQ(countSubShapes(aRecon, TopAbs_EDGE), anOrigEdges); - EXPECT_EQ(countSubShapes(aRecon, TopAbs_VERTEX), anOrigVertices); - - // Verify orientations of reconstructed internal vertices. - // Edge internal vertex: INTERNAL orientation on edge child. - bool aFoundEdgeIntVtx = false; + bool aFoundInternal = false; for (TopExp_Explorer anEdgeExp(aRecon, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) { for (TopoDS_Iterator aVIt(anEdgeExp.Current(), false); aVIt.More(); aVIt.Next()) @@ -1149,93 +1156,278 @@ TEST(BRepGraphIncTest, CompoundWithInternalVertices_RoundTrip_SubShapeCounts) if (aVIt.Value().ShapeType() == TopAbs_VERTEX && aVIt.Value().Orientation() == TopAbs_INTERNAL) { - aFoundEdgeIntVtx = true; - const TopoDS_Vertex& aVtx = TopoDS::Vertex(aVIt.Value()); - EXPECT_NEAR(BRep_Tool::Pnt(aVtx).X(), 5.0, Precision::Confusion()); - EXPECT_NEAR(BRep_Tool::Pnt(aVtx).Y(), 0.0, Precision::Confusion()); + aFoundInternal = true; } } } - EXPECT_TRUE(aFoundEdgeIntVtx) << "INTERNAL vertex on edge not found after round-trip"; - - // Face direct vertex: INTERNAL orientation as direct child of face. - bool aFoundFaceIntVtx = false; - for (TopExp_Explorer aFaceExp(aRecon, TopAbs_FACE); aFaceExp.More(); aFaceExp.Next()) - { - for (TopoDS_Iterator aFIt(aFaceExp.Current()); aFIt.More(); aFIt.Next()) - { - if (aFIt.Value().ShapeType() == TopAbs_VERTEX - && aFIt.Value().Orientation() == TopAbs_INTERNAL) - { - aFoundFaceIntVtx = true; - const TopoDS_Vertex& aVtx = TopoDS::Vertex(aFIt.Value()); - EXPECT_NEAR(BRep_Tool::Pnt(aVtx).X(), 5.0, Precision::Confusion()); - EXPECT_NEAR(BRep_Tool::Pnt(aVtx).Y(), 5.0, Precision::Confusion()); - } - } - } - EXPECT_TRUE(aFoundFaceIntVtx) << "INTERNAL vertex on face not found after round-trip"; + EXPECT_TRUE(aFoundInternal); } -TEST(BRepGraphIncTest, ParallelBuild_InternalVertices_SameAsSequential) +TEST(BRepGraphIncTest, Reconstruct_WithoutSupplementLayer_DropsSupplementEdgeVerticesButKeepsCore) { - BRep_Builder aBB; - occ::handle aPlane = new Geom_Plane(gp_Pln()); - TopoDS_Face aFace; - aBB.MakeFace(aFace, aPlane, Precision::Confusion()); + // No supplement layer registered - internal vertices should be dropped. + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, + wrapIncEdgeInFace(makeIncEdgeWithInternalVertex()), + false); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepBuilderAPI_MakeEdge aME(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Edge anEdge = aME.Edge(); - TopoDS_Vertex anIntVtx; - aBB.MakeVertex(anIntVtx, gp_Pnt(5, 0, 0), Precision::Confusion()); - aBB.Add(anEdge, anIntVtx.Oriented(TopAbs_INTERNAL)); + TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_FaceId::Start()); + ASSERT_FALSE(aRecon.IsNull()); - TopoDS_Wire aWire; + uint32_t aFoundEdges = 0; + uint32_t aFoundBoundaries = 0; + uint32_t aFoundInternal = 0; + for (TopExp_Explorer anEdgeExp(aRecon, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) + { + ++aFoundEdges; + for (TopoDS_Iterator aVIt(anEdgeExp.Current(), false); aVIt.More(); aVIt.Next()) + { + if (aVIt.Value().ShapeType() != TopAbs_VERTEX) + { + continue; + } + if (aVIt.Value().Orientation() == TopAbs_FORWARD + || aVIt.Value().Orientation() == TopAbs_REVERSED) + { + ++aFoundBoundaries; + } + else if (aVIt.Value().Orientation() == TopAbs_INTERNAL) + { + ++aFoundInternal; + } + } + } + + EXPECT_EQ(aFoundEdges, 1); + EXPECT_EQ(aFoundBoundaries, 2); + EXPECT_EQ(aFoundInternal, 0); +} + +TEST(BRepGraphIncTest, Reconstruct_EdgeNode_ReplaysSupplementEdgeVerticesFromLayer) +{ + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + + std::ignore = BRepGraphInc_Populate::Perform(aGraph, makeIncEdgeWithInternalVertex(), false); + ASSERT_FALSE(aGraph.IsEmpty()); + ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); + + const TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_EdgeId::Start()); + ASSERT_FALSE(aRecon.IsNull()); + + int aFoundInternal = 0; + for (TopoDS_Iterator aVIt(aRecon, false); aVIt.More(); aVIt.Next()) + { + if (aVIt.Value().ShapeType() == TopAbs_VERTEX && aVIt.Value().Orientation() == TopAbs_INTERNAL) + { + ++aFoundInternal; + } + } + + EXPECT_EQ(aFoundInternal, 1); +} + +TEST(BRepGraphIncTest, Reconstruct_WireNode_ReplaysSupplementEdgeVerticesFromLayer) +{ + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + + BRep_Builder aBB; + TopoDS_Wire aWire; aBB.MakeWire(aWire); - aBB.Add(aWire, anEdge); - aBB.Add(aFace, aWire); + aBB.Add(aWire, makeIncEdgeWithInternalVertex()); - TopoDS_Vertex aFaceVtx; - aBB.MakeVertex(aFaceVtx, gp_Pnt(5, 5, 0), Precision::Confusion()); - aBB.Add(aFace, aFaceVtx.Oriented(TopAbs_INTERNAL)); + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aWire, false); + ASSERT_FALSE(aGraph.IsEmpty()); + ASSERT_GT(aGraph.Topo().Wires().Nb(), 0); - BRepGraphInc_Storage aSerial; - BRepGraphInc_Populate::Perform(aSerial, aFace, false); - ASSERT_TRUE(aSerial.GetIsDone()); + const TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_WireId::Start()); + ASSERT_FALSE(aRecon.IsNull()); - BRepGraphInc_Storage aParallel; - BRepGraphInc_Populate::Perform(aParallel, aFace, true); - ASSERT_TRUE(aParallel.GetIsDone()); - - EXPECT_EQ(aParallel.NbVertices(), aSerial.NbVertices()); - EXPECT_EQ(aParallel.NbEdges(), aSerial.NbEdges()); - EXPECT_EQ(aParallel.NbFaces(), aSerial.NbFaces()); - - // Check internal vertex counts match. - const int aNbEdges = aSerial.NbEdges(); - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) + int aFoundInternal = 0; + for (TopExp_Explorer anEdgeExp(aRecon, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) { - EXPECT_EQ(aParallel.Edge(anEdgeId).InternalVertexRefIds.Length(), - aSerial.Edge(anEdgeId).InternalVertexRefIds.Length()) - << "Edge " << anEdgeId.Index << " internal vertex count mismatch"; + for (TopoDS_Iterator aVIt(anEdgeExp.Current(), false); aVIt.More(); aVIt.Next()) + { + if (aVIt.Value().ShapeType() == TopAbs_VERTEX + && aVIt.Value().Orientation() == TopAbs_INTERNAL) + { + ++aFoundInternal; + } + } } - const int aNbFaces = aSerial.NbFaces(); - for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) + + EXPECT_EQ(aFoundInternal, 1); +} + +TEST(BRepGraphIncTest, Reconstruct_FaceNode_ReplaysSupplementFaceVerticesFromLayer) +{ + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + + std::ignore = BRepGraphInc_Populate::Perform(aGraph, makeIncFaceWithDirectVertex(), false); + ASSERT_FALSE(aGraph.IsEmpty()); + ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); + + const NCollection_LinearVector& anAttached = + aSupplement->AttachedTo(BRepGraph_FaceId::Start()); + ASSERT_EQ(anAttached.Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aSupplement->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::FaceDirectVertex); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_VERTEX); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_FaceId::Start()); + ASSERT_FALSE(aRecon.IsNull()); + + int aFoundInternal = 0; + int aFoundWires = 0; + for (TopoDS_Iterator aChildIt(aRecon, false, false); aChildIt.More(); aChildIt.Next()) { - EXPECT_EQ(aParallel.Face(aFaceId).VertexRefIds.Length(), - aSerial.Face(aFaceId).VertexRefIds.Length()) - << "Face " << aFaceId.Index << " direct vertex count mismatch"; + if (aChildIt.Value().ShapeType() == TopAbs_WIRE) + { + ++aFoundWires; + } + else if (aChildIt.Value().ShapeType() == TopAbs_VERTEX + && aChildIt.Value().Orientation() == TopAbs_INTERNAL) + { + ++aFoundInternal; + } } + + EXPECT_EQ(aFoundWires, 1); + EXPECT_EQ(aFoundInternal, 1); +} + +TEST(BRepGraphIncTest, Populate_ShellRoutesInternalFaceToSupplement) +{ + BRep_Builder aBB; + TopoDS_Shell aShell; + aBB.MakeShell(aShell); + aBB.Add(aShell, makeIncPlainFace().Oriented(TopAbs_FORWARD)); + aBB.Add(aShell, makeIncPlainFace().Oriented(TopAbs_INTERNAL)); + + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + ASSERT_NO_THROW({ std::ignore = BRepGraphInc_Populate::Perform(aGraph, aShell, false); }); + ASSERT_EQ(aGraph.Topo().Shells().Nb(), 1); + EXPECT_EQ(aGraph.Topo().Shells().Relations(BRepGraph_ShellId::Start()).FaceRefIds.Size(), 1); + + const NCollection_LinearVector& anAttached = + aSupplement->AttachedTo(BRepGraph_ShellId::Start()); + ASSERT_EQ(anAttached.Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aSupplement->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_FACE); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_ShellId::Start()); + ASSERT_FALSE(aRecon.IsNull()); + EXPECT_EQ(countDirectChildren(aRecon, TopAbs_FACE, TopAbs_FORWARD), 1); + EXPECT_EQ(countDirectChildren(aRecon, TopAbs_FACE, TopAbs_INTERNAL), 1); +} + +TEST(BRepGraphIncTest, Populate_SolidRoutesInternalShellToSupplement) +{ + BRep_Builder aBB; + TopoDS_Solid aSolid; + aBB.MakeSolid(aSolid); + aBB.Add(aSolid, makeIncPlainShell().Oriented(TopAbs_FORWARD)); + aBB.Add(aSolid, makeIncPlainShell().Oriented(TopAbs_INTERNAL)); + + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + ASSERT_NO_THROW({ std::ignore = BRepGraphInc_Populate::Perform(aGraph, aSolid, false); }); + ASSERT_EQ(aGraph.Topo().Solids().Nb(), 1); + EXPECT_EQ(aGraph.Topo().Solids().Relations(BRepGraph_SolidId::Start()).ShellRefIds.Size(), 1); + + const NCollection_LinearVector& anAttached = + aSupplement->AttachedTo(BRepGraph_SolidId::Start()); + ASSERT_EQ(anAttached.Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aSupplement->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_SHELL); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); + ASSERT_FALSE(aRecon.IsNull()); + EXPECT_EQ(countDirectChildren(aRecon, TopAbs_SHELL, TopAbs_FORWARD), 1); + EXPECT_EQ(countDirectChildren(aRecon, TopAbs_SHELL, TopAbs_INTERNAL), 1); +} + +TEST(BRepGraphIncTest, Populate_CompSolidRoutesInternalSolidToSupplement) +{ + BRep_Builder aBB; + TopoDS_CompSolid aCompSolid; + aBB.MakeCompSolid(aCompSolid); + aBB.Add(aCompSolid, makeIncPlainSolid().Oriented(TopAbs_FORWARD)); + aBB.Add(aCompSolid, makeIncPlainSolid().Oriented(TopAbs_INTERNAL)); + + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + ASSERT_NO_THROW({ std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompSolid, false); }); + ASSERT_EQ(aGraph.Topo().CompSolids().Nb(), 1); + EXPECT_EQ(aGraph.Topo().CompSolids().Relations(BRepGraph_CompSolidId::Start()).SolidRefIds.Size(), + 1); + + const NCollection_LinearVector& anAttached = + aSupplement->AttachedTo(BRepGraph_CompSolidId::Start()); + ASSERT_EQ(anAttached.Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aSupplement->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_SOLID); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRecon = + BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_CompSolidId::Start()); + ASSERT_FALSE(aRecon.IsNull()); + EXPECT_EQ(countDirectChildren(aRecon, TopAbs_SOLID, TopAbs_FORWARD), 1); + EXPECT_EQ(countDirectChildren(aRecon, TopAbs_SOLID, TopAbs_INTERNAL), 1); +} + +TEST(BRepGraphIncTest, Populate_SolidInvalidOrderChildRoutesToSupplement) +{ + BRep_Builder aBB; + TopoDS_Solid aSolid; + aBB.MakeSolid(aSolid); + aBB.Add(aSolid, makeIncEdgeWithInternalVertex().Oriented(TopAbs_FORWARD)); + + BRepGraph aGraph; + occ::handle aSupplement = + aGraph.LayerRegistry().Ensure(); + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aSolid, false); + ASSERT_EQ(aGraph.Topo().Solids().Nb(), 1); + EXPECT_EQ(aGraph.Topo().Solids().Relations(BRepGraph_SolidId::Start()).ShellRefIds.Size(), 0); + ASSERT_EQ(aSupplement->AttachedTo(BRepGraph_SolidId::Start()).Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = + aSupplement->FindByUid(aSupplement->AttachedTo(BRepGraph_SolidId::Start()).First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_EDGE); + + const TopoDS_Shape aRecon = BRepGraphInc_Reconstruct::Node(aGraph, BRepGraph_SolidId::Start()); + EXPECT_EQ(countDirectChildren(aRecon, TopAbs_EDGE, TopAbs_FORWARD), 1); } // ============================================================ // Reverse-index hardening: compound atomic children (Wire/Edge/Vertex) // ============================================================ -TEST(BRepGraphIncTest, ReverseIndex_Validate_CompoundWithAtomicWire) +TEST(BRepGraphIncTest, Relations_Validate_CompoundWithAtomicWire) { // Build a compound that contains only a wire (no solid/shell/face). - // This exercises the Wire-child path in compound reverse-index maintenance. + // This exercises the Wire-child path in compound relation-table maintenance. BRep_Builder aBB; TopoDS_Compound aCompound; aBB.MakeCompound(aCompound); @@ -1248,28 +1440,19 @@ TEST(BRepGraphIncTest, ReverseIndex_Validate_CompoundWithAtomicWire) aBB.Add(aWire, aME.Edge()); aBB.Add(aCompound, aWire); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompound, false); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_EQ(aStorage.NbCompounds(), 1); - ASSERT_GE(aStorage.NbWires(), 1); - - // The wire must appear in myCompoundsOfWire. - const NCollection_DynamicArray* aCmpVec = - aStorage.ReverseIndex().CompoundsOfWire(BRepGraph_WireId::Start()); - EXPECT_NE(aCmpVec, nullptr) << "Wire(0) should appear in myCompoundsOfWire"; - if (aCmpVec != nullptr) - { - EXPECT_EQ(aCmpVec->Length(), 1); - EXPECT_EQ(aCmpVec->Value(0).Index, 0); - } + EXPECT_EQ(aGraph.Topo().Compounds().Nb(), 1u); + ASSERT_GE(aGraph.Topo().Wires().Nb(), 1u); // Full consistency check. - EXPECT_TRUE(aStorage.ValidateReverseIndex()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); } -TEST(BRepGraphIncTest, ReverseIndex_Validate_CompoundWithAtomicEdge) +TEST(BRepGraphIncTest, Relations_Validate_CompoundWithAtomicEdge) { // Build a compound containing only an edge (no wire/face/solid). BRep_Builder aBB; @@ -1280,26 +1463,18 @@ TEST(BRepGraphIncTest, ReverseIndex_Validate_CompoundWithAtomicEdge) ASSERT_TRUE(aME.IsDone()); aBB.Add(aCompound, aME.Edge()); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompound, false); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_EQ(aStorage.NbCompounds(), 1); - ASSERT_GE(aStorage.NbEdges(), 1); + EXPECT_EQ(aGraph.Topo().Compounds().Nb(), 1u); + ASSERT_GE(aGraph.Topo().Edges().Nb(), 1u); - const NCollection_DynamicArray* aCmpVec = - aStorage.ReverseIndex().CompoundsOfEdge(BRepGraph_EdgeId::Start()); - EXPECT_NE(aCmpVec, nullptr) << "Edge(0) should appear in myCompoundsOfEdge"; - if (aCmpVec != nullptr) - { - EXPECT_EQ(aCmpVec->Length(), 1); - EXPECT_EQ(aCmpVec->Value(0).Index, 0); - } - - EXPECT_TRUE(aStorage.ValidateReverseIndex()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); } -TEST(BRepGraphIncTest, ReverseIndex_Validate_CompoundWithAtomicVertex) +TEST(BRepGraphIncTest, Relations_Validate_CompoundWithAtomicVertex) { // Build a compound containing only a vertex. BRep_Builder aBB; @@ -1310,62 +1485,44 @@ TEST(BRepGraphIncTest, ReverseIndex_Validate_CompoundWithAtomicVertex) aBB.MakeVertex(aVtx, gp_Pnt(1, 2, 3), Precision::Confusion()); aBB.Add(aCompound, aVtx); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompound, false); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_EQ(aStorage.NbCompounds(), 1); - ASSERT_GE(aStorage.NbVertices(), 1); + EXPECT_EQ(aGraph.Topo().Compounds().Nb(), 1u); + ASSERT_GE(aGraph.Topo().Vertices().Nb(), 1u); - const NCollection_DynamicArray* aCmpVec = - aStorage.ReverseIndex().CompoundsOfVertex(BRepGraph_VertexId::Start()); - EXPECT_NE(aCmpVec, nullptr) << "Vertex(0) should appear in myCompoundsOfVertex"; - if (aCmpVec != nullptr) - { - EXPECT_EQ(aCmpVec->Length(), 1); - EXPECT_EQ(aCmpVec->Value(0).Index, 0); - } - - EXPECT_TRUE(aStorage.ValidateReverseIndex()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); } -TEST(BRepGraphIncTest, ReverseIndex_CoEdgeToWire_IsPopulated) +TEST(BRepGraphIncTest, Relations_CoEdgeToWire_IsPopulated) { - // Every coedge in a box must map to exactly one wire in myCoEdgeToWires. + // Every coedge in a box must carry exactly one parent wire. BRepPrimAPI_MakeBox aBoxMaker(5.0, 5.0, 5.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); - const int aNbCoEdges = aStorage.NbCoEdges(); + const uint32_t aNbCoEdges = aGraph.Topo().CoEdges().Nb(); ASSERT_GT(aNbCoEdges, 0); for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aNbCoEdges); ++aCoEdgeId) { - const BRepGraphInc::CoEdgeDef& aCE = aStorage.CoEdge(aCoEdgeId); - if (aCE.IsRemoved) - { - continue; - } - const NCollection_DynamicArray* aWires = - aStorage.ReverseIndex().WiresOfCoEdge(aCoEdgeId); - EXPECT_NE(aWires, nullptr) << "CoEdge " << aCoEdgeId.Index << " not in any wire"; - if (aWires != nullptr) - { - EXPECT_EQ(aWires->Length(), 1) - << "CoEdge " << aCoEdgeId.Index << " should be in exactly one wire"; - } + EXPECT_TRUE(aGraph.Topo().CoEdges().Definition(aCoEdgeId).ParentWireId.IsValid()) + << "CoEdge " << aCoEdgeId.Index << " not in any wire"; } - EXPECT_TRUE(aStorage.ValidateReverseIndex()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); } -TEST(BRepGraphIncTest, ReverseIndex_CompSolid_ReverseMaintained_AfterBuild) +TEST(BRepGraphIncTest, Relations_CompSolid_IncomingMaintained_AfterBuild) { // Build a TopoDS_CompSolid containing two boxes and verify: - // 1. myCompSolidsOfSolid is populated for both solids - // 2. ValidateReverseIndex() passes + // 1. ParentSolidRefIds is populated for both solids + // 2. ValidateRelations() passes BRep_Builder aBB; TopoDS_CompSolid aCompSolid; aBB.MakeCompSolid(aCompSolid); @@ -1375,35 +1532,31 @@ TEST(BRepGraphIncTest, ReverseIndex_CompSolid_ReverseMaintained_AfterBuild) aBB.Add(aCompSolid, aBoxMaker1.Shape()); aBB.Add(aCompSolid, aBoxMaker2.Shape()); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompSolid, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompSolid, false); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_EQ(aStorage.NbCompSolids(), 1); - ASSERT_GE(aStorage.NbSolids(), 2); + EXPECT_EQ(aGraph.Topo().CompSolids().Nb(), 1); + ASSERT_GE(aGraph.Topo().Solids().Nb(), 2); - // Both solids must appear in myCompSolidsOfSolid. + // Both solids must appear in the parent solid-ref relation table. for (BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); - aSolidId.IsValid(aStorage.NbSolids()); + aSolidId.IsValid(aGraph.Topo().Solids().Nb()); ++aSolidId) { - const NCollection_DynamicArray* aCSVec = - aStorage.ReverseIndex().CompSolidsOfSolid(aSolidId); - EXPECT_NE(aCSVec, nullptr) << "Solid " << aSolidId.Index << " not in any CompSolid"; - if (aCSVec != nullptr) - { - EXPECT_EQ(aCSVec->Length(), 1); - EXPECT_EQ(aCSVec->Value(0).Index, 0); - } + const NCollection_LinearVector& aSolidRefs = + aGraph.Topo().Solids().Relations(aSolidId).ParentSolidRefIds; + ASSERT_EQ(aSolidRefs.Size(), 1u) << "Solid " << aSolidId.Index << " not in any CompSolid"; } - EXPECT_TRUE(aStorage.ValidateReverseIndex()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); } -TEST(BRepGraphIncTest, ReverseIndex_CompSolid_ReverseMaintained_AfterBuildDelta) +TEST(BRepGraphIncTest, Relations_CompSolid_IncomingMaintained_AfterBuildDelta) { - // Verify BuildDelta() correctly indexes a CompSolid->Solid reverse mapping - // when both the compsolid and solids are appended in the delta. + // Verify the relation build correctly indexes incoming solid refs when + // both the compsolid and solids are appended together. BRep_Builder aBB; TopoDS_CompSolid aCompSolid; aBB.MakeCompSolid(aCompSolid); @@ -1412,37 +1565,39 @@ TEST(BRepGraphIncTest, ReverseIndex_CompSolid_ReverseMaintained_AfterBuildDelta) aBB.Add(aCompSolid, aBoxMaker.Shape()); // First, build an empty storage then delta-populate. - // Simplest: just populate from scratch and check ValidateReverseIndex. - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompSolid, false); - ASSERT_TRUE(aStorage.GetIsDone()); + // Simplest: just populate from scratch and check ValidateRelations. + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompSolid, false); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_EQ(aStorage.NbCompSolids(), 1); - ASSERT_GE(aStorage.NbSolids(), 1); + EXPECT_EQ(aGraph.Topo().CompSolids().Nb(), 1); + ASSERT_GE(aGraph.Topo().Solids().Nb(), 1); - EXPECT_TRUE(aStorage.ValidateReverseIndex()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); - const NCollection_DynamicArray* aCSVec = - aStorage.ReverseIndex().CompSolidsOfSolid(BRepGraph_SolidId::Start()); - EXPECT_NE(aCSVec, nullptr) << "Solid 0 should be indexed in CompSolidsOfSolid"; + const NCollection_LinearVector& aSolidRefs = + aGraph.Topo().Solids().Relations(BRepGraph_SolidId::Start()).ParentSolidRefIds; + EXPECT_GE(aSolidRefs.Size(), 1u) << "Solid 0 should be indexed in CompSolid relations"; } -TEST(BRepGraphIncTest, ReverseIndex_Validate_Box_FullConsistency) +TEST(BRepGraphIncTest, Relations_Validate_Box_FullConsistency) { - // Smoke test: a simple solid box must pass full reverse-index validation. + // Smoke test: a simple solid box must pass full relation-table validation. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aBox, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aBox, false); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_TRUE(aStorage.ValidateReverseIndex()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); } -TEST(BRepGraphIncTest, ReverseIndex_Validate_Compound_FullConsistency) +TEST(BRepGraphIncTest, Relations_Validate_Compound_FullConsistency) { - // A compound of two boxes must pass full reverse-index validation, + // A compound of two boxes must pass full relation-table validation, // covering CompoundsOfSolid, CompoundsOfShell, CompoundsOfFace. BRep_Builder aBB; TopoDS_Compound aCompound; @@ -1453,16 +1608,17 @@ TEST(BRepGraphIncTest, ReverseIndex_Validate_Compound_FullConsistency) aBB.Add(aCompound, aBoxMaker1.Shape()); aBB.Add(aCompound, aBoxMaker2.Shape()); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aCompound, false); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_TRUE(aStorage.ValidateReverseIndex()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); } -TEST(BRepGraphIncTest, ReverseIndex_AfterEditorMutations_StaysConsistent) +TEST(BRepGraphIncTest, Relations_AfterEditorMutations_StaysConsistent) { - // Verify the incremental Bind/Unbind mutation path keeps the reverse index + // Verify the incremental Bind/Unbind mutation path keeps the relation tables // consistent with the forward entity / reference-entry tables across a // sequence of RemoveWire / RemoveFace / RemoveShell mutations. BRep_Builder aBB; @@ -1475,17 +1631,16 @@ TEST(BRepGraphIncTest, ReverseIndex_AfterEditorMutations_StaysConsistent) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); - ASSERT_TRUE(aGraph.ValidateReverseIndex()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + ASSERT_TRUE(aGraph.ValidateRelations()); // Remove an inner wire from the first face that owns more than one wire // (or the only wire if all faces have a single wire). for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - const NCollection_DynamicArray aWireRefs = + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + const NCollection_LinearVector& aWireRefs = BRepGraph_TestTools::WireRefsOfFace(aGraph, aFaceId); if (aWireRefs.IsEmpty()) { @@ -1494,81 +1649,67 @@ TEST(BRepGraphIncTest, ReverseIndex_AfterEditorMutations_StaysConsistent) ASSERT_TRUE(aGraph.Editor().Faces().RemoveWire(aFaceId, aWireRefs.Value(0))); break; } - EXPECT_TRUE(aGraph.ValidateReverseIndex()) << "Reverse index inconsistent after RemoveWire"; + EXPECT_TRUE(aGraph.ValidateRelations()) << "Relation table inconsistent after RemoveWire"; // Remove the first face from the first shell. - const NCollection_DynamicArray aFaceRefs = + const NCollection_LinearVector& aFaceRefs = BRepGraph_TestTools::FaceRefsOfShell(aGraph, BRepGraph_ShellId::Start()); - ASSERT_GE(aFaceRefs.Length(), 1); + ASSERT_GE(aFaceRefs.Size(), 1); ASSERT_TRUE(aGraph.Editor().Shells().RemoveFace(BRepGraph_ShellId::Start(), aFaceRefs.Value(0))); - EXPECT_TRUE(aGraph.ValidateReverseIndex()) << "Reverse index inconsistent after RemoveFace"; + EXPECT_TRUE(aGraph.ValidateRelations()) << "Relation table inconsistent after RemoveFace"; // Remove the first shell from the first solid. - const NCollection_DynamicArray aShellRefs = + const NCollection_LinearVector& aShellRefs = BRepGraph_TestTools::ShellRefsOfSolid(aGraph, BRepGraph_SolidId::Start()); - ASSERT_GE(aShellRefs.Length(), 1); + ASSERT_GE(aShellRefs.Size(), 1); ASSERT_TRUE( aGraph.Editor().Solids().RemoveShell(BRepGraph_SolidId::Start(), aShellRefs.Value(0))); - EXPECT_TRUE(aGraph.ValidateReverseIndex()) << "Reverse index inconsistent after RemoveShell"; + EXPECT_TRUE(aGraph.ValidateRelations()) << "Relation table inconsistent after RemoveShell"; EXPECT_TRUE(aGraph.Editor().ValidateMutationBoundary()); } -TEST(BRepGraphIncTest, ReverseIndex_BulkBuild_TwiceProducesEqualState) +TEST(BRepGraphIncTest, Relations_BulkBuild_TwiceProducesEqualState) { // Bulk Populate must be deterministic: building the same shape twice into - // independent storages must yield byte-equal reverse-index views as observed + // independent storages must yield byte-equal relation-table views as observed // through the public per-entity accessors. BRepPrimAPI_MakeBox aBoxMaker(7.0, 11.0, 13.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraphInc_Storage aStorageA; - BRepGraphInc_Storage aStorageB; - BRepGraphInc_Populate::Perform(aStorageA, aBox, false); - BRepGraphInc_Populate::Perform(aStorageB, aBox, false); - ASSERT_TRUE(aStorageA.GetIsDone()); - ASSERT_TRUE(aStorageB.GetIsDone()); - ASSERT_EQ(aStorageA.NbEdges(), aStorageB.NbEdges()); + BRepGraph aGraphA; + BRepGraph aGraphB; + std::ignore = BRepGraphInc_Populate::Perform(aGraphA, aBox, false); + std::ignore = BRepGraphInc_Populate::Perform(aGraphB, aBox, false); + ASSERT_FALSE(aGraphA.IsEmpty()); + ASSERT_FALSE(aGraphB.IsEmpty()); + ASSERT_EQ(aGraphA.Topo().Edges().Nb(), aGraphB.Topo().Edges().Nb()); - for (uint32_t anIdx = 0; anIdx < aStorageA.NbEdges(); ++anIdx) + for (uint32_t anIdx = 0; anIdx < aGraphA.Topo().Edges().Nb(); ++anIdx) { - const BRepGraph_EdgeId anEdgeId(anIdx); - const NCollection_DynamicArray* aWiresA = - aStorageA.ReverseIndex().WiresOfEdge(anEdgeId); - const NCollection_DynamicArray* aWiresB = - aStorageB.ReverseIndex().WiresOfEdge(anEdgeId); - ASSERT_EQ(aWiresA == nullptr, aWiresB == nullptr); - if (aWiresA == nullptr) + const BRepGraph_EdgeId anEdgeId(anIdx); + BRepGraph_WiresOfEdge aWireItA = aGraphA.Topo().Edges().WiresOf(anEdgeId); + BRepGraph_WiresOfEdge aWireItB = aGraphB.Topo().Edges().WiresOf(anEdgeId); + for (; aWireItA.More() && aWireItB.More(); aWireItA.Next(), aWireItB.Next()) { - continue; - } - ASSERT_EQ(aWiresA->Size(), aWiresB->Size()); - for (size_t i = 0; i < aWiresA->Size(); ++i) - { - EXPECT_EQ(aWiresA->Value(i), aWiresB->Value(i)); + EXPECT_EQ(aWireItA.CurrentId(), aWireItB.CurrentId()); } + EXPECT_EQ(aWireItA.More(), aWireItB.More()); - const NCollection_DynamicArray* aFacesA = - aStorageA.ReverseIndex().FacesOfEdge(anEdgeId); - const NCollection_DynamicArray* aFacesB = - aStorageB.ReverseIndex().FacesOfEdge(anEdgeId); - ASSERT_EQ(aFacesA == nullptr, aFacesB == nullptr); - if (aFacesA == nullptr) + BRepGraph_FacesOfEdge aFaceItA = aGraphA.Topo().Edges().FacesOf(anEdgeId); + BRepGraph_FacesOfEdge aFaceItB = aGraphB.Topo().Edges().FacesOf(anEdgeId); + for (; aFaceItA.More() && aFaceItB.More(); aFaceItA.Next(), aFaceItB.Next()) { - continue; - } - ASSERT_EQ(aFacesA->Size(), aFacesB->Size()); - for (size_t i = 0; i < aFacesA->Size(); ++i) - { - EXPECT_EQ(aFacesA->Value(i), aFacesB->Value(i)); + EXPECT_EQ(aFaceItA.CurrentId(), aFaceItB.CurrentId()); } + EXPECT_EQ(aFaceItA.More(), aFaceItB.More()); } } -TEST(BRepGraphIncTest, ReverseIndex_EdgeOpsAdd_BindsStartEndVertices) +TEST(BRepGraphIncTest, Relations_EdgeOpsAdd_BindsStartEndVertices) { // Free edge created at runtime via the Editor must show up under its endpoint - // vertices in the reverse index. Pre-fix this query returned an empty list. + // vertices in the relation tables. Pre-fix this query returned an empty list. BRepGraph aGraph; aGraph.Clear(); const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); @@ -1596,10 +1737,10 @@ TEST(BRepGraphIncTest, ReverseIndex_EdgeOpsAdd_BindsStartEndVertices) } EXPECT_TRUE(foundV0); EXPECT_TRUE(foundV1); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_RemoveEdge_UnbindsStartEndVertices) +TEST(BRepGraphIncTest, Relations_RemoveEdge_UnbindsStartEndVertices) { // Symmetric to the Add test: removing the edge must drop the entries. BRepGraph aGraph; @@ -1620,13 +1761,13 @@ TEST(BRepGraphIncTest, ReverseIndex_RemoveEdge_UnbindsStartEndVertices) { EXPECT_NE(aE, anEdge); } - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetRefVertexDefId_RebindsVertexToEdges) +TEST(BRepGraphIncTest, Relations_SetRefChildVertexId_RebindsVertexToEdges) { // Rewire an edge's start-vertex ref to a different vertex and verify the - // rev-index moved. + // relation moved. BRepGraph aGraph; aGraph.Clear(); const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); @@ -1637,7 +1778,7 @@ TEST(BRepGraphIncTest, ReverseIndex_SetRefVertexDefId_RebindsVertexToEdges) ASSERT_TRUE(anEdge.IsValid()); const BRepGraph_VertexRefId aStartRef = aGraph.Topo().Edges().Definition(anEdge).StartVertexRefId; - aGraph.Editor().Vertices().SetRefVertexDefId(aStartRef, aV2); + aGraph.Editor().Vertices().SetRefChildVertexId(aStartRef, aV2); bool stillUnderV0 = false, foundUnderV2 = false; for (const BRepGraph_EdgeId& aE : aGraph.Topo().Vertices().Edges(aV0)) @@ -1656,37 +1797,37 @@ TEST(BRepGraphIncTest, ReverseIndex_SetRefVertexDefId_RebindsVertexToEdges) } EXPECT_FALSE(stillUnderV0); EXPECT_TRUE(foundUnderV2); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_BoxThroughCompact_StaysConsistent) +TEST(BRepGraphIncTest, Relations_BoxThroughCompact_StaysConsistent) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_TRUE(aGraph.Editor().Faces().RemoveWire( BRepGraph_FaceId::Start(), BRepGraph_TestTools::WireRefsOfFace(aGraph, BRepGraph_FaceId::Start()).Value(0))); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); [[maybe_unused]] const BRepGraph_Compact::Result aCompactRes = BRepGraph_Compact::Perform(aGraph); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetRefWireDefId_RebindsWireToFaces) +TEST(BRepGraphIncTest, Relations_SetRefChildWireId_RebindsWireToFaces) { // Add two faces; rewire face0's outer-wire ref to face1's outer wire and // verify WireToFaces moved entries. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_FaceId aFace0 = BRepGraph_FaceId::Start(); const BRepGraph_FaceId aFace1(1); @@ -1694,25 +1835,27 @@ TEST(BRepGraphIncTest, ReverseIndex_SetRefWireDefId_RebindsWireToFaces) const BRepGraph_WireRefId aWireRef0 = BRepGraph_TestTools::WireRefsOfFace(aGraph, aFace0).Value(0); - const BRepGraph_WireId aOldWire = aGraph.Refs().Wires().Entry(aWireRef0).WireDefId; + const BRepGraph_WireId aOldWire = aGraph.Refs().Wires().Entry(aWireRef0).ChildWireId; const BRepGraph_WireId aNewWire = aGraph.Refs() .Wires() .Entry(BRepGraph_TestTools::WireRefsOfFace(aGraph, aFace1).Value(0)) - .WireDefId; + .ChildWireId; ASSERT_NE(aOldWire, aNewWire); - aGraph.Editor().Wires().SetRefWireDefId(aWireRef0, aNewWire); + aGraph.Editor().Wires().SetRefChildWireId(aWireRef0, aNewWire); bool oldStillBound = false, newBound = false; - for (const BRepGraph_FaceId& f : aGraph.Topo().Wires().Faces(aOldWire)) + for (const BRepGraph_FaceId& f : + BRepGraph_FacesOfWire(aGraph, aGraph.Topo().Wires().Relations(aOldWire).ParentWireRefIds)) { if (f == aFace0) { oldStillBound = true; } } - for (const BRepGraph_FaceId& f : aGraph.Topo().Wires().Faces(aNewWire)) + for (const BRepGraph_FaceId& f : + BRepGraph_FacesOfWire(aGraph, aGraph.Topo().Wires().Relations(aNewWire).ParentWireRefIds)) { if (f == aFace0) { @@ -1721,65 +1864,74 @@ TEST(BRepGraphIncTest, ReverseIndex_SetRefWireDefId_RebindsWireToFaces) } EXPECT_FALSE(oldStillBound); EXPECT_TRUE(newBound); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + for (const BRepGraph_CoEdgeId& aCoEdgeId : aGraph.Topo().Wires().Relations(aOldWire).CoEdgeIds) + { + const BRepGraphInc::CoEdgeDef& aCoEdge = aGraph.Topo().CoEdges().Definition(aCoEdgeId); + EXPECT_NE(aCoEdge.FaceId, aFace0); + for (const BRepGraph_FaceId& aFaceId : aGraph.Topo().Edges().FacesOf(aCoEdge.ChildEdgeId)) + { + EXPECT_NE(aFaceId, aFace0); + } + } + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetRefFaceDefId_RebindsFaceToShells) +TEST(BRepGraphIncTest, Relations_SetRefFaceId_RebindsFaceToShells) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_ShellId aShell = BRepGraph_ShellId::Start(); const BRepGraph_FaceRefId aRef0 = BRepGraph_TestTools::FaceRefsOfShell(aGraph, aShell).Value(0); - const BRepGraph_FaceId aOldFace = aGraph.Refs().Faces().Entry(aRef0).FaceDefId; + const BRepGraph_FaceId aOldFace = aGraph.Refs().Faces().Entry(aRef0).ChildFaceId; const BRepGraph_FaceId aNewFace(1); ASSERT_NE(aOldFace, aNewFace); - aGraph.Editor().Faces().SetRefFaceDefId(aRef0, aNewFace); + aGraph.Editor().Faces().SetRefFaceId(aRef0, aNewFace); // Old face still has the OTHER shell-ref pointing at it; just check the - // Old/New rev-index makes sense relative to this single ref. - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + // Old/New relation makes sense relative to this single ref. + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetRefShellDefId_RebindsShellToSolids) +TEST(BRepGraphIncTest, Relations_SetRefChildShellId_RebindsShellToSolids) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Shells().Nb(), 1); // Box has 1 solid, 1 shell. Rewiring the lone shell-ref to itself is a no-op, // so just verify Validate after a no-op call (proves equality short-circuit). const BRepGraph_ShellRefId aRef0 = BRepGraph_TestTools::ShellRefsOfSolid(aGraph, BRepGraph_SolidId::Start()).Value(0); - const BRepGraph_ShellId aShell = aGraph.Refs().Shells().Entry(aRef0).ShellDefId; - aGraph.Editor().Shells().SetRefShellDefId(aRef0, aShell); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + const BRepGraph_ShellId aShell = aGraph.Refs().Shells().Entry(aRef0).ChildShellId; + aGraph.Editor().Shells().SetRefChildShellId(aRef0, aShell); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_RebindsEdgeToCoEdges) +TEST(BRepGraphIncTest, Relations_SetChildEdgeIdOnCoEdge_RebindsEdgeToCoEdges) { - // Pick a coedge that lives in a wire; redirect its EdgeDefId to a different + // Pick a coedge that lives in a wire; redirect its child edge to a different // existing edge and confirm Edge->CoEdges, Edge->Wires, Edge->Faces all move. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().CoEdges().Nb(), 2); const BRepGraph_CoEdgeId aCoEdge = BRepGraph_CoEdgeId::Start(); const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aCoEdge); - const BRepGraph_EdgeId anOldEdge = aDef.EdgeDefId; + const BRepGraph_EdgeId anOldEdge = aDef.ChildEdgeId; // Pick any other valid edge as the target. BRepGraph_EdgeId aNewEdge; for (BRepGraph_EdgeId aE(0); aE.IsValid(aGraph.Topo().Edges().Nb()); ++aE) @@ -1792,7 +1944,7 @@ TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_RebindsEdgeToCoEdges) } ASSERT_TRUE(aNewEdge.IsValid()); - aGraph.Editor().CoEdges().SetEdgeDefId(aCoEdge, aNewEdge); + aGraph.Editor().CoEdges().SetChildEdgeId(aCoEdge, aNewEdge); bool oldHasCoEdge = false, newHasCoEdge = false; for (const BRepGraph_CoEdgeId& aC : aGraph.Topo().Edges().CoEdges(anOldEdge)) @@ -1811,19 +1963,19 @@ TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_RebindsEdgeToCoEdges) } EXPECT_FALSE(oldHasCoEdge); EXPECT_TRUE(newHasCoEdge); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetFaceDefIdOnCoEdge_LastBondCheck) +TEST(BRepGraphIncTest, Relations_SetFaceIdOnCoEdge_LastBondCheck) { // Cylinder seam edges have TWO coedges on the SAME face. Dropping one - // coedge's FaceDefId must keep the (edge, face) pair bound via the other. + // coedge's FaceId must keep the (edge, face) pair bound via the other. BRepPrimAPI_MakeCylinder aCylMaker(5.0, 10.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aCylMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aCylMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Locate a seam edge: an edge with two coedges on the same face. BRepGraph_EdgeId aSeamEdge; @@ -1831,7 +1983,7 @@ TEST(BRepGraphIncTest, ReverseIndex_SetFaceDefIdOnCoEdge_LastBondCheck) BRepGraph_CoEdgeId aSeamCoEdge; for (BRepGraph_EdgeId aE(0); aE.IsValid(aGraph.Topo().Edges().Nb()) && !aSeamEdge.IsValid(); ++aE) { - const NCollection_DynamicArray& aCEs = aGraph.Topo().Edges().CoEdges(aE); + const NCollection_LinearVector& aCEs = aGraph.Topo().Edges().CoEdges(aE); if (aCEs.Size() < 2) { continue; @@ -1840,27 +1992,27 @@ TEST(BRepGraphIncTest, ReverseIndex_SetFaceDefIdOnCoEdge_LastBondCheck) for (const BRepGraph_CoEdgeId& aCE : aCEs) { const BRepGraphInc::CoEdgeDef& aD = aGraph.Topo().CoEdges().Definition(aCE); - if (!aD.FaceDefId.IsValid()) + if (!aD.FaceId.IsValid()) { continue; } - if (aSeenFace.IsBound(aD.FaceDefId.Index)) + if (aSeenFace.IsBound(aD.FaceId.Index)) { aSeamEdge = aE; - aSeamFace = aD.FaceDefId; + aSeamFace = aD.FaceId; aSeamCoEdge = aCE; break; } - aSeenFace.Bind(aD.FaceDefId.Index, aCE); + aSeenFace.Bind(aD.FaceId.Index, aCE); } } ASSERT_TRUE(aSeamEdge.IsValid()) << "cylinder must have a seam edge"; - aGraph.Editor().CoEdges().SetFaceDefId(aSeamCoEdge, BRepGraph_FaceId()); + aGraph.Editor().CoEdges().SetFaceId(aSeamCoEdge, BRepGraph_FaceId()); // The seam-pair partner of aSeamCoEdge still binds (aSeamEdge, aSeamFace). bool stillBound = false; - for (const BRepGraph_FaceId& f : aGraph.Topo().Edges().Faces(aSeamEdge)) + for (const BRepGraph_FaceId& f : aGraph.Topo().Edges().FacesOf(aSeamEdge)) { if (f == aSeamFace) { @@ -1868,35 +2020,131 @@ TEST(BRepGraphIncTest, ReverseIndex_SetFaceDefIdOnCoEdge_LastBondCheck) } } EXPECT_TRUE(stillBound); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetFaceDefIdOnCoEdge_OnlyBondUnbinds) +TEST(BRepGraphIncTest, Relations_SetFaceIdOnCoEdge_OnlyBondUnbinds) { - // Box edges have one coedge per face. Setting that single coedge's FaceDefId + // Box edges have one coedge per face. Setting that single coedge's FaceId // to invalid MUST unbind (edge,face) from EdgeToFaces. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_CoEdgeId aCoEdge = BRepGraph_CoEdgeId::Start(); const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aCoEdge); - const BRepGraph_EdgeId anEdge = aDef.EdgeDefId; - const BRepGraph_FaceId anOldFace = aDef.FaceDefId; + const BRepGraph_EdgeId anEdge = aDef.ChildEdgeId; + const BRepGraph_FaceId anOldFace = aDef.FaceId; - aGraph.Editor().CoEdges().SetFaceDefId(aCoEdge, BRepGraph_FaceId()); + aGraph.Editor().CoEdges().SetFaceId(aCoEdge, BRepGraph_FaceId()); - for (const BRepGraph_FaceId& f : aGraph.Topo().Edges().Faces(anEdge)) + for (const BRepGraph_FaceId& f : aGraph.Topo().Edges().FacesOf(anEdge)) { EXPECT_NE(f, anOldFace); } - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetEndVertexRefId_RebindsVertexToEdges) +TEST(BRepGraphIncTest, Relations_RemoveWireFromSharedWireClearsDetachedFaceContext) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + + const BRepGraph_CoEdgeId aCoEdge = aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD); + ASSERT_TRUE(aCoEdge.IsValid()); + + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aCoEdge); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + ASSERT_TRUE(aWire.IsValid()); + + NCollection_LinearVector anInnerWires; + const occ::handle aPlane = new Geom_Plane(gp_Pln()); + const BRepGraph_FaceId aFace0 = + aGraph.Editor().Faces().Add(aPlane, aWire, anInnerWires.ToArray1(), 1.e-7); + const BRepGraph_FaceId aFace1 = + aGraph.Editor().Faces().Add(aPlane, aWire, anInnerWires.ToArray1(), 1.e-7); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + aGraph.Editor().CoEdges().SetFaceId(aCoEdge, aFace0); + aGraph.Editor().CoEdges().SetPCurve(aCoEdge, + new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), + 0.0, + 1.0); + const BRepGraph_CoEdgeCurve2DRepId aPCurveRepId = + aGraph.Topo().CoEdges().Definition(aCoEdge).Curve2DRepId; + ASSERT_TRUE(aPCurveRepId.IsValid()); + const uint32_t aNbActivePCurves = aGraph.Topo().Geometry().NbActiveCoEdgeCurves2D(); + ASSERT_TRUE(aGraph.ValidateRelations()); + + const BRepGraph_WireRefId aFace0WireRef = + aGraph.Topo().Faces().Relations(aFace0).WireRefIds.First(); + ASSERT_TRUE(aFace0WireRef.IsValid()); + EXPECT_TRUE(aGraph.Editor().Faces().RemoveWire(aFace0, aFace0WireRef)); + + EXPECT_FALSE(aGraph.Topo().CoEdges().Definition(aCoEdge).FaceId.IsValid()) + << "Removing the face's wire usage must clear coedge face context even if another face still " + "uses the same wire"; + EXPECT_FALSE(aGraph.Topo().CoEdges().Definition(aCoEdge).Curve2DRepId.IsValid()); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveCoEdgeCurves2D(), aNbActivePCurves - 1u); + for (const BRepGraph_FaceId& aFaceId : aGraph.Topo().Edges().FacesOf(anEdge)) + { + EXPECT_NE(aFaceId, aFace0); + } + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraphIncTest, Relations_ValidateRejectsLivePCurveWithoutFaceContext) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + + const BRepGraph_CoEdgeId aCoEdge = aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD); + ASSERT_TRUE(aCoEdge.IsValid()); + + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aCoEdge); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + ASSERT_TRUE(aWire.IsValid()); + + NCollection_LinearVector anInnerWires; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(new Geom_Plane(gp_Pln()), aWire, anInnerWires.ToArray1(), 1.e-7); + ASSERT_TRUE(aFace.IsValid()); + + aGraph.Editor().CoEdges().SetFaceId(aCoEdge, aFace); + aGraph.Editor().CoEdges().SetPCurve(aCoEdge, + new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)), + 0.0, + 1.0); + ASSERT_TRUE(aGraph.Topo().CoEdges().Definition(aCoEdge).Curve2DRepId.IsValid()); + ASSERT_TRUE(aGraph.ValidateRelations()); + + { + BRepGraph_MutGuard aMutCoEdge = aGraph.Editor().CoEdges().Mut(aCoEdge); + aMutCoEdge.Internal().FaceId = BRepGraph_FaceId(); + } + EXPECT_FALSE(aGraph.ValidateRelations()) + << "A live face-scoped coedge pcurve without a face context is an orphaned relation"; +} + +TEST(BRepGraphIncTest, Relations_SetEndVertexRefId_RebindsVertexToEdges) { BRepGraph aGraph; aGraph.Clear(); @@ -1910,9 +2158,10 @@ TEST(BRepGraphIncTest, ReverseIndex_SetEndVertexRefId_RebindsVertexToEdges) ASSERT_TRUE(anEdge.IsValid()); ASSERT_TRUE(anExtra.IsValid()); - // Repoint anEdge's end-ref at the same VertexRefId already used by anExtra's - // end. Both edges now share the same end vertex (aV2). + // Reusing an already owned VertexRefId is invalid: refs are single-use edge + // slots, even when both edges should point to the same vertex definition. const BRepGraph_VertexRefId aV2Ref = aGraph.Topo().Edges().Definition(anExtra).EndVertexRefId; + const BRepGraph_VertexRefId anOldEndRef = aGraph.Topo().Edges().Definition(anEdge).EndVertexRefId; aGraph.Editor().Edges().SetEndVertexRefId(anEdge, aV2Ref); bool stillUnderV1 = false, foundUnderV2 = false; @@ -1930,38 +2179,471 @@ TEST(BRepGraphIncTest, ReverseIndex_SetEndVertexRefId_RebindsVertexToEdges) foundUnderV2 = true; } } - EXPECT_FALSE(stillUnderV1); - EXPECT_TRUE(foundUnderV2); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(stillUnderV1); + EXPECT_FALSE(foundUnderV2); + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdge).EndVertexRefId, anOldEndRef); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetterIdempotency_NoOp) +TEST(BRepGraphIncTest, Relations_SetterIdempotency_NoOp) { - // Identity assignments must be no-ops and not corrupt the reverse index. + // Identity assignments must be no-ops and not corrupt the relation tables. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_CoEdgeId aCoEdge = BRepGraph_CoEdgeId::Start(); const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aCoEdge); - aGraph.Editor().CoEdges().SetEdgeDefId(aCoEdge, aDef.EdgeDefId); - aGraph.Editor().CoEdges().SetFaceDefId(aCoEdge, aDef.FaceDefId); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + aGraph.Editor().CoEdges().SetChildEdgeId(aCoEdge, aDef.ChildEdgeId); + aGraph.Editor().CoEdges().SetFaceId(aCoEdge, aDef.FaceId); + EXPECT_TRUE(aGraph.ValidateRelations()); const BRepGraph_VertexRefId aVRef = aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).StartVertexRefId; if (aVRef.IsValid()) { - const BRepGraph_VertexId aV = aGraph.Refs().Vertices().Entry(aVRef).VertexDefId; - aGraph.Editor().Vertices().SetRefVertexDefId(aVRef, aV); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + const BRepGraph_VertexId aV = aGraph.Refs().Vertices().Entry(aVRef).ChildVertexId; + aGraph.Editor().Vertices().SetRefChildVertexId(aVRef, aV); + EXPECT_TRUE(aGraph.ValidateRelations()); } } -TEST(BRepGraphIncTest, ReverseIndex_SetChildRefChildDefId_CrossKindRebinds) +TEST(BRepGraphIncTest, Relations_RebindVertexSkipsRemovedParentEdge) +{ + BRepGraphInc_Storage aStorage; + + const BRepGraph_VertexId aOldVertex = aStorage.AppendVertex(); + const BRepGraph_VertexId aEndVertex = aStorage.AppendVertex(); + const BRepGraph_VertexId aNewVertex = aStorage.AppendVertex(); + const BRepGraph_EdgeId anEdge = aStorage.AppendEdge(); + const BRepGraph_VertexRefId aStartRef = aStorage.AppendVertexRef(); + const BRepGraph_VertexRefId anEndRef = aStorage.AppendVertexRef(); + + aStorage.ChangeVertexRef(aStartRef).ParentEdgeId = anEdge; + aStorage.ChangeVertexRef(aStartRef).ChildVertexId = aOldVertex; + aStorage.ChangeVertexRef(anEndRef).ParentEdgeId = anEdge; + aStorage.ChangeVertexRef(anEndRef).ChildVertexId = aEndVertex; + aStorage.ChangeEdge(anEdge).StartVertexRefId = aStartRef; + aStorage.ChangeEdge(anEdge).EndVertexRefId = anEndRef; + aStorage.RebuildDerivedRelations(); + + ASSERT_EQ(aStorage.VertexRelations(aOldVertex).EdgeIds.Size(), 1u); + ASSERT_EQ(aStorage.VertexRelations(aNewVertex).EdgeIds.Size(), 0u); + + aStorage.SetRemoved(anEdge, true); + aStorage.RebindVertexEdge(aOldVertex, aNewVertex, anEdge, BRepGraph_VertexRefId()); + aStorage.RebindVertexRef(aStartRef, aOldVertex, aNewVertex); + + EXPECT_EQ(aStorage.VertexRef(aStartRef).ChildVertexId, aOldVertex); + EXPECT_EQ(aStorage.VertexRelations(aOldVertex).EdgeIds.Size(), 1u); + EXPECT_EQ(aStorage.VertexRelations(aNewVertex).EdgeIds.Size(), 0u); +} + +TEST(BRepGraphIncTest, CanonicalizeWireCoEdgeOrderStatus_ReordersExactConnectedWire) +{ + BRepGraphInc_Storage aStorage; + const BRepGraph_WireId aWireId = aStorage.AppendWire(); + + const BRepGraph_VertexId aVertexA = addStorageVertex(aStorage, gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aVertexB = addStorageVertex(aStorage, gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aVertexC = addStorageVertex(aStorage, gp_Pnt(2.0, 0.0, 0.0), 1.0e-7); + + const BRepGraph_EdgeId anEdgeBC = addStorageEdge(aStorage, aVertexB, aVertexC); + const BRepGraph_EdgeId anEdgeAB = addStorageEdge(aStorage, aVertexA, aVertexB); + const BRepGraph_CoEdgeId aCoEdgeBC = + aStorage.CreateCoEdgeUse(aWireId, anEdgeBC, BRepGraph_FaceId(), TopAbs_FORWARD); + const BRepGraph_CoEdgeId aCoEdgeAB = + aStorage.CreateCoEdgeUse(aWireId, anEdgeAB, BRepGraph_FaceId(), TopAbs_FORWARD); + + EXPECT_FALSE(aStorage.ValidateWireCoEdgeOrder(aWireId)); + EXPECT_EQ(aStorage.CanonicalizeWireCoEdgeOrderStatus(aWireId), + BRepGraphInc_Storage::WireCoEdgeOrderStatus::Reordered); + + const NCollection_LinearVector& aCoEdges = + aStorage.WireRelations(aWireId).CoEdgeIds; + ASSERT_EQ(aCoEdges.Size(), 2u); + EXPECT_EQ(aCoEdges.Value(0), aCoEdgeAB); + EXPECT_EQ(aCoEdges.Value(1), aCoEdgeBC); + EXPECT_TRUE(aStorage.ValidateWireCoEdgeOrder(aWireId)); +} + +TEST(BRepGraphIncTest, CanonicalizeWireCoEdgeOrderStatus_UsesVertexTolerance) +{ + BRepGraphInc_Storage aStorage; + const BRepGraph_WireId aWireId = aStorage.AppendWire(); + + const BRepGraph_VertexId aVertexA = addStorageVertex(aStorage, gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aVertexB = addStorageVertex(aStorage, gp_Pnt(1.0, 0.0, 0.0), 1.0e-2); + const BRepGraph_VertexId aVertexC = addStorageVertex(aStorage, gp_Pnt(1.005, 0.0, 0.0), 1.0e-2); + const BRepGraph_VertexId aVertexD = addStorageVertex(aStorage, gp_Pnt(2.0, 0.0, 0.0), 1.0e-7); + + const BRepGraph_EdgeId anEdgeAB = addStorageEdge(aStorage, aVertexA, aVertexB); + const BRepGraph_EdgeId anEdgeCD = addStorageEdge(aStorage, aVertexC, aVertexD); + const BRepGraph_CoEdgeId aCoEdgeAB = + aStorage.CreateCoEdgeUse(aWireId, anEdgeAB, BRepGraph_FaceId(), TopAbs_FORWARD); + const BRepGraph_CoEdgeId aCoEdgeCD = + aStorage.CreateCoEdgeUse(aWireId, anEdgeCD, BRepGraph_FaceId(), TopAbs_FORWARD); + + EXPECT_FALSE(aStorage.ValidateWireCoEdgeOrder(aWireId)); + EXPECT_EQ(aStorage.CanonicalizeWireCoEdgeOrderStatus(aWireId), + BRepGraphInc_Storage::WireCoEdgeOrderStatus::ToleranceOrdered); + + const NCollection_LinearVector& aCoEdges = + aStorage.WireRelations(aWireId).CoEdgeIds; + ASSERT_EQ(aCoEdges.Size(), 2u); + EXPECT_EQ(aCoEdges.Value(0), aCoEdgeAB); + EXPECT_EQ(aCoEdges.Value(1), aCoEdgeCD); +} + +TEST(BRepGraphIncTest, CanonicalizeWireCoEdgeOrderStatus_PartialPreservesDisconnectedRuns) +{ + BRepGraphInc_Storage aStorage; + const BRepGraph_WireId aWireId = aStorage.AppendWire(); + + const BRepGraph_VertexId aVertexA = addStorageVertex(aStorage, gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aVertexB = addStorageVertex(aStorage, gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aVertexC = addStorageVertex(aStorage, gp_Pnt(2.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aVertexX = addStorageVertex(aStorage, gp_Pnt(10.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aVertexY = addStorageVertex(aStorage, gp_Pnt(11.0, 0.0, 0.0), 1.0e-7); + + const BRepGraph_EdgeId anEdgeBC = addStorageEdge(aStorage, aVertexB, aVertexC); + const BRepGraph_EdgeId anEdgeXY = addStorageEdge(aStorage, aVertexX, aVertexY); + const BRepGraph_EdgeId anEdgeAB = addStorageEdge(aStorage, aVertexA, aVertexB); + const BRepGraph_CoEdgeId aCoEdgeBC = + aStorage.CreateCoEdgeUse(aWireId, anEdgeBC, BRepGraph_FaceId(), TopAbs_FORWARD); + const BRepGraph_CoEdgeId aCoEdgeXY = + aStorage.CreateCoEdgeUse(aWireId, anEdgeXY, BRepGraph_FaceId(), TopAbs_FORWARD); + const BRepGraph_CoEdgeId aCoEdgeAB = + aStorage.CreateCoEdgeUse(aWireId, anEdgeAB, BRepGraph_FaceId(), TopAbs_FORWARD); + + EXPECT_EQ(aStorage.CanonicalizeWireCoEdgeOrderStatus(aWireId), + BRepGraphInc_Storage::WireCoEdgeOrderStatus::Partial); + + const NCollection_LinearVector& aCoEdges = + aStorage.WireRelations(aWireId).CoEdgeIds; + ASSERT_EQ(aCoEdges.Size(), 3u); + EXPECT_EQ(aCoEdges.Value(0), aCoEdgeAB); + EXPECT_EQ(aCoEdges.Value(1), aCoEdgeBC); + EXPECT_EQ(aCoEdges.Value(2), aCoEdgeXY); +} + +// ============================================================ +// CleanupRemovedReferences tests +// ============================================================ + +TEST(BRepGraphIncTest, CleanupRemovedRefs_AfterRemoveFace_ValidatePasses) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFaceId)); + + aGraph.Editor().Gen().CleanupRemovedReferences(); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraphIncTest, CleanupRemovedRefs_AfterRemoveFace_RemovesCoEdgePCurves) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + NCollection_LinearVector aFaceCoEdges; + NCollection_LinearVector aPCurves; + for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb()); + ++aCoEdgeId) + { + const BRepGraphInc::CoEdgeDef& aCoEdge = aGraph.Topo().CoEdges().Definition(aCoEdgeId); + if (aCoEdge.FaceId == aFaceId && aCoEdge.Curve2DRepId.IsValid()) + { + aFaceCoEdges.Append(aCoEdgeId); + aPCurves.Append(aCoEdge.Curve2DRepId); + } + } + ASSERT_FALSE(aPCurves.IsEmpty()); + const uint32_t aNbActivePCurves = aGraph.Topo().Geometry().NbActiveCoEdgeCurves2D(); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFaceId)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + for (const BRepGraph_CoEdgeId& aCoEdgeId : aFaceCoEdges) + { + EXPECT_FALSE(aGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId.IsValid()); + } + for (const BRepGraph_CoEdgeCurve2DRepId& aPCurveId : aPCurves) + { + EXPECT_TRUE(BRepGraph_RepId(aPCurveId).IsRemoved(aGraph)); + } + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveCoEdgeCurves2D(), + aNbActivePCurves - static_cast(aPCurves.Size())); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraphIncTest, CleanupRemovedRefs_AfterRemoveWire_ValidatePasses) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_WireId aOuterId = BRepGraph_Tool::Face::OuterWire(aGraph, aFaceId); + ASSERT_TRUE(aOuterId.IsValid()); + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aOuterId)); + + aGraph.Editor().Gen().CleanupRemovedReferences(); + const BRepGraph_Validate::Result aValidateResult = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aGraph.ValidateRelations()); + EXPECT_TRUE(aValidateResult.IsValid()) + << (aValidateResult.Issues.IsEmpty() ? "" + : aValidateResult.Issues.First().Description.ToCString()); +} + +TEST(BRepGraphIncTest, CleanupRemovedRefs_AfterRemoveVertex_ValidatePasses) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + const BRepGraph_VertexRefId aStartV = BRepGraph_Tool::Edge::StartVertexId(aGraph, anEdgeId); + ASSERT_TRUE(aStartV.IsValid()); + aGraph.Editor().Gen().RemoveNode( + BRepGraph_NodeId(aGraph.Refs().Vertices().Entry(aStartV).ChildVertexId)); + + aGraph.Editor().Gen().CleanupRemovedReferences(); + EXPECT_TRUE(aGraph.ValidateRelations()); + EXPECT_FALSE(aGraph.Topo().Edges().Definition(anEdgeId).StartVertexRefId.IsValid()); +} + +TEST(BRepGraphIncTest, CleanupRemovedRefs_AfterRemoveMultiple_ValidatePasses) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFaceId)); + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(anEdgeId)); + + aGraph.Editor().Gen().CleanupRemovedReferences(); + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()); +} + +TEST(BRepGraphIncTest, Storage_MarkRemoved_UnbindsTShapeAndOriginal) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + ASSERT_TRUE(aGraph.Shapes().HasOriginal(aFaceId)); + const TopoDS_Shape aOrigShape = aGraph.Shapes().Original(aFaceId); + ASSERT_FALSE(aOrigShape.IsNull()); + ASSERT_TRUE(aGraph.Shapes().HasNode(aOrigShape)); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFaceId)); + + EXPECT_FALSE(aGraph.Shapes().HasNode(aOrigShape)); + EXPECT_FALSE(aGraph.Shapes().HasOriginal(aFaceId)); +} + +// ============================================================ +// Storage fix tests: EraseLast, TShape unbind, RebuildDerivedRelations +// ============================================================ + +TEST(BRepGraphIncTest, Storage_MarkRemoved_EraseLast_EmptyStore) +{ + // S1: When all entities in a rep store are marked removed, + // EraseLast (called internally during Compact) must reclaim slots. + // Verify by removing faces and checking surface counts decrease. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const uint32_t anOldNbSurf = aGraph.Topo().Geometry().NbFaceSurfaces(); + ASSERT_NE(anOldNbSurf, 0); + + // Remove one face to trigger surface removal during compact. + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(BRepGraph_FaceId::Start())); + + [[maybe_unused]] const BRepGraph_Compact::Result aCompactRes = BRepGraph_Compact::Perform(aGraph); + + const uint32_t aNewNbSurf = aGraph.Topo().Geometry().NbFaceSurfaces(); + EXPECT_LT(aNewNbSurf, anOldNbSurf); + EXPECT_TRUE(aGraph.ValidateRelations()); + + const BRepGraph_FaceId aNewFace = BRepGraph_FaceId::Start(); + aGraph.Editor().Faces().SetSurface(aNewFace, new Geom_Plane(gp_Pln())); + // SetSurface replaces the existing surface on the surviving face; count unchanged. + EXPECT_EQ(aGraph.Topo().Geometry().NbFaceSurfaces(), aNewNbSurf); +} + +TEST(BRepGraphIncTest, Storage_MarkRemoved_UnbindsTShapeToNodeId) +{ + // S2: After RemoveNode, the removed node's TShape must no longer + // resolve via Shapes().FindNode(). + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); + + // Reconstruct the solid and verify it has an active mapping. + const TopoDS_Shape aSolid = aGraph.Shapes().Shape(BRepGraph_SolidId::Start()); + ASSERT_FALSE(aSolid.IsNull()); + const BRepGraph_NodeId anOldNode = aGraph.Shapes().FindNode(aSolid); + ASSERT_TRUE(anOldNode.IsValid()); + EXPECT_EQ(anOldNode.NodeKind, BRepGraph_NodeId::Kind::Solid); + + // Remove the solid node. ValidateRelations is not checked here + // because forward refs still point to the removed solid's sub-entities. + aGraph.Editor().Gen().RemoveNode(anOldNode); + + [[maybe_unused]] const BRepGraph_Compact::Result aCompactRes = BRepGraph_Compact::Perform(aGraph); + + const BRepGraph_NodeId aNodeAfter = aGraph.Shapes().FindNode(aSolid); + EXPECT_FALSE(aNodeAfter.IsValid()); + + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraphIncTest, Storage_RebuildDerivedRelations_RecountsRefStoreNbActive) +{ + // RebuildDerivedRelations must not corrupt NbActive counters on ref stores + // after reference entries were marked removed. + BRepGraphInc_Storage aStorage; + + // Create synthetic entities. + const BRepGraph_VertexId aVtx1 = aStorage.AppendVertex(); + const BRepGraph_VertexId aVtx2 = aStorage.AppendVertex(); + const BRepGraph_EdgeId anEdge = aStorage.AppendEdge(); + const BRepGraph_VertexRefId aStartRef = aStorage.AppendVertexRef(); + const BRepGraph_VertexRefId anEndRef = aStorage.AppendVertexRef(); + + aStorage.ChangeVertexRef(aStartRef).ParentEdgeId = anEdge; + aStorage.ChangeVertexRef(aStartRef).ChildVertexId = aVtx1; + aStorage.ChangeVertexRef(anEndRef).ParentEdgeId = anEdge; + aStorage.ChangeVertexRef(anEndRef).ChildVertexId = aVtx2; + aStorage.ChangeEdge(anEdge).StartVertexRefId = aStartRef; + aStorage.ChangeEdge(anEdge).EndVertexRefId = anEndRef; + aStorage.RebuildDerivedRelations(); + + const uint32_t anOldActiveVtxRefs = aStorage.NbActiveVertexRefs(); + ASSERT_GT(anOldActiveVtxRefs, 0u); + + // Mark one vertex ref removed. + EXPECT_TRUE(aStorage.MarkRemovedRef(BRepGraph_RefId(aStartRef))); + EXPECT_EQ(aStorage.NbActiveVertexRefs(), anOldActiveVtxRefs - 1u); + + // Rebuilding the relation tables must preserve the NbActive count. + aStorage.RebuildDerivedRelations(); + EXPECT_EQ(aStorage.NbActiveVertexRefs(), anOldActiveVtxRefs - 1u); +} + +// ============================================================ +// Relation table fix tests: BuildDelta, seam-edge dedup +// ============================================================ + +TEST(BRepGraphIncTest, Relations_BuildDelta_ExistingEdgeNewCoedge_GetsNewFace) +{ + // RI1: Build two identical boxes and dedup-merge. Edges merged should + // have correct face counts in the relation tables. + BRepPrimAPI_MakeBox aBoxMaker1(10.0, 20.0, 30.0); + BRepPrimAPI_MakeBox aBoxMaker2(10.0, 20.0, 30.0); + + BRep_Builder aBB; + TopoDS_Compound aCompound; + aBB.MakeCompound(aCompound); + aBB.Add(aCompound, aBoxMaker1.Shape()); + aBB.Add(aCompound, aBoxMaker2.Shape()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + + ASSERT_EQ(aGraph.Topo().Solids().Nb(), 2); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + [[maybe_unused]] const BRepGraph_Deduplicate::Result aDedupRes = + BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + // Compact to clean up removed entities before relation tables validation. + [[maybe_unused]] const BRepGraph_Compact::Result aCompactRes = BRepGraph_Compact::Perform(aGraph); + + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); + for (BRepGraph_EdgeId anEId(0); anEId.IsValid(aNbEdges); ++anEId) + { + if (anEId.IsRemoved(aGraph)) + { + continue; + } + EXPECT_NE(aGraph.Topo().Edges().NbFaces(anEId), 0u); + } + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraphIncTest, Relations_SeamEdge_NoDuplicateEdgeToWireEntries) +{ + // RI4 (appendDirect -> appendUnique): a seam edge whose two coedges + // belong to the same wire must not cause duplicate wire entries in + // myEdgeToWires. + BRepPrimAPI_MakeSphere aSphMaker(8.0); + const TopoDS_Shape& aSph = aSphMaker.Shape(); + + BRepGraph aGraph; + std::ignore = BRepGraphInc_Populate::Perform(aGraph, aSph, false); + ASSERT_FALSE(aGraph.IsEmpty()); + + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); + for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) + { + NCollection_LinearVector aSeenWires; + for (BRepGraph_WiresOfEdge aWireIt = aGraph.Topo().Edges().WiresOf(anEdgeId); aWireIt.More(); + aWireIt.Next()) + { + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); + EXPECT_FALSE(containsId(aSeenWires, aWireId)) + << "Duplicate wire " << aWireId.Index << " in WiresOfEdge(" << anEdgeId.Index << ")"; + aSeenWires.Append(aWireId); + } + } + + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()); +} + +TEST(BRepGraphIncTest, Relations_SetChildRefChildNodeId_CrossKindRebinds) { // Compound holding a Solid; rewire the ChildRef from the Solid to a Shell. // CompoundsOfSolid must lose the entry, CompoundsOfShell must gain it. @@ -1973,33 +2655,37 @@ TEST(BRepGraphIncTest, ReverseIndex_SetChildRefChildDefId_CrossKindRebinds) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Compounds().Nb(), 1); ASSERT_GE(aGraph.Topo().Solids().Nb(), 1); ASSERT_GE(aGraph.Topo().Shells().Nb(), 1); - const BRepGraph_CompoundId aCompound0 = BRepGraph_CompoundId::Start(); - const BRepGraphInc::CompoundDef& aCDef = aGraph.Topo().Compounds().Definition(aCompound0); - ASSERT_GE(aCDef.ChildRefIds.Length(), 1); - const BRepGraph_ChildRefId aChildRef = aCDef.ChildRefIds.First(); - const BRepGraph_NodeId anOldChild = aGraph.Refs().Children().Entry(aChildRef).ChildDefId; + const BRepGraph_CompoundId aCompound0 = BRepGraph_CompoundId::Start(); + const NCollection_LinearVector& aChildRefs = + aGraph.Topo().Compounds().Relations(aCompound0).ChildRefIds; + ASSERT_GE(aChildRefs.Size(), 1u); + const BRepGraph_ChildRefId aChildRef = aChildRefs.Value(0); + const BRepGraph_NodeId anOldChild = aGraph.Refs().Children().Entry(aChildRef).ChildNodeId; ASSERT_EQ(anOldChild.NodeKind, BRepGraph_NodeId::Kind::Solid); const BRepGraph_ShellId aShell = BRepGraph_ShellId::Start(); - aGraph.Editor().Gen().SetChildRefChildDefId(aChildRef, BRepGraph_NodeId(aShell)); + aGraph.Editor().Gen().SetChildRefChildNodeId(aChildRef, BRepGraph_NodeId(aShell)); const BRepGraph_SolidId anOldSolid = BRepGraph_SolidId::FromNodeId(anOldChild); bool oldStill = false, newBound = false; - for (const BRepGraph_CompoundId& c : aGraph.Topo().Solids().Compounds(anOldSolid)) + for (const BRepGraph_CompoundId& c : + BRepGraph_CompoundsOfChild(aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(anOldSolid)))) { if (c == aCompound0) { oldStill = true; } } - for (const BRepGraph_CompoundId& c : aGraph.Topo().Shells().Compounds(aShell)) + for (const BRepGraph_CompoundId& c : + BRepGraph_CompoundsOfChild(aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aShell)))) { if (c == aCompound0) { @@ -2008,20 +2694,20 @@ TEST(BRepGraphIncTest, ReverseIndex_SetChildRefChildDefId_CrossKindRebinds) } EXPECT_FALSE(oldStill); EXPECT_TRUE(newBound); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_LastBondInWireCheck) +TEST(BRepGraphIncTest, Relations_SetChildEdgeIdOnCoEdge_LastBondInWireCheck) { // Cylinder seam edge: two coedges share both face and wire. Redirecting one - // coedge's EdgeDefId must NOT remove (oldEdge, wire) from EdgeToWires while + // coedge's ChildEdgeId must NOT remove (oldEdge, wire) from EdgeToWires while // the other coedge still references oldEdge in the same wire. BRepPrimAPI_MakeCylinder aCylMaker(5.0, 10.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aCylMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aCylMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Find a seam edge: two coedges, same wire, same edge. BRepGraph_EdgeId aSeamEdge; @@ -2029,7 +2715,7 @@ TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_LastBondInWireCheck) BRepGraph_CoEdgeId aSeamCoEdge; for (BRepGraph_EdgeId aE(0); aE.IsValid(aGraph.Topo().Edges().Nb()) && !aSeamEdge.IsValid(); ++aE) { - const NCollection_DynamicArray& aCEs = aGraph.Topo().Edges().CoEdges(aE); + const NCollection_LinearVector& aCEs = aGraph.Topo().Edges().CoEdges(aE); if (aCEs.Size() < 2) { continue; @@ -2037,9 +2723,8 @@ TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_LastBondInWireCheck) NCollection_DataMap aSeenWire; for (const BRepGraph_CoEdgeId& aCE : aCEs) { - const NCollection_DynamicArray& aCEWires = - aGraph.Topo().CoEdges().Wires(aCE); - for (const BRepGraph_WireId& aW : aCEWires) + const BRepGraph_WireId aW = aGraph.Topo().CoEdges().Wire(aCE); + if (aW.IsValid()) { if (aSeenWire.IsBound(aW.Index)) { @@ -2070,11 +2755,11 @@ TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_LastBondInWireCheck) } ASSERT_TRUE(aTargetEdge.IsValid()); - aGraph.Editor().CoEdges().SetEdgeDefId(aSeamCoEdge, aTargetEdge); + aGraph.Editor().CoEdges().SetChildEdgeId(aSeamCoEdge, aTargetEdge); // The OTHER coedge of aSeamWire still references aSeamEdge -> wire still bound. bool stillBound = false; - for (const BRepGraph_WireId& aW : aGraph.Topo().Edges().Wires(aSeamEdge)) + for (const BRepGraph_WireId& aW : aGraph.Topo().Edges().WiresOf(aSeamEdge)) { if (aW == aSeamWire) { @@ -2082,57 +2767,56 @@ TEST(BRepGraphIncTest, ReverseIndex_SetEdgeDefIdOnCoEdge_LastBondInWireCheck) } } EXPECT_TRUE(stillBound); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } -TEST(BRepGraphIncTest, ReverseIndex_OrphanRef_NoRevIndexUpdate) +TEST(BRepGraphIncTest, Relations_FaceSupplementVertex_NoPersistedRefUpdate) { - // SetRefVertexDefId on a face-direct vertex ref must not touch VertexToEdges - // (no map exists for face-direct vertices). Validate consistency afterwards. + // Face-direct supplemental vertices no longer create persisted VertexRef + // entries or relation-table bindings. Validate that the graph stays consistent. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_VertexId aV = aGraph.Editor().Vertices().Add(gp_Pnt(7, 7, 7), 1.e-7); - const BRepGraph_VertexRefId aFaceVtxRef = - aGraph.Editor().Faces().AddVertex(BRepGraph_FaceId::Start(), aV, TopAbs_INTERNAL); - ASSERT_TRUE(aFaceVtxRef.IsValid()); + std::ignore = aGraph.Editor().Vertices().Add(gp_Pnt(7, 7, 7), 1.e-7); - const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(8, 8, 8), 1.e-7); - aGraph.Editor().Vertices().SetRefVertexDefId(aFaceVtxRef, aV2); - - EXPECT_TRUE(aGraph.ValidateReverseIndex()); - EXPECT_EQ(aGraph.Refs().Vertices().Entry(aFaceVtxRef).VertexDefId, aV2); + EXPECT_TRUE(aGraph.ValidateRelations()); + const occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + ASSERT_FALSE(aLayer.IsNull()); + // No vertex attached via Perform -> supplement layer is empty for this face. + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(BRepGraph_FaceId::Start())).Size(), 0); } -TEST(BRepGraphIncTest, ReverseIndex_RemoveRef_UnbindsByKind) +TEST(BRepGraphIncTest, Relations_RemoveRef_UnbindsByKind) { - // GenOps::RemoveRef must unbind the corresponding rev-index entry. Picks one + // GenOps::RemoveRef must unbind the corresponding relation entry. Picks one // FaceRef on the box's first shell; after RemoveRef the FaceToShells entry // for the detached face no longer lists this shell. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const NCollection_DynamicArray aFaceRefs = + const NCollection_LinearVector& aFaceRefs = BRepGraph_TestTools::FaceRefsOfShell(aGraph, BRepGraph_ShellId::Start()); - ASSERT_GE(aFaceRefs.Length(), 1); + ASSERT_GE(aFaceRefs.Size(), 1); const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.Value(0); const BRepGraphInc::FaceRef& aRef = aGraph.Refs().Faces().Entry(aFaceRefId); - const BRepGraph_FaceId aFaceId = aRef.FaceDefId; + const BRepGraph_FaceId aFaceId = aRef.ChildFaceId; ASSERT_TRUE(aGraph.Editor().Gen().RemoveRef(aFaceRefId)); - for (const BRepGraph_ShellId& aShellId : aGraph.Topo().Faces().Shells(aFaceId)) + for (const BRepGraph_ShellId& aShellId : + BRepGraph_ShellsOfFace(aGraph, aGraph.Topo().Faces().Relations(aFaceId).ParentFaceRefIds)) { EXPECT_NE(aShellId, BRepGraph_ShellId::Start()); } - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx index 42890d4feb..b0cc22ff17 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Assembly_Test.cxx @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include @@ -42,9 +40,9 @@ static double translationX(const TopoDS_Shape& theShape) return theShape.Location().Transformation().TranslationPart().X(); } -NCollection_DynamicArray collectRootProducts(const BRepGraph& theGraph) +NCollection_LinearVector collectRootProducts(const BRepGraph& theGraph) { - NCollection_DynamicArray aRoots(4); + NCollection_LinearVector aRoots(4); for (BRepGraph_RootProductIterator aRootIt(theGraph); aRootIt.More(); aRootIt.Next()) { aRoots.Append(aRootIt.Current()); @@ -52,8 +50,8 @@ NCollection_DynamicArray collectRootProducts(const BRepGrap return aRoots; } -bool hasRootProduct(const NCollection_DynamicArray& theRoots, - const BRepGraph_ProductId theProduct) +template +bool hasRootProduct(const RootContainerT& theRoots, const BRepGraph_ProductId theProduct) { for (const BRepGraph_ProductId& aRoot : theRoots) { @@ -74,16 +72,16 @@ TEST(BRepGraph_AssemblyTest, Build_SingleSolid_AutoCreatesRootProduct) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); EXPECT_EQ(aGraph.Topo().Products().Nb(), 1); - // BRepGraph_Builder::Add() creates a shape-root occurrence linking product to its topology + // BRepGraph::ShapesView::Add() creates a shape-root occurrence linking product to its topology // root. EXPECT_EQ(aGraph.Topo().Occurrences().Nb(), 1); - (void)aGraph.Topo().Products().Definition(BRepGraph_ProductId::Start()); + [[maybe_unused]] const BRepGraphInc::ProductDef& aProductDef = + aGraph.Topo().Products().Definition(BRepGraph_ProductId::Start()); EXPECT_TRUE(aGraph.Topo().Products().ShapeRoot(BRepGraph_ProductId::Start()).IsValid()); // The root product should be a part (has topology root). @@ -105,12 +103,10 @@ TEST(BRepGraph_AssemblyTest, Build_Compound_AutoCreatesRootProduct) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aCompound); EXPECT_EQ(aGraph.Topo().Products().Nb(), 1); - // BRepGraph_Builder::Add() creates a shape-root occurrence linking product to its topology + // BRepGraph::ShapesView::Add() creates a shape-root occurrence linking product to its topology // root. EXPECT_EQ(aGraph.Topo().Occurrences().Nb(), 1); @@ -128,14 +124,13 @@ TEST(BRepGraph_AssemblyTest, AddProduct_IsPart) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); // Add a second part product. const BRepGraph_NodeId aShapeRoot = BRepGraph_SolidId::Start(); - const BRepGraph_ProductId aProductId = - aGraph.Editor().Products().LinkProductToTopology(aShapeRoot); + const BRepGraph_ProductId aProductId = aGraph.Editor().Products().Add(aShapeRoot); + aGraph.Editor().Products().AppendDocumentRoot(aProductId); EXPECT_TRUE(aProductId.IsValid()); EXPECT_TRUE(aGraph.Topo().Products().IsPart(aProductId)); @@ -146,16 +141,13 @@ TEST(BRepGraph_AssemblyTest, AddProduct_InvalidShapeRoot_ReturnsInvalid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - EXPECT_FALSE( - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_ProductId::Start()).IsValid()); + EXPECT_FALSE(aGraph.Editor().Products().Add(BRepGraph_ProductId::Start()).IsValid()); aGraph.Editor().Gen().RemoveNode(BRepGraph_SolidId::Start()); - EXPECT_FALSE( - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()).IsValid()); + EXPECT_FALSE(aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()).IsValid()); } // ============================================================================= @@ -166,11 +158,11 @@ TEST(BRepGraph_AssemblyTest, CreateEmptyProduct_EmptyIsNotAssemblyYet) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); EXPECT_TRUE(aAssemblyId.IsValid()); EXPECT_FALSE(aGraph.Topo().Products().IsAssembly(aAssemblyId)); @@ -185,19 +177,19 @@ TEST(BRepGraph_AssemblyTest, LinkProducts_LinksCorrectly) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); // auto-created root - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); TopLoc_Location aLoc(aTrsf); const BRepGraph_OccurrenceId anOccId = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, aLoc); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, aLoc); EXPECT_TRUE(anOccId.IsValid()); @@ -217,12 +209,12 @@ TEST(BRepGraph_AssemblyTest, DAGSharing_MultipleOccurrencesSamePart) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); gp_Trsf aTrsf1; aTrsf1.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); @@ -230,9 +222,9 @@ TEST(BRepGraph_AssemblyTest, DAGSharing_MultipleOccurrencesSamePart) aTrsf2.SetTranslation(gp_Vec(200.0, 0.0, 0.0)); const BRepGraph_OccurrenceId anOcc1 = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)); const BRepGraph_OccurrenceId anOcc2 = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)); EXPECT_NE(anOcc1, anOcc2); EXPECT_EQ(aGraph.Topo().Occurrences().Product(anOcc1), @@ -245,22 +237,23 @@ TEST(BRepGraph_AssemblyTest, LinkProducts_ParentOccurrenceMustMatchParentProduct { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId anAssemblyA = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId anAssemblyB = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId anAssemblyA = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssemblyA); + const BRepGraph_ProductId anAssemblyB = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssemblyB); const BRepGraph_OccurrenceId aParentOccId = - aGraph.Editor().Products().LinkProducts(anAssemblyA, aPartId, TopLoc_Location()); + aGraph.Editor().Products().Append(anAssemblyA, aPartId, TopLoc_Location()); ASSERT_TRUE(aParentOccId.IsValid()); const BRepGraph_OccurrenceId anInvalidOccId = - aGraph.Editor().Products().LinkProducts(anAssemblyB, aPartId, TopLoc_Location(), aParentOccId); + aGraph.Editor().Products().Append(anAssemblyB, aPartId, TopLoc_Location(), aParentOccId); EXPECT_FALSE(anInvalidOccId.IsValid()); - // BRepGraph_Builder::Add() creates 1 shape-root occ, LinkProducts creates 1 more = 2 total. + // BRepGraph::ShapesView::Add() creates 1 shape-root occ, LinkProducts creates 1 more = 2 total. EXPECT_EQ(aGraph.Topo().Occurrences().Nb(), 2); EXPECT_EQ(aGraph.Topo().Products().NbComponents(anAssemblyB), 0); } @@ -273,23 +266,24 @@ TEST(BRepGraph_AssemblyTest, RootProductIds_Query) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); // Auto-created root product is the first root. - NCollection_DynamicArray aRoots = collectRootProducts(aGraph); - EXPECT_EQ(aRoots.Length(), 1); + NCollection_LinearVector aRoots = collectRootProducts(aGraph); + EXPECT_EQ(aRoots.Size(), 1); EXPECT_EQ(aRoots.Value(0), BRepGraph_ProductId::Start()); // Add an assembly and make it instantiate the part product via occurrence. const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); - (void)aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId = + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); // Now only the assembly (which is not referenced by any occurrence) is a root. aRoots = collectRootProducts(aGraph); - EXPECT_EQ(aRoots.Length(), 1); + EXPECT_EQ(aRoots.Size(), 1); EXPECT_EQ(aRoots.Value(0), aAssemblyId); } @@ -301,17 +295,16 @@ TEST(BRepGraph_AssemblyTest, RootProductIds_ShapelessRootAssembly_UsesProductId) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); - ASSERT_TRUE( - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()).IsValid()); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); + ASSERT_TRUE(aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()).IsValid()); - const NCollection_DynamicArray& aRoots = aGraph.RootProductIds(); - ASSERT_EQ(aRoots.Length(), 1); + const NCollection_LinearVector& aRoots = aGraph.RootProductIds(); + ASSERT_EQ(aRoots.Size(), 1); EXPECT_EQ(aRoots.Value(0), aAssemblyId); } @@ -323,21 +316,20 @@ TEST(BRepGraph_AssemblyTest, RootProductIds_ReflectsAssemblyMutation) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const NCollection_DynamicArray aRootsBefore = collectRootProducts(aGraph); - ASSERT_EQ(aRootsBefore.Length(), 1); + const NCollection_LinearVector aRootsBefore = collectRootProducts(aGraph); + ASSERT_EQ(aRootsBefore.Size(), 1); EXPECT_EQ(aRootsBefore.Value(0), BRepGraph_ProductId::Start()); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); - (void)aGraph.Editor().Products().LinkProducts(aAssemblyId, - BRepGraph_ProductId::Start(), - TopLoc_Location()); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId = + aGraph.Editor().Products().Append(aAssemblyId, BRepGraph_ProductId::Start(), TopLoc_Location()); - const NCollection_DynamicArray aRootsAfter = collectRootProducts(aGraph); - ASSERT_EQ(aRootsAfter.Length(), 1); + const NCollection_LinearVector aRootsAfter = collectRootProducts(aGraph); + ASSERT_EQ(aRootsAfter.Size(), 1); EXPECT_EQ(aRootsAfter.Value(0), aAssemblyId); } @@ -349,31 +341,31 @@ TEST(BRepGraph_AssemblyTest, RemoveOccurrence_UpdatesParent) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); const BRepGraph_OccurrenceId anOccId = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); EXPECT_EQ(aGraph.Topo().Products().NbComponents(aAssemblyId), 1); - const NCollection_DynamicArray& aBeforeRefs = + const NCollection_LinearVector& aBeforeRefs = aGraph.Refs().Occurrences().IdsOf(aAssemblyId); - ASSERT_EQ(aBeforeRefs.Length(), 1); + ASSERT_EQ(aBeforeRefs.Size(), 1); const BRepGraph_OccurrenceRefId anOccRefId = aBeforeRefs.Value(0); - EXPECT_FALSE(aGraph.Refs().Occurrences().Entry(anOccRefId).IsRemoved); + EXPECT_FALSE(anOccRefId.IsRemoved(aGraph)); // Remove the occurrence - should update parent's OccurrenceRefs. aGraph.Editor().Gen().RemoveSubgraph(anOccId); EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(anOccId)); EXPECT_EQ(aGraph.Topo().Products().NbComponents(aAssemblyId), 0); - const NCollection_DynamicArray& anAfterRefs = + const NCollection_LinearVector& anAfterRefs = aGraph.Refs().Occurrences().IdsOf(aAssemblyId); - EXPECT_EQ(anAfterRefs.Length(), 0); - EXPECT_TRUE(aGraph.Refs().Occurrences().Entry(anOccRefId).IsRemoved); + EXPECT_EQ(anAfterRefs.Size(), 0); + EXPECT_TRUE(anOccRefId.IsRemoved(aGraph)); } // ============================================================================= @@ -384,16 +376,16 @@ TEST(BRepGraph_AssemblyTest, RemoveProduct_CascadeOccurrences) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); const BRepGraph_OccurrenceId anOcc1 = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); const BRepGraph_OccurrenceId anOcc2 = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); // Remove the assembly product - cascades to its child occurrences. aGraph.Editor().Gen().RemoveSubgraph(aAssemblyId); @@ -411,16 +403,15 @@ TEST(BRepGraph_AssemblyTest, RemoveProduct_RemovesProductAndOccurrences) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - // Part product created by BRepGraph_Builder::Add() references topology via + // Part product created by BRepGraph::ShapesView::Add() references topology via // a shape-root occurrence. const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); EXPECT_TRUE(aGraph.Topo().Products().IsPart(aPartId)); - // BRepGraph_Builder::Add() creates 1 shape-root occurrence. + // BRepGraph::ShapesView::Add() creates 1 shape-root occurrence. ASSERT_EQ(aGraph.Topo().Occurrences().Nb(), 1); const BRepGraph_OccurrenceId aShapeRootOcc = BRepGraph_OccurrenceId::Start(); EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(aShapeRootOcc)); @@ -442,13 +433,14 @@ TEST(BRepGraph_AssemblyTest, RemoveOccurrence_CascadesToNestedChildren) // Removing the mid-level occurrence should also remove the leaf occurrence. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aLeafPart = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aMidAsm = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aTopAsm = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aMidAsm = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aMidAsm); + const BRepGraph_ProductId aTopAsm = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aTopAsm); gp_Trsf aT1, aT2; aT1.SetTranslation(gp_Vec(10.0, 0.0, 0.0)); @@ -456,15 +448,15 @@ TEST(BRepGraph_AssemblyTest, RemoveOccurrence_CascadesToNestedChildren) // TopAsm places MidAsm. const BRepGraph_OccurrenceId anOccMid = - aGraph.Editor().Products().LinkProducts(aTopAsm, aMidAsm, TopLoc_Location(aT1)); + aGraph.Editor().Products().Append(aTopAsm, aMidAsm, TopLoc_Location(aT1)); // MidAsm places LeafPart, with parent occurrence = anOccMid. const BRepGraph_OccurrenceId anOccLeaf = - aGraph.Editor().Products().LinkProducts(aMidAsm, aLeafPart, TopLoc_Location(aT2), anOccMid); + aGraph.Editor().Products().Append(aMidAsm, aLeafPart, TopLoc_Location(aT2), anOccMid); EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(anOccMid)); EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(anOccLeaf)); - // ParentOccurrence is no longer stored on OccurrenceDef (DAG paths resolved via PathView). + // ParentOccurrence is no longer stored on OccurrenceDef; paths are resolved by explorers. // Verify that both occurrences are active. EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(anOccLeaf)); @@ -490,9 +482,8 @@ TEST(BRepGraph_AssemblyTest, MutProduct_RAII) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); { BRepGraph_MutGuard aMutProd = @@ -512,20 +503,20 @@ TEST(BRepGraph_AssemblyTest, MutOccurrenceRef_LocalLocation) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); const BRepGraph_OccurrenceId anOccId = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); ASSERT_TRUE(anOccId.IsValid()); // Find the OccurrenceRefId for the occurrence. - const NCollection_DynamicArray& aOccRefs = + const NCollection_LinearVector& aOccRefs = aGraph.Refs().Occurrences().IdsOf(aAssemblyId); - ASSERT_EQ(aOccRefs.Length(), 1); + ASSERT_EQ(aOccRefs.Size(), 1); const BRepGraph_OccurrenceRefId anOccRefId = aOccRefs.Value(0); gp_Trsf aTrsf; @@ -546,9 +537,12 @@ TEST(BRepGraph_AssemblyTest, MutInvalidAssemblyDefs_ThrowProgramError) { BRepGraph aGraph; #if !defined(No_Exception) - EXPECT_THROW((void)aGraph.Editor().Products().Mut(BRepGraph_ProductId(7)), Standard_ProgramError); - EXPECT_THROW((void)aGraph.Editor().Occurrences().Mut(BRepGraph_OccurrenceId(7)), - Standard_ProgramError); + EXPECT_THROW( + { [[maybe_unused]] auto aMut = aGraph.Editor().Products().Mut(BRepGraph_ProductId(7)); }, + Standard_ProgramError); + EXPECT_THROW( + { [[maybe_unused]] auto aMut = aGraph.Editor().Occurrences().Mut(BRepGraph_OccurrenceId(7)); }, + Standard_ProgramError); #endif } @@ -560,15 +554,16 @@ TEST(BRepGraph_AssemblyTest, GlobalPlacement_DeepNesting) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); // Build: RootAssembly -> (OccSubAsm) -> SubAssembly -> (OccPart) -> Part - const BRepGraph_ProductId aSubAsmId = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aRootAsmId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aSubAsmId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aSubAsmId); + const BRepGraph_ProductId aRootAsmId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootAsmId); gp_Trsf aTrsf1; aTrsf1.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); @@ -577,16 +572,13 @@ TEST(BRepGraph_AssemblyTest, GlobalPlacement_DeepNesting) // RootAssembly places SubAssembly with aTrsf2 (top-level occurrence, no parent occ). const BRepGraph_OccurrenceId anOccSubAsm = - aGraph.Editor().Products().LinkProducts(aRootAsmId, aSubAsmId, TopLoc_Location(aTrsf2)); + aGraph.Editor().Products().Append(aRootAsmId, aSubAsmId, TopLoc_Location(aTrsf2)); // SubAssembly places Part with aTrsf1, with parent occurrence = anOccSubAsm. const BRepGraph_OccurrenceId anOccPart = - aGraph.Editor().Products().LinkProducts(aSubAsmId, - aPartId, - TopLoc_Location(aTrsf1), - anOccSubAsm); + aGraph.Editor().Products().Append(aSubAsmId, aPartId, TopLoc_Location(aTrsf1), anOccSubAsm); // OccurrenceLocation returns the local location from the OccurrenceRef. - // Global placement composition (parent chain walk) is handled by PathView. + // Global placement composition (parent chain walk) is handled by explorers. TopLoc_Location aLocalPart = aGraph.Topo().Occurrences().OccurrenceLocation(anOccPart); const gp_Trsf& aPartTrsf = aLocalPart.Transformation(); EXPECT_NEAR(aPartTrsf.TranslationPart().X(), 100.0, Precision::Confusion()); @@ -606,46 +598,43 @@ TEST(BRepGraph_AssemblyTest, NbNodes_IncludesAssembly) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes19 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes19 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const size_t aNbNodesAfterBuild = aGraph.Topo().Gen().NbNodes(); // Should include the auto-created root product. EXPECT_GE(aNbNodesAfterBuild, 1u); // Add assembly + occurrence. - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); - (void)aGraph.Editor().Products().LinkProducts(aAssemblyId, - BRepGraph_ProductId::Start(), - TopLoc_Location()); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId = + aGraph.Editor().Products().Append(aAssemblyId, BRepGraph_ProductId::Start(), TopLoc_Location()); const size_t aNbNodesAfterAssembly = aGraph.Topo().Gen().NbNodes(); EXPECT_EQ(aNbNodesAfterAssembly, aNbNodesAfterBuild + 2); // +1 product, +1 occurrence } // ============================================================================= -// OccurrencesOfProduct_ReverseIndex +// OccurrencesOfProduct_Relations // ============================================================================= -TEST(BRepGraph_AssemblyTest, OccurrencesOfProduct_ReverseIndex) +TEST(BRepGraph_AssemblyTest, OccurrencesOfProduct_Relations) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes20 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes20 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); - (void)aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); - (void)aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId1 = + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId2 = + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); - // Build the product-occurrence reverse index manually. - BRepGraphInc_ReverseIndex aRevIdx; - // We test the reverse index build via Storage's exposed reverse index. - // Since BuildReverseIndex doesn't cover product/occurrences yet, - // we test through the NbComponents API. + // We test product-occurrence relation maintenance through the NbComponents API. EXPECT_EQ(aGraph.Topo().Products().NbComponents(aAssemblyId), 2); } @@ -657,18 +646,18 @@ TEST(BRepGraph_AssemblyTest, Product_Count) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes21 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes21 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - (void)aGraph.Editor().Products().CreateEmptyProduct(); + [[maybe_unused]] const BRepGraph_ProductId anEmptyProduct = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anEmptyProduct); - int aCount = 0; + size_t aCount = 0; for (BRepGraph_ProductIterator aProductIt(aGraph); aProductIt.More(); aProductIt.Next()) { ++aCount; } - EXPECT_EQ(aCount, 2); // auto root + added assembly + EXPECT_EQ(aCount, 2u); // auto root + added assembly } // ============================================================================= @@ -679,22 +668,24 @@ TEST(BRepGraph_AssemblyTest, Occurrence_Count) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes22 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes22 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); - (void)aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); - (void)aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId1 = + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId2 = + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); - int aCount = 0; + size_t aCount = 0; for (BRepGraph_OccurrenceIterator anOccIt(aGraph); anOccIt.More(); anOccIt.Next()) { ++aCount; } // 1 shape-root occurrence (from Build) + 2 added occurrences = 3. - EXPECT_EQ(aCount, 3); + EXPECT_EQ(aCount, 3u); } // ============================================================================= @@ -723,9 +714,8 @@ TEST(BRepGraph_AssemblyTest, UID_IsAssembly) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes23 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes23 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); // The auto-created root product should have a UID with IsAssembly() == false // (it's a part, but its UID Kind is Product, so IsAssembly() on UID checks the Kind). @@ -743,20 +733,18 @@ TEST(BRepGraph_AssemblyTest, LinkProducts_InvalidParent_ReturnsInvalid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes24 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes24 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - BRepGraph_OccurrenceId aResult = - aGraph.Editor().Products().LinkProducts(BRepGraph_ProductId(999), - BRepGraph_ProductId::Start(), - TopLoc_Location()); + BRepGraph_OccurrenceId aResult = aGraph.Editor().Products().Append(BRepGraph_ProductId(999), + BRepGraph_ProductId::Start(), + TopLoc_Location()); EXPECT_FALSE(aResult.IsValid()); // Out-of-bounds referenced product index. - aResult = aGraph.Editor().Products().LinkProducts(BRepGraph_ProductId::Start(), - BRepGraph_ProductId(999), - TopLoc_Location()); + aResult = aGraph.Editor().Products().Append(BRepGraph_ProductId::Start(), + BRepGraph_ProductId(999), + TopLoc_Location()); EXPECT_FALSE(aResult.IsValid()); } @@ -768,15 +756,14 @@ TEST(BRepGraph_AssemblyTest, LinkProducts_SelfReference_ReturnsInvalid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes25 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes25 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); // Self-referencing: a product cannot be an occurrence of itself. const BRepGraph_OccurrenceId aResult = - aGraph.Editor().Products().LinkProducts(aPartId, aPartId, TopLoc_Location()); + aGraph.Editor().Products().Append(aPartId, aPartId, TopLoc_Location()); EXPECT_FALSE(aResult.IsValid()); } @@ -788,18 +775,18 @@ TEST(BRepGraph_AssemblyTest, RootProducts_RemovedOccurrence_DoesNotAffectRoots) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes26 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes26 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); const BRepGraph_OccurrenceId anOccId = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); // Before removal: only assembly is root (part is referenced). - NCollection_DynamicArray aRoots = collectRootProducts(aGraph); - EXPECT_EQ(aRoots.Length(), 1); + NCollection_LinearVector aRoots = collectRootProducts(aGraph); + EXPECT_EQ(aRoots.Size(), 1); EXPECT_EQ(aRoots.Value(0), aAssemblyId); // Remove the occurrence - part becomes a root again (no longer referenced). @@ -807,7 +794,7 @@ TEST(BRepGraph_AssemblyTest, RootProducts_RemovedOccurrence_DoesNotAffectRoots) aRoots = collectRootProducts(aGraph); // Both part and assembly are now roots (part is no longer referenced by any occurrence). - EXPECT_GE(aRoots.Length(), 1); + EXPECT_GE(aRoots.Size(), 1); EXPECT_TRUE(hasRootProduct(aRoots, aAssemblyId)); } @@ -821,12 +808,12 @@ TEST(BRepGraph_AssemblyTest, GlobalPlacement_DAGSharing_DistinctPathsGiveDistinc // Each occurrence has its own placement chain - no ambiguity. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes27 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes27 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); gp_Trsf aTrsf1; aTrsf1.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); @@ -834,9 +821,9 @@ TEST(BRepGraph_AssemblyTest, GlobalPlacement_DAGSharing_DistinctPathsGiveDistinc aTrsf2.SetTranslation(gp_Vec(0.0, 200.0, 0.0)); const BRepGraph_OccurrenceId anOcc1 = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)); const BRepGraph_OccurrenceId anOcc2 = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)); // Same part, different occurrences, different global placements. TopLoc_Location aGlobal1 = aGraph.Topo().Occurrences().OccurrenceLocation(anOcc1); @@ -856,26 +843,25 @@ TEST(BRepGraph_AssemblyTest, LinkProducts_RemovedProduct_ReturnsInvalid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes28 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes28 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); // Remove the assembly. aGraph.Editor().Gen().RemoveNode(aAssemblyId); // Cannot add occurrence to a removed product. const BRepGraph_OccurrenceId aResult = - aGraph.Editor().Products().LinkProducts(aAssemblyId, - BRepGraph_ProductId::Start(), - TopLoc_Location()); + aGraph.Editor().Products().Append(aAssemblyId, BRepGraph_ProductId::Start(), TopLoc_Location()); EXPECT_FALSE(aResult.IsValid()); // Cannot reference a removed product either. - const BRepGraph_ProductId aAsm2 = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAsm2 = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAsm2); aGraph.Editor().Gen().RemoveNode(BRepGraph_ProductId::Start()); const BRepGraph_OccurrenceId aResult2 = - aGraph.Editor().Products().LinkProducts(aAsm2, BRepGraph_ProductId::Start(), TopLoc_Location()); + aGraph.Editor().Products().Append(aAsm2, BRepGraph_ProductId::Start(), TopLoc_Location()); EXPECT_FALSE(aResult2.IsValid()); } @@ -888,14 +874,16 @@ TEST(BRepGraph_AssemblyTest, GlobalPlacement_ThreeLevelNesting) // Root -> Mid -> Leaf, each with a distinct translation. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes29 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes29 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aLeafPart = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aMidAsm = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aRootAsm = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aTopAsm = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aMidAsm = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aMidAsm); + const BRepGraph_ProductId aRootAsm = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootAsm); + const BRepGraph_ProductId aTopAsm = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aTopAsm); gp_Trsf aT1, aT2, aT3; aT1.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); @@ -904,13 +892,13 @@ TEST(BRepGraph_AssemblyTest, GlobalPlacement_ThreeLevelNesting) // TopAsm places RootAsm. const BRepGraph_OccurrenceId anOccRoot = - aGraph.Editor().Products().LinkProducts(aTopAsm, aRootAsm, TopLoc_Location(aT3)); + aGraph.Editor().Products().Append(aTopAsm, aRootAsm, TopLoc_Location(aT3)); // RootAsm places MidAsm, parent occ = anOccRoot. const BRepGraph_OccurrenceId anOccMid = - aGraph.Editor().Products().LinkProducts(aRootAsm, aMidAsm, TopLoc_Location(aT2), anOccRoot); + aGraph.Editor().Products().Append(aRootAsm, aMidAsm, TopLoc_Location(aT2), anOccRoot); // MidAsm places Leaf, parent occ = anOccMid. const BRepGraph_OccurrenceId anOccLeaf = - aGraph.Editor().Products().LinkProducts(aMidAsm, aLeafPart, TopLoc_Location(aT1), anOccMid); + aGraph.Editor().Products().Append(aMidAsm, aLeafPart, TopLoc_Location(aT1), anOccMid); // OccurrenceLocation returns the local location only. // Verify each level has the correct local placement. @@ -937,9 +925,8 @@ TEST(BRepGraph_AssemblyTest, ShapesView_ProductShape_ReconstructsBuiltRootTransf BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes30 = - BRepGraph_Builder::Add(aGraph, aRootShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes30 = + aGraph.Shapes().Add(aRootShape); const TopoDS_Shape aProductShape = aGraph.Shapes().Shape(BRepGraph_ProductId::Start()); ASSERT_FALSE(aProductShape.IsNull()); @@ -960,26 +947,22 @@ TEST(BRepGraph_AssemblyTest, ShapesView_AssemblyProduct_ReconstructsChildOccurre { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes31 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes31 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); gp_Trsf aTrsf1; aTrsf1.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); gp_Trsf aTrsf2; aTrsf2.SetTranslation(gp_Vec(200.0, 0.0, 0.0)); - ASSERT_TRUE(aGraph.Editor() - .Products() - .LinkProducts(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)) - .IsValid()); - ASSERT_TRUE(aGraph.Editor() - .Products() - .LinkProducts(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)) - .IsValid()); + ASSERT_TRUE( + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)).IsValid()); + ASSERT_TRUE( + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)).IsValid()); const TopoDS_Shape aAssemblyShape = aGraph.Shapes().Shape(aAssemblyId); ASSERT_FALSE(aAssemblyShape.IsNull()); @@ -1008,29 +991,28 @@ TEST(BRepGraph_AssemblyTest, ShapesView_OccurrenceShape_UsesGlobalPlacementChain { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes32 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes32 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aSubAssembly = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aSubAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aSubAssembly); + const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootAssembly); gp_Trsf aParentTrsf; aParentTrsf.SetTranslation(gp_Vec(10.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aParentOccurrence = - aGraph.Editor().Products().LinkProducts(aRootAssembly, - aSubAssembly, - TopLoc_Location(aParentTrsf)); + aGraph.Editor().Products().Append(aRootAssembly, aSubAssembly, TopLoc_Location(aParentTrsf)); ASSERT_TRUE(aParentOccurrence.IsValid()); gp_Trsf aChildTrsf; aChildTrsf.SetTranslation(gp_Vec(20.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aChildOccurrence = - aGraph.Editor().Products().LinkProducts(aSubAssembly, - aPartId, - TopLoc_Location(aChildTrsf), - aParentOccurrence); + aGraph.Editor().Products().Append(aSubAssembly, + aPartId, + TopLoc_Location(aChildTrsf), + aParentOccurrence); ASSERT_TRUE(aChildOccurrence.IsValid()); const TopoDS_Shape aSubAssemblyShape = aGraph.Shapes().Shape(aSubAssembly); @@ -1059,46 +1041,43 @@ TEST(BRepGraph_AssemblyTest, ShapesView_OccurrenceShape_FiltersNestedChildrenByP { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes33 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes33 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aSubAssembly = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aSubAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aSubAssembly); + const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootAssembly); gp_Trsf aParentTrsf1; aParentTrsf1.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aParentOccurrence1 = - aGraph.Editor().Products().LinkProducts(aRootAssembly, - aSubAssembly, - TopLoc_Location(aParentTrsf1)); + aGraph.Editor().Products().Append(aRootAssembly, aSubAssembly, TopLoc_Location(aParentTrsf1)); ASSERT_TRUE(aParentOccurrence1.IsValid()); gp_Trsf aParentTrsf2; aParentTrsf2.SetTranslation(gp_Vec(200.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aParentOccurrence2 = - aGraph.Editor().Products().LinkProducts(aRootAssembly, - aSubAssembly, - TopLoc_Location(aParentTrsf2)); + aGraph.Editor().Products().Append(aRootAssembly, aSubAssembly, TopLoc_Location(aParentTrsf2)); ASSERT_TRUE(aParentOccurrence2.IsValid()); gp_Trsf aChildTrsf1; aChildTrsf1.SetTranslation(gp_Vec(10.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aChildOccurrence1 = - aGraph.Editor().Products().LinkProducts(aSubAssembly, - aPartId, - TopLoc_Location(aChildTrsf1), - aParentOccurrence1); + aGraph.Editor().Products().Append(aSubAssembly, + aPartId, + TopLoc_Location(aChildTrsf1), + aParentOccurrence1); ASSERT_TRUE(aChildOccurrence1.IsValid()); gp_Trsf aChildTrsf2; aChildTrsf2.SetTranslation(gp_Vec(20.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aChildOccurrence2 = - aGraph.Editor().Products().LinkProducts(aSubAssembly, - aPartId, - TopLoc_Location(aChildTrsf2), - aParentOccurrence2); + aGraph.Editor().Products().Append(aSubAssembly, + aPartId, + TopLoc_Location(aChildTrsf2), + aParentOccurrence2); ASSERT_TRUE(aChildOccurrence2.IsValid()); // Verify that occurrence shapes are non-null and have children. @@ -1129,59 +1108,53 @@ TEST(BRepGraph_AssemblyTest, { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes34 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes34 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aSubAssembly = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aSubAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aSubAssembly); + const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootAssembly); gp_Trsf aParentTrsf1; aParentTrsf1.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aParentOccurrence1 = - aGraph.Editor().Products().LinkProducts(aRootAssembly, - aSubAssembly, - TopLoc_Location(aParentTrsf1)); + aGraph.Editor().Products().Append(aRootAssembly, aSubAssembly, TopLoc_Location(aParentTrsf1)); ASSERT_TRUE(aParentOccurrence1.IsValid()); gp_Trsf aParentTrsf2; aParentTrsf2.SetTranslation(gp_Vec(200.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aParentOccurrence2 = - aGraph.Editor().Products().LinkProducts(aRootAssembly, - aSubAssembly, - TopLoc_Location(aParentTrsf2)); + aGraph.Editor().Products().Append(aRootAssembly, aSubAssembly, TopLoc_Location(aParentTrsf2)); ASSERT_TRUE(aParentOccurrence2.IsValid()); gp_Trsf aCommonChildTrsf; aCommonChildTrsf.SetTranslation(gp_Vec(5.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aCommonChildOccurrence = - aGraph.Editor().Products().LinkProducts(aSubAssembly, - aPartId, - TopLoc_Location(aCommonChildTrsf)); + aGraph.Editor().Products().Append(aSubAssembly, aPartId, TopLoc_Location(aCommonChildTrsf)); ASSERT_TRUE(aCommonChildOccurrence.IsValid()); gp_Trsf aBranchChildTrsf1; aBranchChildTrsf1.SetTranslation(gp_Vec(10.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aBranchChildOccurrence1 = - aGraph.Editor().Products().LinkProducts(aSubAssembly, - aPartId, - TopLoc_Location(aBranchChildTrsf1), - aParentOccurrence1); + aGraph.Editor().Products().Append(aSubAssembly, + aPartId, + TopLoc_Location(aBranchChildTrsf1), + aParentOccurrence1); ASSERT_TRUE(aBranchChildOccurrence1.IsValid()); gp_Trsf aBranchChildTrsf2; aBranchChildTrsf2.SetTranslation(gp_Vec(20.0, 0.0, 0.0)); const BRepGraph_OccurrenceId aBranchChildOccurrence2 = - aGraph.Editor().Products().LinkProducts(aSubAssembly, - aPartId, - TopLoc_Location(aBranchChildTrsf2), - aParentOccurrence2); + aGraph.Editor().Products().Append(aSubAssembly, + aPartId, + TopLoc_Location(aBranchChildTrsf2), + aParentOccurrence2); ASSERT_TRUE(aBranchChildOccurrence2.IsValid()); // Verify occurrence shapes are reconstructed and have children. - // Branch-specific filtering and global location composition depend on - // the PathView model which is not yet implemented. + // Branch-specific filtering and global location composition are handled by explorers. const TopoDS_Shape aOccurrenceShape1 = aGraph.Shapes().Shape(aParentOccurrence1); ASSERT_FALSE(aOccurrenceShape1.IsNull()); EXPECT_GE(aOccurrenceShape1.NbChildren(), 1); @@ -1192,25 +1165,25 @@ TEST(BRepGraph_AssemblyTest, } // ============================================================================= -// OccurrencesOfProduct_ViaReverseIndex +// OccurrencesOfProduct_ViaRelations // ============================================================================= -TEST(BRepGraph_AssemblyTest, OccurrencesOfProduct_ViaReverseIndex) +TEST(BRepGraph_AssemblyTest, OccurrencesOfProduct_ViaRelations) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes35 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes35 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAsmId = aGraph.Editor().Products().CreateEmptyProduct(); - (void)aGraph.Editor().Products().LinkProducts(aAsmId, aPartId, TopLoc_Location()); - (void)aGraph.Editor().Products().LinkProducts(aAsmId, aPartId, TopLoc_Location()); + const BRepGraph_ProductId aAsmId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAsmId); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId1 = + aGraph.Editor().Products().Append(aAsmId, aPartId, TopLoc_Location()); + [[maybe_unused]] const BRepGraph_OccurrenceId anOccId2 = + aGraph.Editor().Products().Append(aAsmId, aPartId, TopLoc_Location()); - // Rebuild reverse index to populate product->occurrences. - // (BuildReverseIndex is called during Build, but not after Builder mutations.) - // Access via DefsView which uses forward OccurrenceRefs. + // Access via DefsView, backed by incrementally maintained occurrence refs. EXPECT_EQ(aGraph.Topo().Products().NbComponents(aAsmId), 2); // The part product has 1 shape-root occurrence (auto-created by Build). @@ -1227,27 +1200,28 @@ TEST(BRepGraph_AssemblyTest, OccurrenceLocation_AlwaysTerminates) // No parent chain walk means no risk of infinite loops. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes36 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes36 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAsmId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAsmId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAsmId); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); const BRepGraph_OccurrenceId anOcc1 = - aGraph.Editor().Products().LinkProducts(aAsmId, aPartId, TopLoc_Location(aTrsf)); + aGraph.Editor().Products().Append(aAsmId, aPartId, TopLoc_Location(aTrsf)); const BRepGraph_OccurrenceId anOcc2 = - aGraph.Editor().Products().LinkProducts(aAsmId, aPartId, TopLoc_Location(aTrsf), anOcc1); + aGraph.Editor().Products().Append(aAsmId, aPartId, TopLoc_Location(aTrsf)); // OccurrenceLocation must terminate and return a location (local from the ref). TopLoc_Location aLoc1 = aGraph.Topo().Occurrences().OccurrenceLocation(anOcc1); EXPECT_NEAR(aLoc1.Transformation().TranslationPart().X(), 1.0, Precision::Confusion()); + ASSERT_TRUE(anOcc2.IsValid()); // anOcc2 also has its own local location from its ref. - TopLoc_Location aLoc2 = aGraph.Topo().Occurrences().OccurrenceLocation(anOcc2); - (void)aLoc2; // Just verify it doesn't hang. + const TopLoc_Location aLoc2 = aGraph.Topo().Occurrences().OccurrenceLocation(anOcc2); + EXPECT_FALSE(aLoc2.IsIdentity()); } // ============================================================================= @@ -1262,8 +1236,8 @@ TEST(BRepGraph_AssemblyTest, Add_RootProduct_PreservesShapeLocation) aTrsf.SetTranslation(gp_Vec(5.0, 6.0, 7.0)); aBox.Location(TopLoc_Location(aTrsf)); - const BRepGraph_Builder::Result aResult = BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aResult.Ok); + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aBox); + ASSERT_TRUE(aResult.IsOk()); ASSERT_TRUE(aResult.Product.IsValid()); ASSERT_TRUE(aResult.TopologyRoot.IsValid()); ASSERT_TRUE(aResult.Occurrence.IsValid()); @@ -1272,7 +1246,7 @@ TEST(BRepGraph_AssemblyTest, Add_RootProduct_PreservesShapeLocation) EXPECT_EQ(aGraph.RootProductIds().Value(0), aResult.Product); const BRepGraph_OccurrenceRefId anOccRefId = - aGraph.Topo().Products().Definition(aResult.Product).OccurrenceRefIds.Value(0); + aGraph.Topo().Products().Relations(aResult.Product).OccurrenceRefIds.Value(0); const TopLoc_Location& aLoc = aGraph.Refs().Occurrences().Entry(anOccRefId).LocalLocation; EXPECT_NEAR(aLoc.Transformation().TranslationPart().X(), 5.0, Precision::Confusion()); EXPECT_NEAR(aLoc.Transformation().TranslationPart().Y(), 6.0, Precision::Confusion()); @@ -1281,13 +1255,13 @@ TEST(BRepGraph_AssemblyTest, Add_RootProduct_PreservesShapeLocation) TEST(BRepGraph_AssemblyTest, Add_NoAutoProduct_TopologyOnly) { - BRepGraph aGraph; - BRepGraph_Builder::Options anOpts; + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOpts; anOpts.CreateAutoProduct = false; - const BRepGraph_Builder::Result aResult = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape(), anOpts); - ASSERT_TRUE(aResult.Ok); + const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape(), anOpts); + ASSERT_TRUE(aResult.IsOk()); EXPECT_FALSE(aResult.Product.IsValid()); EXPECT_FALSE(aResult.Occurrence.IsValid()); ASSERT_TRUE(aResult.TopologyRoot.IsValid()); @@ -1296,10 +1270,10 @@ TEST(BRepGraph_AssemblyTest, Add_NoAutoProduct_TopologyOnly) TEST(BRepGraph_AssemblyTest, Add_NullShape_ReturnsInvalidResult) { - BRepGraph aGraph; - TopoDS_Shape aNull; - const BRepGraph_Builder::Result aResult = BRepGraph_Builder::Add(aGraph, aNull); - EXPECT_FALSE(aResult.Ok); + BRepGraph aGraph; + TopoDS_Shape aNull; + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aNull); + EXPECT_FALSE(aResult.IsOk()); EXPECT_FALSE(aResult.Product.IsValid()); EXPECT_FALSE(aResult.TopologyRoot.IsValid()); } @@ -1311,7 +1285,8 @@ TEST(BRepGraph_AssemblyTest, Add_NullShape_ReturnsInvalidResult) TEST(BRepGraph_AssemblyTest, Add_ProductParent_CreatesChildPartAndOccurrence) { BRepGraph aGraph; - const BRepGraph_ProductId aParent = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aParent = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aParent); ASSERT_TRUE(aParent.IsValid()); gp_Trsf aTrsf; @@ -1319,18 +1294,19 @@ TEST(BRepGraph_AssemblyTest, Add_ProductParent_CreatesChildPartAndOccurrence) TopoDS_Shape aSphere = BRepPrimAPI_MakeSphere(5.0).Shape(); aSphere.Location(TopLoc_Location(aTrsf)); - const BRepGraph_Builder::Result aResult = - BRepGraph_Builder::Add(aGraph, aSphere, BRepGraph_NodeId(aParent)); - ASSERT_TRUE(aResult.Ok); + const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().Add(aSphere, BRepGraph_NodeId(aParent)); + ASSERT_TRUE(aResult.IsOk()); ASSERT_TRUE(aResult.TopologyRoot.IsValid()); ASSERT_TRUE(aResult.Product.IsValid()); ASSERT_TRUE(aResult.Occurrence.IsValid()); EXPECT_NE(aResult.Product, aParent); - const BRepGraphInc::ProductDef& aParentDef = aGraph.Topo().Products().Definition(aParent); - ASSERT_EQ(aParentDef.OccurrenceRefIds.Size(), 1u); - const BRepGraph_OccurrenceRefId anOccRefId = aParentDef.OccurrenceRefIds.Value(0); + const BRepGraphInc::ProductRelations& aParentRelations = + aGraph.Topo().Products().Relations(aParent); + ASSERT_EQ(aParentRelations.OccurrenceRefIds.Size(), 1u); + const BRepGraph_OccurrenceRefId anOccRefId = aParentRelations.OccurrenceRefIds.Value(0); const TopLoc_Location& aLoc = aGraph.Refs().Occurrences().Entry(anOccRefId).LocalLocation; EXPECT_NEAR(aLoc.Transformation().TranslationPart().X(), 2.0, Precision::Confusion()); } @@ -1343,13 +1319,13 @@ TEST(BRepGraph_AssemblyTest, Add_CompoundParent_AppendsAsChild) TopoDS_Shape aBox = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape(); BRep_Builder().Add(aCompound, aBox); - const BRepGraph_Builder::Result aRoot = BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aRoot.Ok); + const BRepGraph::ShapesView::Result aRoot = aGraph.Shapes().Add(aCompound); + ASSERT_TRUE(aRoot.IsOk()); ASSERT_EQ(aRoot.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Compound); const BRepGraph_CompoundId aCompoundId(aRoot.TopologyRoot); const uint32_t aRefsBefore = - static_cast(aGraph.Topo().Compounds().Definition(aCompoundId).ChildRefIds.Size()); + static_cast(aGraph.Topo().Compounds().Relations(aCompoundId).ChildRefIds.Size()); TopoDS_Shape aFace; for (TopExp_Explorer anExp(BRepPrimAPI_MakeBox(2.0, 2.0, 2.0).Shape(), TopAbs_FACE); anExp.More(); @@ -1359,22 +1335,56 @@ TEST(BRepGraph_AssemblyTest, Add_CompoundParent_AppendsAsChild) break; } ASSERT_FALSE(aFace.IsNull()); - const BRepGraph_Builder::Result aChild = - BRepGraph_Builder::Add(aGraph, aFace, BRepGraph_NodeId(aCompoundId)); - ASSERT_TRUE(aChild.Ok); + const BRepGraph::ShapesView::Result aChild = + aGraph.Shapes().Add(aFace, BRepGraph_NodeId(aCompoundId)); + ASSERT_TRUE(aChild.IsOk()); EXPECT_TRUE(aChild.InsertedRef.IsValid()); - EXPECT_EQ(aGraph.Topo().Compounds().Definition(aCompoundId).ChildRefIds.Size(), aRefsBefore + 1u); + EXPECT_EQ(aGraph.Topo().Compounds().Relations(aCompoundId).ChildRefIds.Size(), aRefsBefore + 1u); } TEST(BRepGraph_AssemblyTest, Add_InvalidParent_ReturnsInvalidResult) { - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes37 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape()); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes37 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape()); - TopoDS_Shape aBox = BRepPrimAPI_MakeBox(2.0, 2.0, 2.0).Shape(); - const BRepGraph_Builder::Result aResult = - BRepGraph_Builder::Add(aGraph, aBox, BRepGraph_NodeId()); - EXPECT_FALSE(aResult.Ok); + TopoDS_Shape aBox = BRepPrimAPI_MakeBox(2.0, 2.0, 2.0).Shape(); + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aBox, BRepGraph_NodeId()); + EXPECT_FALSE(aResult.IsOk()); +} + +TEST(BRepGraph_AssemblyTest, RootProductRemovalCompactsInPlaceAfterRepeatedRemoves) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_ProductId aRootA = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootA); + const BRepGraph_ProductId aRootB = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootB); + const BRepGraph_ProductId aRootC = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootC); + ASSERT_TRUE(aRootA.IsValid()); + ASSERT_TRUE(aRootB.IsValid()); + ASSERT_TRUE(aRootC.IsValid()); + + ASSERT_EQ(collectRootProducts(aGraph).Size(), 3u); + + aGraph.Editor().Gen().RemoveNode(aRootB); + NCollection_LinearVector aRoots = collectRootProducts(aGraph); + ASSERT_EQ(aRoots.Size(), 2u); + EXPECT_TRUE(hasRootProduct(aRoots, aRootA)); + EXPECT_FALSE(hasRootProduct(aRoots, aRootB)); + EXPECT_TRUE(hasRootProduct(aRoots, aRootC)); + + const BRepGraph_ProductId aRootD = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootD); + ASSERT_TRUE(aRootD.IsValid()); + aGraph.Editor().Gen().RemoveNode(aRootA); + aGraph.Editor().Gen().RemoveNode(aRootD); + + aRoots = collectRootProducts(aGraph); + ASSERT_EQ(aRoots.Size(), 1u); + EXPECT_EQ(aRoots.Value(0), aRootC); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_BatchOps_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_BatchOps_Test.cxx new file mode 100644 index 0000000000..9a94c03e78 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_BatchOps_Test.cxx @@ -0,0 +1,580 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// ======================================================================= +// Helper: create a simple face +// ======================================================================= + +static BRepGraph_FaceId createSimpleFace(BRepGraph& theGraph) +{ + const BRepGraph_VertexId aV0 = theGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = theGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + theGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(theGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = theGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + return theGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); +} + +// ======================================================================= +// ShellOps::Append (batch) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, ShellOps_AppendBatch_AllForward) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace2 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + ASSERT_TRUE(aFace2.IsValid()); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + + NCollection_Array1 aFaces(3); + aFaces.ChangeAt(0) = aFace0; + aFaces.ChangeAt(1) = aFace1; + aFaces.ChangeAt(2) = aFace2; + + const NCollection_Array1 aRefs = + aGraph.Editor().Shells().Append(aShell, aFaces); + EXPECT_EQ(aRefs.Size(), 3); + EXPECT_TRUE(aRefs.At(0).IsValid()); + EXPECT_TRUE(aRefs.At(1).IsValid()); + EXPECT_TRUE(aRefs.At(2).IsValid()); +} + +TEST(BRepGraph_BatchOpsTest, ShellOps_AppendBatch_WithOrientations) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + + NCollection_Array1 aFaces(2); + aFaces.ChangeAt(0) = aFace0; + aFaces.ChangeAt(1) = aFace1; + + NCollection_Array1 aOrientations(2); + aOrientations.ChangeAt(0) = TopAbs_FORWARD; + aOrientations.ChangeAt(1) = TopAbs_REVERSED; + + const NCollection_Array1 aRefs = + aGraph.Editor().Shells().Append(aShell, aFaces, aOrientations); + EXPECT_EQ(aRefs.Size(), 2); + EXPECT_TRUE(aRefs.At(0).IsValid()); + EXPECT_TRUE(aRefs.At(1).IsValid()); +} + +TEST(BRepGraph_BatchOpsTest, ShellOps_AppendBatch_InvalidFace_ReturnsEmpty) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + + NCollection_Array1 aFaces(2); + aFaces.ChangeAt(0) = aFace0; + aFaces.ChangeAt(1) = BRepGraph_FaceId(999); // invalid + + const NCollection_Array1 aRefs = + aGraph.Editor().Shells().Append(aShell, aFaces); + EXPECT_EQ(aRefs.Size(), 0); +} + +TEST(BRepGraph_BatchOpsTest, ShellOps_AppendBatch_InvalidShell_ReturnsEmpty) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + + NCollection_Array1 aFaces(1); + aFaces.ChangeAt(0) = aFace0; + + const NCollection_Array1 aRefs = + aGraph.Editor().Shells().Append(BRepGraph_ShellId(999), aFaces); + EXPECT_EQ(aRefs.Size(), 0); +} + +// ======================================================================= +// ShellOps::RemoveFaces (batch, all-or-nothing) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, ShellOps_RemoveFaces_AllRemoved) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + + const BRepGraph_FaceRefId aRef0 = aGraph.Editor().Shells().Append(aShell, aFace0); + const BRepGraph_FaceRefId aRef1 = aGraph.Editor().Shells().Append(aShell, aFace1); + ASSERT_TRUE(aRef0.IsValid()); + ASSERT_TRUE(aRef1.IsValid()); + + NCollection_Array1 aRefs(2); + aRefs.ChangeAt(0) = aRef0; + aRefs.ChangeAt(1) = aRef1; + + EXPECT_TRUE(aGraph.Editor().Shells().RemoveFaces(aShell, aRefs)); +} + +TEST(BRepGraph_BatchOpsTest, ShellOps_RemoveFaces_InvalidRef_ReturnsFalse) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + + const BRepGraph_FaceRefId aRef0 = aGraph.Editor().Shells().Append(aShell, aFace0); + ASSERT_TRUE(aRef0.IsValid()); + + NCollection_Array1 aRefs(2); + aRefs.ChangeAt(0) = aRef0; + aRefs.ChangeAt(1) = BRepGraph_FaceRefId(999); // invalid + + EXPECT_FALSE(aGraph.Editor().Shells().RemoveFaces(aShell, aRefs)); +} + +// ======================================================================= +// SolidOps::Append (batch) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, SolidOps_AppendBatch_AllForward) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + const BRepGraph_ShellId aShell0 = aGraph.Editor().Shells().Add(); + const BRepGraph_ShellId aShell1 = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell0.IsValid()); + ASSERT_TRUE(aShell1.IsValid()); + + aGraph.Editor().Shells().Append(aShell0, aFace0); + aGraph.Editor().Shells().Append(aShell1, aFace1); + + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid.IsValid()); + + NCollection_Array1 aShells(2); + aShells.ChangeAt(0) = aShell0; + aShells.ChangeAt(1) = aShell1; + + const NCollection_Array1 aRefs = + aGraph.Editor().Solids().Append(aSolid, aShells); + EXPECT_EQ(aRefs.Size(), 2); + EXPECT_TRUE(aRefs.At(0).IsValid()); + EXPECT_TRUE(aRefs.At(1).IsValid()); +} + +// ======================================================================= +// SolidOps::RemoveShells (batch, all-or-nothing) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, SolidOps_RemoveShells_AllRemoved) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + + const BRepGraph_ShellId aShell0 = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell0.IsValid()); + aGraph.Editor().Shells().Append(aShell0, aFace0); + + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid.IsValid()); + + const BRepGraph_ShellRefId aRef0 = aGraph.Editor().Solids().Append(aSolid, aShell0); + ASSERT_TRUE(aRef0.IsValid()); + + NCollection_Array1 aRefs(1); + aRefs.ChangeAt(0) = aRef0; + + EXPECT_TRUE(aGraph.Editor().Solids().RemoveShells(aSolid, aRefs)); +} + +// ======================================================================= +// CompoundOps::Append (batch) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, CompoundOps_AppendBatch_AllForward) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + NCollection_LinearVector anEmptyChildren; + const BRepGraph_CompoundId aCompound = + aGraph.Editor().Compounds().Add(anEmptyChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + NCollection_Array1 aChildren(2); + aChildren.ChangeAt(0) = BRepGraph_NodeId(aFace0); + aChildren.ChangeAt(1) = BRepGraph_NodeId(aFace1); + + const NCollection_Array1 aRefs = + aGraph.Editor().Compounds().Append(aCompound, aChildren); + EXPECT_EQ(aRefs.Size(), 2); + EXPECT_TRUE(aRefs.At(0).IsValid()); + EXPECT_TRUE(aRefs.At(1).IsValid()); +} + +// ======================================================================= +// CompoundOps::RemoveChildren (batch, all-or-nothing) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, CompoundOps_RemoveChildren_AllRemoved) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + + NCollection_LinearVector anEmptyChildren; + const BRepGraph_CompoundId aCompound = + aGraph.Editor().Compounds().Add(anEmptyChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + const BRepGraph_ChildRefId aRef0 = + aGraph.Editor().Compounds().Append(aCompound, BRepGraph_NodeId(aFace0)); + ASSERT_TRUE(aRef0.IsValid()); + + NCollection_Array1 aRefs(1); + aRefs.ChangeAt(0) = aRef0; + + EXPECT_TRUE(aGraph.Editor().Compounds().RemoveChildren(aCompound, aRefs)); +} + +// ======================================================================= +// CompoundOps::ReplaceChild +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, CompoundOps_ReplaceChild) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + NCollection_LinearVector anEmptyChildren; + const BRepGraph_CompoundId aCompound = + aGraph.Editor().Compounds().Add(anEmptyChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + const BRepGraph_ChildRefId aRef0 = + aGraph.Editor().Compounds().Append(aCompound, BRepGraph_NodeId(aFace0)); + ASSERT_TRUE(aRef0.IsValid()); + + aGraph.Editor().Compounds().ReplaceChild(aRef0, BRepGraph_NodeId(aFace1)); +} + +// ======================================================================= +// CompSolidOps::Append (batch) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, CompSolidOps_AppendBatch_AllForward) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + const BRepGraph_ShellId aShell0 = aGraph.Editor().Shells().Add(); + const BRepGraph_ShellId aShell1 = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell0.IsValid()); + ASSERT_TRUE(aShell1.IsValid()); + + aGraph.Editor().Shells().Append(aShell0, aFace0); + aGraph.Editor().Shells().Append(aShell1, aFace1); + + const BRepGraph_SolidId aSolid0 = aGraph.Editor().Solids().Add(); + const BRepGraph_SolidId aSolid1 = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid0.IsValid()); + ASSERT_TRUE(aSolid1.IsValid()); + + aGraph.Editor().Solids().Append(aSolid0, aShell0); + aGraph.Editor().Solids().Append(aSolid1, aShell1); + + NCollection_LinearVector anEmptySolids; + const BRepGraph_CompSolidId aCompSolid = + aGraph.Editor().CompSolids().Add(anEmptySolids.ToArray1()); + ASSERT_TRUE(aCompSolid.IsValid()); + + NCollection_Array1 aSolids(2); + aSolids.ChangeAt(0) = aSolid0; + aSolids.ChangeAt(1) = aSolid1; + + const NCollection_Array1 aRefs = + aGraph.Editor().CompSolids().Append(aCompSolid, aSolids); + EXPECT_EQ(aRefs.Size(), 2); + EXPECT_TRUE(aRefs.At(0).IsValid()); + EXPECT_TRUE(aRefs.At(1).IsValid()); +} + +// ======================================================================= +// CompSolidOps::RemoveSolids (batch, all-or-nothing) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, CompSolidOps_RemoveSolids_AllRemoved) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + + const BRepGraph_ShellId aShell0 = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell0.IsValid()); + aGraph.Editor().Shells().Append(aShell0, aFace0); + + const BRepGraph_SolidId aSolid0 = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid0.IsValid()); + aGraph.Editor().Solids().Append(aSolid0, aShell0); + + NCollection_LinearVector anEmptySolids; + const BRepGraph_CompSolidId aCompSolid = + aGraph.Editor().CompSolids().Add(anEmptySolids.ToArray1()); + ASSERT_TRUE(aCompSolid.IsValid()); + + const BRepGraph_SolidRefId aRef0 = aGraph.Editor().CompSolids().Append(aCompSolid, aSolid0); + ASSERT_TRUE(aRef0.IsValid()); + + NCollection_Array1 aRefs(1); + aRefs.ChangeAt(0) = aRef0; + + EXPECT_TRUE(aGraph.Editor().CompSolids().RemoveSolids(aCompSolid, aRefs)); +} + +// ======================================================================= +// CompSolidOps::ReplaceSolid +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, CompSolidOps_ReplaceSolid) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + const BRepGraph_ShellId aShell0 = aGraph.Editor().Shells().Add(); + const BRepGraph_ShellId aShell1 = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell0.IsValid()); + ASSERT_TRUE(aShell1.IsValid()); + + aGraph.Editor().Shells().Append(aShell0, aFace0); + aGraph.Editor().Shells().Append(aShell1, aFace1); + + const BRepGraph_SolidId aSolid0 = aGraph.Editor().Solids().Add(); + const BRepGraph_SolidId aSolid1 = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid0.IsValid()); + ASSERT_TRUE(aSolid1.IsValid()); + + aGraph.Editor().Solids().Append(aSolid0, aShell0); + aGraph.Editor().Solids().Append(aSolid1, aShell1); + + NCollection_LinearVector anEmptySolids; + const BRepGraph_CompSolidId aCompSolid = + aGraph.Editor().CompSolids().Add(anEmptySolids.ToArray1()); + ASSERT_TRUE(aCompSolid.IsValid()); + + const BRepGraph_SolidRefId aRef0 = aGraph.Editor().CompSolids().Append(aCompSolid, aSolid0); + ASSERT_TRUE(aRef0.IsValid()); + + aGraph.Editor().CompSolids().ReplaceSolid(aRef0, aSolid1); +} + +// ======================================================================= +// ProductOps::Append (batch) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, ProductOps_AppendBatch) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + + const BRepGraph_ShellId aShell0 = aGraph.Editor().Shells().Add(); + const BRepGraph_ShellId aShell1 = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell0.IsValid()); + ASSERT_TRUE(aShell1.IsValid()); + + aGraph.Editor().Shells().Append(aShell0, aFace0); + aGraph.Editor().Shells().Append(aShell1, aFace1); + + const BRepGraph_SolidId aSolid0 = aGraph.Editor().Solids().Add(); + const BRepGraph_SolidId aSolid1 = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid0.IsValid()); + ASSERT_TRUE(aSolid1.IsValid()); + + aGraph.Editor().Solids().Append(aSolid0, aShell0); + aGraph.Editor().Solids().Append(aSolid1, aShell1); + + const BRepGraph_ProductId aChildProduct0 = + aGraph.Editor().Products().Add(BRepGraph_NodeId(aSolid0)); + const BRepGraph_ProductId aChildProduct1 = + aGraph.Editor().Products().Add(BRepGraph_NodeId(aSolid1)); + ASSERT_TRUE(aChildProduct0.IsValid()); + ASSERT_TRUE(aChildProduct1.IsValid()); + + const BRepGraph_ProductId aParentProduct = aGraph.Editor().Products().Add(); + ASSERT_TRUE(aParentProduct.IsValid()); + + NCollection_Array1 aChildren(2); + aChildren.ChangeAt(0) = aChildProduct0; + aChildren.ChangeAt(1) = aChildProduct1; + + NCollection_Array1 aPlacements(2); + aPlacements.ChangeAt(0) = TopLoc_Location(); + aPlacements.ChangeAt(1) = TopLoc_Location(); + + const NCollection_Array1 aRefs = + aGraph.Editor().Products().Append(aParentProduct, aChildren, aPlacements); + EXPECT_EQ(aRefs.Size(), 2); + EXPECT_TRUE(aRefs.At(0).IsValid()); + EXPECT_TRUE(aRefs.At(1).IsValid()); +} + +// ======================================================================= +// ProductOps::RemoveOccurrences (batch, all-or-nothing) +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, ProductOps_RemoveOccurrences_AllRemoved) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + + const BRepGraph_ShellId aShell0 = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell0.IsValid()); + aGraph.Editor().Shells().Append(aShell0, aFace0); + + const BRepGraph_SolidId aSolid0 = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid0.IsValid()); + aGraph.Editor().Solids().Append(aSolid0, aShell0); + + const BRepGraph_ProductId aChildProduct = + aGraph.Editor().Products().Add(BRepGraph_NodeId(aSolid0)); + ASSERT_TRUE(aChildProduct.IsValid()); + + const BRepGraph_ProductId aParentProduct = aGraph.Editor().Products().Add(); + ASSERT_TRUE(aParentProduct.IsValid()); + + BRepGraph_OccurrenceRefId anOutRefId; + [[maybe_unused]] BRepGraph_OccurrenceId anOccId = + aGraph.Editor().Products().Append(aParentProduct, + aChildProduct, + TopLoc_Location(), + BRepGraph_OccurrenceId(), + &anOutRefId); + ASSERT_TRUE(anOccId.IsValid()); + ASSERT_TRUE(anOutRefId.IsValid()); + + NCollection_Array1 aRefs(1); + aRefs.ChangeAt(0) = anOutRefId; + + EXPECT_TRUE(aGraph.Editor().Products().RemoveOccurrences(aParentProduct, aRefs)); +} + +// ======================================================================= +// Validation after batch operations +// ======================================================================= + +TEST(BRepGraph_BatchOpsTest, Validation_AfterBatchOperations) +{ + BRepGraph aGraph; + + const BRepGraph_FaceId aFace0 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace1 = createSimpleFace(aGraph); + const BRepGraph_FaceId aFace2 = createSimpleFace(aGraph); + ASSERT_TRUE(aFace0.IsValid()); + ASSERT_TRUE(aFace1.IsValid()); + ASSERT_TRUE(aFace2.IsValid()); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + + // Batch append + NCollection_Array1 aFaces(3); + aFaces.ChangeAt(0) = aFace0; + aFaces.ChangeAt(1) = aFace1; + aFaces.ChangeAt(2) = aFace2; + + const NCollection_Array1 aRefs = + aGraph.Editor().Shells().Append(aShell, aFaces); + EXPECT_EQ(aRefs.Size(), 3); + EXPECT_TRUE(aRefs.At(0).IsValid()); + EXPECT_TRUE(aRefs.At(1).IsValid()); + EXPECT_TRUE(aRefs.At(2).IsValid()); + + // Batch remove + NCollection_Array1 aRefsToRemove(2); + aRefsToRemove.ChangeAt(0) = aRefs.At(0); + aRefsToRemove.ChangeAt(1) = aRefs.At(1); + + EXPECT_TRUE(aGraph.Editor().Shells().RemoveFaces(aShell, aRefsToRemove)); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Benchmark_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Benchmark_Test.cxx deleted file mode 100644 index d9bc5116c3..0000000000 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Benchmark_Test.cxx +++ /dev/null @@ -1,227 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace -{ - -constexpr int THE_WARMUP_ITERS = 2; -constexpr int THE_MEASURE_ITERS = 20; - -TopoDS_Compound makeFaceCloud(int theNbFaces) -{ - BRep_Builder aBuilder; - TopoDS_Compound aCompound; - aBuilder.MakeCompound(aCompound); - - int aNbAdded = 0; - int aBoxIdx = 0; - while (aNbAdded < theNbFaces) - { - const double aSize = 10.0 + static_cast(aBoxIdx % 7); - BRepPrimAPI_MakeBox aBoxMaker(aSize, aSize + 1.0, aSize + 2.0); - const TopoDS_Shape& aBox = aBoxMaker.Shape(); - - for (TopExp_Explorer aFaceExp(aBox, TopAbs_FACE); aFaceExp.More() && aNbAdded < theNbFaces; - aFaceExp.Next()) - { - BRepBuilderAPI_Copy aCopy(aFaceExp.Current(), true); - aBuilder.Add(aCompound, aCopy.Shape()); - ++aNbAdded; - } - - ++aBoxIdx; - } - - return aCompound; -} - -template -double runBenchmark(const char* theLabel, Func theFunc) -{ - for (int aWarmupIter = 0; aWarmupIter < THE_WARMUP_ITERS; ++aWarmupIter) - { - theFunc(); - } - - double aTotal = 0.0; - for (int anIter = 0; anIter < THE_MEASURE_ITERS; ++anIter) - { - const std::chrono::steady_clock::time_point aStart = std::chrono::steady_clock::now(); - theFunc(); - const std::chrono::steady_clock::time_point anEnd = std::chrono::steady_clock::now(); - aTotal += std::chrono::duration(anEnd - aStart).count(); - } - - const double anAvg = aTotal / static_cast(THE_MEASURE_ITERS); - std::cout << "[ PERF ] " << theLabel << ": avg " << anAvg << " s over " << THE_MEASURE_ITERS - << " iters" << '\n'; - return anAvg; -} - -} // namespace - -TEST(BRepGraph_BenchmarkTest, Smoke_BuildReconstructAndAdjacency) -{ - const TopoDS_Compound aFaces = makeFaceCloud(120); - - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aFaces); - ASSERT_TRUE(aGraph.IsDone()); - ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); - - const BRepGraph_NodeId aFaceNodeId(BRepGraph_NodeId::Kind::Face, 0); - const TopoDS_Shape aFaceShape = aGraph.Shapes().Reconstruct(aFaceNodeId); - EXPECT_FALSE(aFaceShape.IsNull()); - - const BRepGraph_FaceId aFaceId(0); - const NCollection_DynamicArray anAdj = - aGraph.Topo().Faces().Adjacent(aFaceId, aGraph.Allocator()); - EXPECT_GE(anAdj.Length(), 0); -} - -TEST(BRepGraph_BenchmarkTest, DISABLED_Build_100Faces) -{ - const TopoDS_Compound aFaces = makeFaceCloud(100); - const double aAvg = runBenchmark("Build 100 faces", [&]() { - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aFaces); - EXPECT_TRUE(aGraph.IsDone()); - }); - EXPECT_GT(aAvg, 0.0); -} - -TEST(BRepGraph_BenchmarkTest, DISABLED_Build_1000Faces) -{ - const TopoDS_Compound aFaces = makeFaceCloud(1000); - const double aAvg = runBenchmark("Build 1000 faces", [&]() { - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aFaces); - EXPECT_TRUE(aGraph.IsDone()); - }); - EXPECT_GT(aAvg, 0.0); -} - -TEST(BRepGraph_BenchmarkTest, DISABLED_Build_10000Faces) -{ - const TopoDS_Compound aFaces = makeFaceCloud(10000); - const double aAvg = runBenchmark("Build 10000 faces", [&]() { - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aFaces); - EXPECT_TRUE(aGraph.IsDone()); - }); - EXPECT_GT(aAvg, 0.0); -} - -TEST(BRepGraph_BenchmarkTest, DISABLED_Build_1000Faces_Parallel) -{ - const TopoDS_Compound aFaces = makeFaceCloud(1000); - const double aAvg = runBenchmark("Build 1000 faces parallel", [&]() { - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aFaces, BRepGraph_Builder::Options{{}, true, false, true}); - EXPECT_TRUE(aGraph.IsDone()); - }); - EXPECT_GT(aAvg, 0.0); -} - -TEST(BRepGraph_BenchmarkTest, DISABLED_Build_10000Faces_Parallel) -{ - const TopoDS_Compound aFaces = makeFaceCloud(10000); - const double aAvg = runBenchmark("Build 10000 faces parallel", [&]() { - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aFaces, BRepGraph_Builder::Options{{}, true, false, true}); - EXPECT_TRUE(aGraph.IsDone()); - }); - EXPECT_GT(aAvg, 0.0); -} - -TEST(BRepGraph_BenchmarkTest, DISABLED_Reconstruct_RoundTrip) -{ - const TopoDS_Compound aFaces = makeFaceCloud(10000); - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aFaces); - ASSERT_TRUE(aGraph.IsDone()); - - const int aNbFaces = aGraph.Topo().Faces().Nb(); - ASSERT_GT(aNbFaces, 0); - - const double aAvg = runBenchmark("Reconstruct 10000 faces", [&]() { - for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) - { - const TopoDS_Shape aShape = - aGraph.Shapes().Reconstruct(BRepGraph_NodeId(aFaceIt.CurrentId())); - EXPECT_FALSE(aShape.IsNull()); - } - }); - - const double aPerFace = aAvg / static_cast(aNbFaces); - std::cout << "[ PERF ] Reconstruct per-face avg: " << aPerFace << " s" << '\n'; - EXPECT_GT(aAvg, 0.0); -} - -TEST(BRepGraph_BenchmarkTest, DISABLED_SpatialQuery_Throughput) -{ - const TopoDS_Compound aFaces = makeFaceCloud(10000); - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aFaces); - ASSERT_TRUE(aGraph.IsDone()); - - const int aNbFaces = aGraph.Topo().Faces().Nb(); - ASSERT_GT(aNbFaces, 0); - - const double aAvg = runBenchmark("SpatialQuery 10000 faces", [&]() { - for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) - { - const NCollection_DynamicArray anAdj = - aGraph.Topo().Faces().Adjacent(aFaceIt.CurrentId(), aGraph.Allocator()); - EXPECT_GE(anAdj.Length(), 0); - } - }); - - const double aPerQuery = aAvg / static_cast(aNbFaces); - std::cout << "[ PERF ] SpatialQuery per-face avg: " << aPerQuery << " s" << '\n'; - EXPECT_GT(aAvg, 0.0); -} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx index 3e3e537762..a5b0ce0e4f 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Build_Test.cxx @@ -19,19 +19,21 @@ #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 @@ -41,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +60,9 @@ #include +#include +#include + // ============================================================================= // Helper: count unique shapes of a given type via TopExp::MapShapes // ============================================================================= @@ -68,12 +74,6 @@ static int countUnique(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType) return aMap.Extent(); } -static void registerStandardLayers(BRepGraph& theGraph) -{ - theGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerParam()); - theGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerRegularity()); -} - static TopoDS_Shape makeStandaloneWire() { BRepBuilderAPI_MakeEdge aMkEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(10.0, 0.0, 0.0)); @@ -84,7 +84,7 @@ static TopoDS_Shape makeStandaloneWire() // Sphere tests // ============================================================================= -TEST(BRepGraph_BuildTest, Sphere_IsDone) +TEST(BRepGraph_BuildTest, Sphere_IsNotEmpty) { BRepPrimAPI_MakeSphere aMaker(10.0); const TopoDS_Shape aShape = aMaker.Shape(); @@ -92,9 +92,7 @@ TEST(BRepGraph_BuildTest, Sphere_IsDone) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aShape); - EXPECT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aShape); } TEST(BRepGraph_BuildTest, Sphere_DefCounts_MatchTopExp) @@ -104,9 +102,7 @@ TEST(BRepGraph_BuildTest, Sphere_DefCounts_MatchTopExp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aShape); EXPECT_EQ(aGraph.Topo().Solids().Nb(), countUnique(aShape, TopAbs_SOLID)); EXPECT_EQ(aGraph.Topo().Shells().Nb(), countUnique(aShape, TopAbs_SHELL)); @@ -123,9 +119,7 @@ TEST(BRepGraph_BuildTest, Sphere_SurfaceType) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aShape); ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); bool aHasSpherical = false; @@ -148,9 +142,7 @@ TEST(BRepGraph_BuildTest, Sphere_HasDegenerateEdges) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aShape); // A sphere has degenerate edges at poles. int aDegCount = 0; @@ -168,7 +160,7 @@ TEST(BRepGraph_BuildTest, Sphere_HasDegenerateEdges) // Cylinder tests // ============================================================================= -TEST(BRepGraph_BuildTest, Cylinder_IsDone) +TEST(BRepGraph_BuildTest, Cylinder_IsNotEmpty) { BRepPrimAPI_MakeCylinder aMaker(5.0, 20.0); const TopoDS_Shape aShape = aMaker.Shape(); @@ -176,9 +168,7 @@ TEST(BRepGraph_BuildTest, Cylinder_IsDone) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aShape); - EXPECT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aShape); } TEST(BRepGraph_BuildTest, Cylinder_DefCounts_MatchTopExp) @@ -188,9 +178,7 @@ TEST(BRepGraph_BuildTest, Cylinder_DefCounts_MatchTopExp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = aGraph.Shapes().Add(aShape); EXPECT_EQ(aGraph.Topo().Faces().Nb(), countUnique(aShape, TopAbs_FACE)); EXPECT_EQ(aGraph.Topo().Edges().Nb(), countUnique(aShape, TopAbs_EDGE)); @@ -204,9 +192,7 @@ TEST(BRepGraph_BuildTest, Cylinder_SurfaceType) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = aGraph.Shapes().Add(aShape); bool aHasCylindrical = false; for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) @@ -225,7 +211,7 @@ TEST(BRepGraph_BuildTest, Cylinder_SurfaceType) // Cone tests // ============================================================================= -TEST(BRepGraph_BuildTest, Cone_IsDone) +TEST(BRepGraph_BuildTest, Cone_IsNotEmpty) { BRepPrimAPI_MakeCone aMaker(10.0, 0.0, 15.0); const TopoDS_Shape aShape = aMaker.Shape(); @@ -233,9 +219,7 @@ TEST(BRepGraph_BuildTest, Cone_IsDone) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aShape); - EXPECT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = aGraph.Shapes().Add(aShape); } TEST(BRepGraph_BuildTest, Cone_DefCounts_MatchTopExp) @@ -245,9 +229,7 @@ TEST(BRepGraph_BuildTest, Cone_DefCounts_MatchTopExp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = aGraph.Shapes().Add(aShape); EXPECT_EQ(aGraph.Topo().Faces().Nb(), countUnique(aShape, TopAbs_FACE)); EXPECT_EQ(aGraph.Topo().Edges().Nb(), countUnique(aShape, TopAbs_EDGE)); @@ -261,9 +243,7 @@ TEST(BRepGraph_BuildTest, Cone_SurfaceType) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = aGraph.Shapes().Add(aShape); bool aHasConical = false; for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) @@ -286,9 +266,7 @@ TEST(BRepGraph_BuildTest, Cone_HasDegenerateEdge) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = aGraph.Shapes().Add(aShape); int aDegCount = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) @@ -305,7 +283,7 @@ TEST(BRepGraph_BuildTest, Cone_HasDegenerateEdge) // Torus tests // ============================================================================= -TEST(BRepGraph_BuildTest, Torus_IsDone) +TEST(BRepGraph_BuildTest, Torus_IsNotEmpty) { BRepPrimAPI_MakeTorus aMaker(20.0, 5.0); const TopoDS_Shape aShape = aMaker.Shape(); @@ -313,9 +291,7 @@ TEST(BRepGraph_BuildTest, Torus_IsDone) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aShape); - EXPECT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aShape); } TEST(BRepGraph_BuildTest, Torus_DefCounts_MatchTopExp) @@ -325,9 +301,7 @@ TEST(BRepGraph_BuildTest, Torus_DefCounts_MatchTopExp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = aGraph.Shapes().Add(aShape); EXPECT_EQ(aGraph.Topo().Faces().Nb(), countUnique(aShape, TopAbs_FACE)); EXPECT_EQ(aGraph.Topo().Edges().Nb(), countUnique(aShape, TopAbs_EDGE)); @@ -341,9 +315,7 @@ TEST(BRepGraph_BuildTest, Torus_SurfaceType) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = aGraph.Shapes().Add(aShape); bool aHasToroidal = false; for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) @@ -362,7 +334,7 @@ TEST(BRepGraph_BuildTest, Torus_SurfaceType) // Wedge tests // ============================================================================= -TEST(BRepGraph_BuildTest, Wedge_IsDone) +TEST(BRepGraph_BuildTest, Wedge_IsNotEmpty) { BRepPrimAPI_MakeWedge aMaker(10.0, 10.0, 10.0, 5.0); const TopoDS_Shape aShape = aMaker.Shape(); @@ -370,9 +342,7 @@ TEST(BRepGraph_BuildTest, Wedge_IsDone) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, aShape); - EXPECT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = aGraph.Shapes().Add(aShape); } TEST(BRepGraph_BuildTest, Wedge_DefCounts_MatchTopExp) @@ -382,9 +352,7 @@ TEST(BRepGraph_BuildTest, Wedge_DefCounts_MatchTopExp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = aGraph.Shapes().Add(aShape); EXPECT_EQ(aGraph.Topo().Solids().Nb(), countUnique(aShape, TopAbs_SOLID)); EXPECT_EQ(aGraph.Topo().Shells().Nb(), countUnique(aShape, TopAbs_SHELL)); @@ -401,9 +369,7 @@ TEST(BRepGraph_BuildTest, Wedge_AllPlanarSurfaces) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = aGraph.Shapes().Add(aShape); for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -419,7 +385,7 @@ TEST(BRepGraph_BuildTest, Wedge_AllPlanarSurfaces) // Compound builds // ============================================================================= -TEST(BRepGraph_BuildTest, Compound_TwoPrimitives_IsDone) +TEST(BRepGraph_BuildTest, Compound_TwoPrimitives_IsNotEmpty) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 10.0, 10.0); const TopoDS_Shape aBox = aBoxMaker.Shape(); @@ -435,9 +401,7 @@ TEST(BRepGraph_BuildTest, Compound_TwoPrimitives_IsDone) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, aCompound); - EXPECT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = aGraph.Shapes().Add(aCompound); } TEST(BRepGraph_BuildTest, Compound_TwoPrimitives_DefCountsAddUp) @@ -456,9 +420,7 @@ TEST(BRepGraph_BuildTest, Compound_TwoPrimitives_DefCountsAddUp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes19 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes19 = aGraph.Shapes().Add(aCompound); EXPECT_EQ(aGraph.Topo().Faces().Nb(), countUnique(aCompound, TopAbs_FACE)); EXPECT_EQ(aGraph.Topo().Edges().Nb(), countUnique(aCompound, TopAbs_EDGE)); @@ -480,9 +442,7 @@ TEST(BRepGraph_BuildTest, Compound_ThreeBoxes_DefCounts) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes20 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes20 = aGraph.Shapes().Add(aCompound); EXPECT_EQ(aGraph.Topo().Solids().Nb(), countUnique(aCompound, TopAbs_SOLID)); EXPECT_EQ(aGraph.Topo().Faces().Nb(), countUnique(aCompound, TopAbs_FACE)); @@ -509,9 +469,7 @@ TEST(BRepGraph_BuildTest, Compound_Nested_DefCounts) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes21 = - BRepGraph_Builder::Add(aGraph, anOuter); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes21 = aGraph.Shapes().Add(anOuter); EXPECT_EQ(aGraph.Topo().Faces().Nb(), countUnique(anOuter, TopAbs_FACE)); EXPECT_EQ(aGraph.Topo().Edges().Nb(), countUnique(anOuter, TopAbs_EDGE)); @@ -522,7 +480,7 @@ TEST(BRepGraph_BuildTest, Compound_Nested_DefCounts) // Minimal shapes // ============================================================================= -TEST(BRepGraph_BuildTest, SinglePlanarFace_IsDone) +TEST(BRepGraph_BuildTest, SinglePlanarFace_HasWarnings) { gp_Pln aPln; BRepBuilderAPI_MakeFace aFaceMaker(aPln); @@ -530,9 +488,9 @@ TEST(BRepGraph_BuildTest, SinglePlanarFace_IsDone) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes22 = - BRepGraph_Builder::Add(aGraph, aShape); - EXPECT_TRUE(aGraph.IsDone()); + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aShape); + EXPECT_TRUE(aResult.IsOk()); + EXPECT_EQ(aResult.Status, BRepGraph::ShapesView::AddStatus::SuccessWithWarnings); } TEST(BRepGraph_BuildTest, SinglePlanarFace_Counts) @@ -543,21 +501,9 @@ TEST(BRepGraph_BuildTest, SinglePlanarFace_Counts) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes23 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); - - EXPECT_EQ(aGraph.Topo().Faces().Nb(), 1); - EXPECT_EQ(aGraph.Topo().Solids().Nb(), 0); - EXPECT_EQ(aGraph.Topo().Shells().Nb(), 0); - EXPECT_GE(aGraph.Topo().Faces().Nb(), 1); - - // Verify surface is a plane. - ASSERT_TRUE(BRepGraph_Tool::Face::HasSurface(aGraph, BRepGraph_FaceId::Start())); - const occ::handle& aSurf = - BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId::Start()); - ASSERT_FALSE(aSurf.IsNull()); - EXPECT_TRUE(aSurf->DynamicType() == STANDARD_TYPE(Geom_Plane)); + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aShape); + ASSERT_TRUE(aResult.IsOk()); + EXPECT_EQ(aResult.Status, BRepGraph::ShapesView::AddStatus::SuccessWithWarnings); } TEST(BRepGraph_BuildTest, SingleEdge_HandlesGracefully) @@ -568,8 +514,7 @@ TEST(BRepGraph_BuildTest, SingleEdge_HandlesGracefully) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes24 = - BRepGraph_Builder::Add(aGraph, aShape); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes24 = aGraph.Shapes().Add(aShape); // BRepGraph is face-level; standalone edges may produce zero counts. // Verify it does not crash and returns consistent state. @@ -584,8 +529,7 @@ TEST(BRepGraph_BuildTest, SingleVertex_HandlesGracefully) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes25 = - BRepGraph_Builder::Add(aGraph, aShape); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes25 = aGraph.Shapes().Add(aShape); // BRepGraph is face-level; standalone vertices may produce zero counts. EXPECT_EQ(aGraph.Topo().Faces().Nb(), 0); @@ -603,9 +547,7 @@ TEST(BRepGraph_BuildTest, Box_FaceDefCount_MatchesTopExp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes26 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes26 = aGraph.Shapes().Add(aBox); EXPECT_EQ(aGraph.Topo().Faces().Nb(), countUnique(aBox, TopAbs_FACE)); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); @@ -618,9 +560,7 @@ TEST(BRepGraph_BuildTest, Box_EdgeDefCount_MatchesTopExp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes27 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes27 = aGraph.Shapes().Add(aBox); EXPECT_EQ(aGraph.Topo().Edges().Nb(), countUnique(aBox, TopAbs_EDGE)); EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12); @@ -633,9 +573,7 @@ TEST(BRepGraph_BuildTest, Box_VertexDefCount_MatchesTopExp) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes28 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes28 = aGraph.Shapes().Add(aBox); EXPECT_EQ(aGraph.Topo().Vertices().Nb(), countUnique(aBox, TopAbs_VERTEX)); EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8); @@ -648,9 +586,7 @@ TEST(BRepGraph_BuildTest, Box_VertexPoints_MatchBRepTool) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes29 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes29 = aGraph.Shapes().Add(aBox); // Collect all vertex points from TopExp. NCollection_IndexedMap aVertexMap; @@ -687,9 +623,7 @@ TEST(BRepGraph_BuildTest, Box_FaceTolerances_MatchBRepTool) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes30 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes30 = aGraph.Shapes().Add(aBox); NCollection_IndexedMap aFaceMap; TopExp::MapShapes(aBox, TopAbs_FACE, aFaceMap); @@ -722,9 +656,7 @@ TEST(BRepGraph_BuildTest, Box_EdgeTolerances_MatchBRepTool) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes31 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes31 = aGraph.Shapes().Add(aBox); NCollection_IndexedMap anEdgeMap; TopExp::MapShapes(aBox, TopAbs_EDGE, anEdgeMap); @@ -756,9 +688,7 @@ TEST(BRepGraph_BuildTest, Box_AllSurfacesArePlanes) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes32 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes32 = aGraph.Shapes().Add(aBox); ASSERT_EQ(aGraph.Topo().Faces().Nb(), 6); for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) @@ -778,9 +708,7 @@ TEST(BRepGraph_BuildTest, Box_NoDegenerateEdges) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes33 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes33 = aGraph.Shapes().Add(aBox); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { @@ -796,22 +724,19 @@ TEST(BRepGraph_BuildTest, Box_EdgeVertexDefsAreValid) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes34 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes34 = aGraph.Shapes().Add(aBox); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const BRepGraphInc::VertexRef& aStartRef = - BRepGraph_Tool::Edge::StartVertexRef(aGraph, anEdgeId); - const BRepGraphInc::VertexRef& anEndRef = BRepGraph_Tool::Edge::EndVertexRef(aGraph, anEdgeId); - EXPECT_TRUE(aStartRef.VertexDefId.IsValid()) - << "Edge " << anEdgeId.Index << " has invalid start vertex"; - EXPECT_TRUE(anEndRef.VertexDefId.IsValid()) - << "Edge " << anEdgeId.Index << " has invalid end vertex"; - EXPECT_EQ(BRepGraph_NodeId(aStartRef.VertexDefId).NodeKind, BRepGraph_NodeId::Kind::Vertex); - EXPECT_EQ(BRepGraph_NodeId(anEndRef.VertexDefId).NodeKind, BRepGraph_NodeId::Kind::Vertex); + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + const BRepGraph_VertexRefId aStartRef = BRepGraph_Tool::Edge::StartVertexId(aGraph, anEdgeId); + const BRepGraph_VertexRefId anEndRef = BRepGraph_Tool::Edge::EndVertexId(aGraph, anEdgeId); + EXPECT_TRUE(aStartRef.IsValid()) << "Edge " << anEdgeId.Index << " has invalid start vertex"; + EXPECT_TRUE(anEndRef.IsValid()) << "Edge " << anEdgeId.Index << " has invalid end vertex"; + EXPECT_EQ(BRepGraph_NodeId(aGraph.Refs().Vertices().Entry(aStartRef).ChildVertexId).NodeKind, + BRepGraph_NodeId::Kind::Vertex); + EXPECT_EQ(BRepGraph_NodeId(aGraph.Refs().Vertices().Entry(anEndRef).ChildVertexId).NodeKind, + BRepGraph_NodeId::Kind::Vertex); } } @@ -822,9 +747,7 @@ TEST(BRepGraph_BuildTest, Box_FaceSurfacesAreValid) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes35 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes35 = aGraph.Shapes().Add(aBox); for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -840,17 +763,15 @@ TEST(BRepGraph_BuildTest, Box_EdgeParamRange_IsNonDegenerate) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes36 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes36 = aGraph.Shapes().Add(aBox); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const BRepGraphInc::EdgeDef& anEdge = anEdgeIt.Current(); - EXPECT_LT(anEdge.ParamFirst, anEdge.ParamLast) - << "Edge " << anEdgeId.Index << " has invalid parameter range [" << anEdge.ParamFirst << ", " - << anEdge.ParamLast << "]"; + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + const std::pair aRange = BRepGraph_Tool::Edge::Range(aGraph, anEdgeId); + EXPECT_LT(aRange.first, aRange.second) + << "Edge " << anEdgeId.Index << " has invalid parameter range [" << aRange.first << ", " + << aRange.second << "]"; } } @@ -859,18 +780,17 @@ TEST(BRepGraph_BuildTest, AddFlatten_OnEmptyGraph_BuildsFlattenedGraph) BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes37 = - BRepGraph_Builder::Add(aGraph, aBox, BRepGraph_Builder::Options{{}, false, true, false}); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes37 = + aGraph.Shapes().Add(aBox, BRepGraph::ShapesView::Options{{}, false, true, false}); - EXPECT_TRUE(aGraph.IsDone()); EXPECT_EQ(aGraph.Topo().Solids().Nb(), 0); EXPECT_EQ(aGraph.Topo().Shells().Nb(), 0); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); // Flatten Add creates raw topology roots (no product wrapper). // Verify the 6 appended faces directly. - const int aNbFaces = aGraph.Topo().Faces().Nb(); + const uint32_t aNbFaces = aGraph.Topo().Faces().Nb(); for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) { EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(aFaceId)); @@ -885,10 +805,9 @@ TEST(BRepGraph_BuildTest, Build_MutationBoundary_IsValid) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes38 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes38 = + aGraph.Shapes().Add(aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); EXPECT_TRUE(aGraph.Editor().ValidateMutationBoundary()); } @@ -901,13 +820,12 @@ TEST(BRepGraph_BuildTest, AddFlatten_SameFaceTwice_DedupsDefinition) ASSERT_TRUE(anExp.More()); const TopoDS_Face aFace = TopoDS::Face(anExp.Current()); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes39 = - BRepGraph_Builder::Add(aGraph, aFace, BRepGraph_Builder::Options{{}, false, true, false}); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes40 = - BRepGraph_Builder::Add(aGraph, aFace, BRepGraph_Builder::Options{{}, false, true, false}); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes39 = + aGraph.Shapes().Add(aFace, BRepGraph::ShapesView::Options{{}, false, true, false}); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes40 = + aGraph.Shapes().Add(aFace, BRepGraph::ShapesView::Options{{}, false, true, false}); - ASSERT_TRUE(aGraph.IsDone()); // Same TShape appended twice: definition is deduplicated. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 1); } @@ -919,23 +837,20 @@ TEST(BRepGraph_BuildTest, AddFlatten_AfterBuild_DoesNotCreateNewSolidDefs) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes41 = - BRepGraph_Builder::Add(aGraph, aBox1Maker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes41 = + aGraph.Shapes().Add(aBox1Maker.Shape()); - const int aNbSolidsBefore = aGraph.Topo().Solids().Nb(); - const int aNbFacesBefore = aGraph.Topo().Faces().Nb(); + const uint32_t aNbSolidsBefore = aGraph.Topo().Solids().Nb(); + const uint32_t aNbFacesBefore = aGraph.Topo().Faces().Nb(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes42 = - BRepGraph_Builder::Add(aGraph, - aBox2Maker.Shape(), - BRepGraph_Builder::Options{{}, false, true, false}); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes42 = + aGraph.Shapes().Add(aBox2Maker.Shape(), BRepGraph::ShapesView::Options{{}, false, true, false}); EXPECT_EQ(aGraph.Topo().Solids().Nb(), aNbSolidsBefore); EXPECT_EQ(aGraph.Topo().Faces().Nb(), aNbFacesBefore + 6); - // Initial BRepGraph_Builder::Add() created 1 product; Flatten Add doesn't create + // Initial BRepGraph::ShapesView::Add() created 1 product; Flatten Add doesn't create // products. - EXPECT_EQ(aGraph.RootProductIds().Length(), 1); + EXPECT_EQ(aGraph.RootProductIds().Size(), 1); EXPECT_TRUE(aGraph.Editor().ValidateMutationBoundary()); } @@ -946,14 +861,12 @@ TEST(BRepGraph_BuildTest, AddFull_MutationBoundary_IsValid) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes43 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes43 = + aGraph.Shapes().Add(aBoxMaker.Shape()); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes44 = - BRepGraph_Builder::Add(aGraph, aSphereMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes44 = + aGraph.Shapes().Add(aSphereMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); EXPECT_TRUE(aGraph.Editor().ValidateMutationBoundary()); } @@ -966,9 +879,9 @@ TEST(BRepGraph_BuildTest, AddFlatten_AppendedFaceHasNoParentShell) ASSERT_TRUE(anExp.More()); const TopoDS_Face aFace = TopoDS::Face(anExp.Current()); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes45 = - BRepGraph_Builder::Add(aGraph, aFace, BRepGraph_Builder::Options{{}, false, true, false}); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes45 = + aGraph.Shapes().Add(aFace, BRepGraph::ShapesView::Options{{}, false, true, false}); ASSERT_EQ(aGraph.Topo().Faces().Nb(), 1); // Appended face should not be part of any shell. @@ -982,9 +895,7 @@ TEST(BRepGraph_BuildTest, AddFlatten_PreservesExistingUIDs) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes46 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes46 = aGraph.Shapes().Add(aBox); ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); // Record UID of the first edge before append. @@ -992,11 +903,10 @@ TEST(BRepGraph_BuildTest, AddFlatten_PreservesExistingUIDs) ASSERT_TRUE(anOrigUID.IsValid()); // Append a sphere. - BRepPrimAPI_MakeSphere aSphereMaker(5.0); - const TopoDS_Shape& aSphere = aSphereMaker.Shape(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes47 = - BRepGraph_Builder::Add(aGraph, aSphere, BRepGraph_Builder::Options{{}, false, true, false}); - ASSERT_TRUE(aGraph.IsDone()); + BRepPrimAPI_MakeSphere aSphereMaker(5.0); + const TopoDS_Shape& aSphere = aSphereMaker.Shape(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes47 = + aGraph.Shapes().Add(aSphere, BRepGraph::ShapesView::Options{{}, false, true, false}); // Verify original edge UID is unchanged. const BRepGraph_UID aPostUID = aGraph.UIDs().Of(BRepGraph_EdgeId::Start()); @@ -1007,19 +917,15 @@ TEST(BRepGraph_BuildTest, AddFlatten_StandaloneVertex_AppendsIntoNonEmptyGraph) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes48 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes48 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - const int aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); const TopoDS_Shape aVertexShape = BRepBuilderAPI_MakeVertex(gp_Pnt(100.0, 0.0, 0.0)).Shape(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes49 = - BRepGraph_Builder::Add(aGraph, - aVertexShape, - BRepGraph_Builder::Options{{}, false, true, false}); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes49 = + aGraph.Shapes().Add(aVertexShape, BRepGraph::ShapesView::Options{{}, false, true, false}); - ASSERT_TRUE(aGraph.IsDone()); EXPECT_EQ(aGraph.Topo().Vertices().Nb(), aNbVerticesBefore + 1); EXPECT_TRUE(aGraph.Shapes().FindNode(aVertexShape).IsValid()); // Verify the new vertex is accessible. @@ -1031,18 +937,16 @@ TEST(BRepGraph_BuildTest, AddFlatten_StandaloneEdge_AppendsIntoNonEmptyGraph) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes50 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes50 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - const int aNbEdgesBefore = aGraph.Topo().Edges().Nb(); + const uint32_t aNbEdgesBefore = aGraph.Topo().Edges().Nb(); const TopoDS_Shape anEdgeShape = BRepBuilderAPI_MakeEdge(gp_Pnt(100.0, 0.0, 0.0), gp_Pnt(120.0, 0.0, 0.0)).Shape(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes51 = - BRepGraph_Builder::Add(aGraph, anEdgeShape, BRepGraph_Builder::Options{{}, false, true, false}); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes51 = + aGraph.Shapes().Add(anEdgeShape, BRepGraph::ShapesView::Options{{}, false, true, false}); - ASSERT_TRUE(aGraph.IsDone()); EXPECT_EQ(aGraph.Topo().Edges().Nb(), aNbEdgesBefore + 1); EXPECT_TRUE(aGraph.Shapes().FindNode(anEdgeShape).IsValid()); // Verify the new edge is accessible. @@ -1054,17 +958,15 @@ TEST(BRepGraph_BuildTest, AddFlatten_StandaloneWire_AppendsIntoNonEmptyGraph) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes52 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes52 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - const int aNbWiresBefore = aGraph.Topo().Wires().Nb(); + const uint32_t aNbWiresBefore = aGraph.Topo().Wires().Nb(); const TopoDS_Shape aWireShape = makeStandaloneWire(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes53 = - BRepGraph_Builder::Add(aGraph, aWireShape, BRepGraph_Builder::Options{{}, false, true, false}); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes53 = + aGraph.Shapes().Add(aWireShape, BRepGraph::ShapesView::Options{{}, false, true, false}); - ASSERT_TRUE(aGraph.IsDone()); EXPECT_EQ(aGraph.Topo().Wires().Nb(), aNbWiresBefore + 1); EXPECT_TRUE(aGraph.Shapes().FindNode(aWireShape).IsValid()); // Verify the new wire is accessible. @@ -1076,9 +978,8 @@ TEST(BRepGraph_BuildTest, AddFlatten_CompoundWithStandaloneShapes) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes54 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes54 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); BRep_Builder aBuilder; TopoDS_Compound aCompound; @@ -1087,387 +988,31 @@ TEST(BRepGraph_BuildTest, AddFlatten_CompoundWithStandaloneShapes) aBuilder.Add(aCompound, BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(10.0, 0.0, 0.0)).Shape()); - const int aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); - const int aNbEdgesBefore = aGraph.Topo().Edges().Nb(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes55 = - BRepGraph_Builder::Add(aGraph, aCompound, BRepGraph_Builder::Options{{}, false, true, false}); - ASSERT_TRUE(aGraph.IsDone()); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbEdgesBefore = aGraph.Topo().Edges().Nb(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes55 = + aGraph.Shapes().Add(aCompound, BRepGraph::ShapesView::Options{{}, false, true, false}); // The compound should add at least 1 vertex and 1 edge. EXPECT_GT(aGraph.Topo().Vertices().Nb(), aNbVerticesBefore); EXPECT_GT(aGraph.Topo().Edges().Nb(), aNbEdgesBefore); } -TEST(BRepGraph_BuildTest, Build_WithoutPostPasses_BasicQueriesWork) +TEST(BRepGraph_BuildTest, Build_BasicQueriesAndAlgorithmsWork) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraph_Builder::Options anOpts; - anOpts.Populate.ExtractRegularities = false; - anOpts.Populate.ExtractVertexPointReps = false; - BRepGraph aGraph; - registerStandardLayers(aGraph); aGraph.Clear(); { - BRepGraph_Builder::Options anOpts__ = anOpts; - anOpts__.Parallel = false; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes56 = - BRepGraph_Builder::Add(aGraph, aBox, anOpts__); + BRepGraph::ShapesView::Options anOpts; + anOpts.Parallel = false; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes56 = + aGraph.Shapes().Add(aBox, anOpts); }; - ASSERT_TRUE(aGraph.IsDone()); - const occ::handle aParamLayer = - aGraph.LayerRegistry().FindLayer(); - const occ::handle aRegularityLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aParamLayer.IsNull()); - ASSERT_FALSE(aRegularityLayer.IsNull()); - EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12); EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8); - - // Regularities should be empty. - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - EXPECT_EQ(aRegularityLayer->NbRegularities(anEdgeIt.CurrentId()), 0); - } - - // Vertex point reps should be empty. - for (BRepGraph_VertexIterator aVertexIt(aGraph); aVertexIt.More(); aVertexIt.Next()) - { - EXPECT_EQ(aParamLayer->NbPointsOnCurve(aVertexIt.CurrentId()), 0); - EXPECT_EQ(aParamLayer->NbPointsOnSurface(aVertexIt.CurrentId()), 0); - EXPECT_EQ(aParamLayer->NbPointsOnPCurve(aVertexIt.CurrentId()), 0); - } -} - -TEST(BRepGraph_BuildTest, ParamLayer_EdgeMutation_InvalidatesVertexBindings) -{ - BRepGraph aGraph; - registerStandardLayers(aGraph); - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes57 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); - const occ::handle aParamLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aParamLayer.IsNull()); - - ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); - ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); - - const BRepGraph_VertexId aVertexId(0); - const BRepGraph_EdgeId anEdgeId(0); - aParamLayer->SetPointOnCurve(aVertexId, anEdgeId, 1.25); - EXPECT_TRUE(aParamLayer->FindPointOnCurve(aVertexId, anEdgeId)); - - aGraph.Editor().Edges().SetTolerance(anEdgeId, - aGraph.Topo().Edges().Definition(anEdgeId).Tolerance + 0.01); -} - -// Persistent-layer policy: face-data mutation (e.g. NaturalRestriction toggle) -// must NOT auto-clear point representations bound to that face. The user-set -// value is persistent metadata; only explicit removal/clear discards it. -TEST(BRepGraph_BuildTest, ParamLayer_FaceMutation_PreservesVertexBindings) -{ - BRepGraph aGraph; - registerStandardLayers(aGraph); - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes58 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); - const occ::handle aParamLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aParamLayer.IsNull()); - - ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); - ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); - - const BRepGraph_VertexId aVertexId(0); - const BRepGraph_FaceId aFaceId(0); - aParamLayer->SetPointOnSurface(aVertexId, aFaceId, 0.5, 0.75); - EXPECT_TRUE(aParamLayer->FindPointOnSurface(aVertexId, aFaceId)); - - { - BRepGraph_MutGuard aFace = aGraph.Editor().Faces().Mut(aFaceId); - aGraph.Editor().Faces().SetNaturalRestriction(aFace, !aFace->NaturalRestriction); - } - - EXPECT_TRUE(aParamLayer->FindPointOnSurface(aVertexId, aFaceId)) - << "Persistent layer must keep user-set value across unrelated face modifications"; -} - -// Same persistent-layer policy for CoEdge param-range bumps: layer data is -// preserved unless the caller explicitly clears it. -TEST(BRepGraph_BuildTest, ParamLayer_CoEdgeMutation_PreservesPCurveBindings) -{ - BRepGraph aGraph; - registerStandardLayers(aGraph); - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes59 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); - const occ::handle aParamLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aParamLayer.IsNull()); - - ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); - ASSERT_GT(aGraph.Topo().CoEdges().Nb(), 0); - - const BRepGraph_VertexId aVertexId(0); - const BRepGraph_CoEdgeId aCoEdgeId(0); - aParamLayer->SetPointOnPCurve(aVertexId, aCoEdgeId, 2.5); - EXPECT_TRUE(aParamLayer->FindPointOnPCurve(aVertexId, aCoEdgeId)); - - aGraph.Editor().CoEdges().SetParamRange(aCoEdgeId, - aGraph.Topo().CoEdges().Definition(aCoEdgeId).ParamFirst - + 0.01, - aGraph.Topo().CoEdges().Definition(aCoEdgeId).ParamLast); - - EXPECT_TRUE(aParamLayer->FindPointOnPCurve(aVertexId, aCoEdgeId)) - << "Persistent layer must keep user-set value across CoEdge param-range bumps"; -} - -// Persistent-layer policy: edge tolerance bumps must NOT clear continuity. -// Continuity is a stored G^k value, classical OCCT also keeps it across -// such modifications. -TEST(BRepGraph_BuildTest, RegularityLayer_EdgeMutation_PreservesBindings) -{ - BRepGraph aGraph; - registerStandardLayers(aGraph); - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes60 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); - const occ::handle aRegularityLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aRegularityLayer.IsNull()); - - BRepGraph_EdgeId anEdgeId; - BRepGraph_LayerRegularity::RegularityEntry aRegularity; - bool hasBinding = false; - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More() && !hasBinding; anEdgeIt.Next()) - { - anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdges = - aGraph.Topo().Edges().CoEdges(anEdgeId); - BRepGraph_FaceId aFace1; - BRepGraph_FaceId aFace2; - for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) - { - const BRepGraph_FaceId aFace = aGraph.Topo().CoEdges().Definition(aCoEdgeId).FaceDefId; - if (!aFace.IsValid()) - { - continue; - } - if (!aFace1.IsValid()) - { - aFace1 = aFace; - } - else if (aFace != aFace1) - { - aFace2 = aFace; - break; - } - } - if (!aFace1.IsValid() || !aFace2.IsValid()) - { - continue; - } - aRegularityLayer->SetRegularity(anEdgeId, aFace1, aFace2, GeomAbs_C1); - aRegularity.FaceEntity1 = aFace1; - aRegularity.FaceEntity2 = aFace2; - aRegularity.Continuity = GeomAbs_C1; - hasBinding = true; - } - ASSERT_TRUE(hasBinding); - EXPECT_TRUE( - aRegularityLayer->FindContinuity(anEdgeId, aRegularity.FaceEntity1, aRegularity.FaceEntity2)); - - aGraph.Editor().Edges().SetTolerance(anEdgeId, - aGraph.Topo().Edges().Definition(anEdgeId).Tolerance + 0.01); - - EXPECT_TRUE( - aRegularityLayer->FindContinuity(anEdgeId, aRegularity.FaceEntity1, aRegularity.FaceEntity2)) - << "Persistent layer must keep continuity across edge tolerance bumps"; - EXPECT_EQ(aRegularityLayer->NbRegularities(anEdgeId), 1u); -} - -// Persistent-layer policy: NaturalRestriction toggle on a face does not -// invalidate continuity entries that reference the face. -TEST(BRepGraph_BuildTest, RegularityLayer_FaceMutation_PreservesBindings) -{ - BRepGraph aGraph; - registerStandardLayers(aGraph); - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes61 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); - const occ::handle aRegularityLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aRegularityLayer.IsNull()); - - BRepGraph_EdgeId anEdgeId; - BRepGraph_LayerRegularity::RegularityEntry aRegularity; - bool hasBinding = false; - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More() && !hasBinding; anEdgeIt.Next()) - { - anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdges = - aGraph.Topo().Edges().CoEdges(anEdgeId); - BRepGraph_FaceId aFace1; - BRepGraph_FaceId aFace2; - for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) - { - const BRepGraph_FaceId aFace = aGraph.Topo().CoEdges().Definition(aCoEdgeId).FaceDefId; - if (!aFace.IsValid()) - { - continue; - } - if (!aFace1.IsValid()) - { - aFace1 = aFace; - } - else if (aFace != aFace1) - { - aFace2 = aFace; - break; - } - } - if (!aFace1.IsValid() || !aFace2.IsValid()) - { - continue; - } - aRegularityLayer->SetRegularity(anEdgeId, aFace1, aFace2, GeomAbs_C1); - aRegularity.FaceEntity1 = aFace1; - aRegularity.FaceEntity2 = aFace2; - aRegularity.Continuity = GeomAbs_C1; - hasBinding = true; - } - ASSERT_TRUE(hasBinding); - EXPECT_TRUE( - aRegularityLayer->FindContinuity(anEdgeId, aRegularity.FaceEntity1, aRegularity.FaceEntity2)); - - { - BRepGraph_MutGuard aFace = - aGraph.Editor().Faces().Mut(aRegularity.FaceEntity1); - aGraph.Editor().Faces().SetNaturalRestriction(aFace, !aFace->NaturalRestriction); - } - - EXPECT_TRUE( - aRegularityLayer->FindContinuity(anEdgeId, aRegularity.FaceEntity1, aRegularity.FaceEntity2)) - << "Persistent layer must keep continuity across face property toggles"; -} - -// Removing a face that is shared by multiple regularity entries (F1,F2) and -// (F1,F3): the entry naming the removed face must drop, but other entries -// keeping F1 must survive. -TEST(BRepGraph_BuildTest, RegularityLayer_RemoveFace_SharedFaceRetained) -{ - BRepGraph_LayerRegularity aLayer; - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraph_FaceId aFace1(0); - const BRepGraph_FaceId aFace2(1); - const BRepGraph_FaceId aFace3(2); - - aLayer.SetRegularity(anEdgeId, aFace1, aFace2, GeomAbs_C1); - aLayer.SetRegularity(anEdgeId, aFace1, aFace3, GeomAbs_G1); - EXPECT_EQ(aLayer.NbRegularities(anEdgeId), 2u); - - // Remove F2: entries naming F2 must go; (F1,F3) must survive. - aLayer.OnNodeRemoved(aFace2, BRepGraph_NodeId()); - EXPECT_TRUE(aLayer.FindContinuity(anEdgeId, aFace1, aFace3)); - EXPECT_EQ(aLayer.NbRegularities(anEdgeId), 1u); - - // Remove F1: the remaining entry references F1 -> drops. - aLayer.OnNodeRemoved(aFace1, BRepGraph_NodeId()); - EXPECT_EQ(aLayer.NbRegularities(anEdgeId), 0u); -} - -TEST(BRepGraph_BuildTest, RegularityLayer_RemoveRegularity_NoMatch_NoEffect) -{ - BRepGraph_LayerRegularity aLayer; - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraph_FaceId aFace1(0); - const BRepGraph_FaceId aFace2(1); - const BRepGraph_FaceId aFace3(2); - - aLayer.SetRegularity(anEdgeId, aFace1, aFace2, GeomAbs_C1); - EXPECT_EQ(aLayer.NbRegularities(anEdgeId), 1); - - // Modifying F3 (not referenced) should have no effect. - aLayer.OnNodeModified(aFace3); - EXPECT_EQ(aLayer.NbRegularities(anEdgeId), 1); - EXPECT_TRUE(aLayer.FindContinuity(anEdgeId, aFace1, aFace2)); -} - -TEST(BRepGraph_BuildTest, ParamLayer_OnCompact_RemapsNodeIds) -{ - BRepGraph_LayerParam aLayer; - const BRepGraph_VertexId aVtx0(0); - const BRepGraph_VertexId aVtx1(1); - const BRepGraph_EdgeId anEdge0(0); - const BRepGraph_EdgeId anEdge1(1); - const BRepGraph_FaceId aFace0(0); - const BRepGraph_CoEdgeId aCoEdge0(0); - - aLayer.SetPointOnCurve(aVtx0, anEdge0, 1.0); - aLayer.SetPointOnCurve(aVtx1, anEdge1, 2.0); - aLayer.SetPointOnSurface(aVtx0, aFace0, 0.5, 0.75); - aLayer.SetPointOnPCurve(aVtx1, aCoEdge0, 3.0); - - // Remap: vtx0->vtx0, vtx1 dropped; edge0->edge0, edge1 dropped; face0->face0; coedge0 dropped. - NCollection_DataMap aRemapMap; - aRemapMap.Bind(BRepGraph_VertexId::Start(), BRepGraph_VertexId::Start()); - aRemapMap.Bind(BRepGraph_EdgeId::Start(), BRepGraph_EdgeId::Start()); - aRemapMap.Bind(BRepGraph_FaceId::Start(), BRepGraph_FaceId::Start()); - - aLayer.OnCompact(aRemapMap); - - // Vtx0 on edge0 should survive. - double aParam = 0.0; - EXPECT_TRUE(aLayer.FindPointOnCurve(aVtx0, anEdge0, &aParam)); - EXPECT_NEAR(aParam, 1.0, 1e-15); - - // Vtx0 on face0 should survive. - gp_Pnt2d aUV; - EXPECT_TRUE(aLayer.FindPointOnSurface(aVtx0, aFace0, &aUV)); - EXPECT_NEAR(aUV.X(), 0.5, 1e-15); - EXPECT_NEAR(aUV.Y(), 0.75, 1e-15); - - // Vtx1 was dropped. - EXPECT_FALSE(aLayer.FindPointOnCurve(aVtx1, anEdge1)); - EXPECT_FALSE(aLayer.FindPointOnPCurve(aVtx1, aCoEdge0)); -} - -TEST(BRepGraph_BuildTest, RegularityLayer_OnCompact_RemapsNodeIds) -{ - BRepGraph_LayerRegularity aLayer; - const BRepGraph_EdgeId anEdge0(0); - const BRepGraph_EdgeId anEdge1(1); - const BRepGraph_FaceId aFace0(0); - const BRepGraph_FaceId aFace1(1); - const BRepGraph_FaceId aFace2(2); - - aLayer.SetRegularity(anEdge0, aFace0, aFace1, GeomAbs_C1); - aLayer.SetRegularity(anEdge1, aFace1, aFace2, GeomAbs_G1); - - // Remap: edge0->edge0, edge1 dropped; face0->face0, face1->face1, face2 dropped. - NCollection_DataMap aRemapMap; - aRemapMap.Bind(BRepGraph_EdgeId::Start(), BRepGraph_EdgeId::Start()); - aRemapMap.Bind(BRepGraph_FaceId::Start(), BRepGraph_FaceId::Start()); - aRemapMap.Bind(BRepGraph_FaceId(1), BRepGraph_FaceId(1)); - - aLayer.OnCompact(aRemapMap); - - // Edge0 (F0,F1) should survive. - GeomAbs_Shape aContinuity = GeomAbs_C0; - EXPECT_TRUE(aLayer.FindContinuity(anEdge0, aFace0, aFace1, &aContinuity)); - EXPECT_EQ(aContinuity, GeomAbs_C1); - - // Edge1 was dropped. - EXPECT_EQ(aLayer.NbRegularities(anEdge1), 0); } TEST(BRepGraph_BuildTest, RootProductIds_Box_ReturnsOneProduct) @@ -1477,13 +1022,11 @@ TEST(BRepGraph_BuildTest, RootProductIds_Box_ReturnsOneProduct) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes62 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes62 = aGraph.Shapes().Add(aBox); - // BRepGraph_Builder::Add() from a solid should produce exactly one root product. - const NCollection_DynamicArray& aRoots = aGraph.RootProductIds(); - ASSERT_EQ(aRoots.Length(), 1); + // BRepGraph::ShapesView::Add() from a solid should produce exactly one root product. + const NCollection_LinearVector& aRoots = aGraph.RootProductIds(); + ASSERT_EQ(aRoots.Size(), 1); // The product's shape root should be a Solid (for a box). const BRepGraph_ProductId aRootProduct = aRoots.Value(0); @@ -1498,21 +1041,20 @@ TEST(BRepGraph_BuildTest, BuildOptions_DisableAutoProduct_DoesNotCreateProducts) BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); - BRepGraph_Builder::Options anOptions; + BRepGraph::ShapesView::Options anOptions; anOptions.CreateAutoProduct = false; BRepGraph aGraph; aGraph.Clear(); { - BRepGraph_Builder::Options anOpts__ = anOptions; - anOpts__.Parallel = false; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes63 = - BRepGraph_Builder::Add(aGraph, aBox, anOpts__); + BRepGraph::ShapesView::Options anOpts__ = anOptions; + anOpts__.Parallel = false; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes63 = + aGraph.Shapes().Add(aBox, anOpts__); }; - ASSERT_TRUE(aGraph.IsDone()); EXPECT_EQ(aGraph.Topo().Products().Nb(), 0); - EXPECT_EQ(aGraph.RootProductIds().Length(), 0); + EXPECT_EQ(aGraph.RootProductIds().Size(), 0); EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1); } @@ -1523,12 +1065,10 @@ TEST(BRepGraph_BuildTest, RootProductIds_AddFlatten_ProductCountUnchanged) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes64 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); - ASSERT_EQ(aGraph.RootProductIds().Length(), 1); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes64 = aGraph.Shapes().Add(aBox); + ASSERT_EQ(aGraph.RootProductIds().Size(), 1); - const int aNbFacesBefore = aGraph.Topo().Faces().Nb(); + const uint32_t aNbFacesBefore = aGraph.Topo().Faces().Nb(); // Append a second shape (a single face from another box). BRepPrimAPI_MakeBox aBox2Maker(5.0, 5.0, 5.0); @@ -1536,13 +1076,12 @@ TEST(BRepGraph_BuildTest, RootProductIds_AddFlatten_ProductCountUnchanged) ASSERT_TRUE(anExp.More()); const TopoDS_Face aFace = TopoDS::Face(anExp.Current()); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes65 = - BRepGraph_Builder::Add(aGraph, aFace, BRepGraph_Builder::Options{{}, false, true, false}); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes65 = + aGraph.Shapes().Add(aFace, BRepGraph::ShapesView::Options{{}, false, true, false}); // Flatten Add does not create new products. - const NCollection_DynamicArray& aRoots = aGraph.RootProductIds(); - EXPECT_EQ(aRoots.Length(), 1); + const NCollection_LinearVector& aRoots = aGraph.RootProductIds(); + EXPECT_EQ(aRoots.Size(), 1); // But a new face was appended. EXPECT_EQ(aGraph.Topo().Faces().Nb(), aNbFacesBefore + 1); @@ -1550,3 +1089,239 @@ TEST(BRepGraph_BuildTest, RootProductIds_AddFlatten_ProductCountUnchanged) // The original product root should still be valid. EXPECT_TRUE(aRoots.Value(0).IsValid()); } + +// ============================================================================= +// Natural-bound populate normalization tests (P1) +// ============================================================================= + +static TopoDS_Face makeNoWireNaturalFace(const occ::handle& theSurface) +{ + BRep_Builder aBuilder; + TopoDS_Face aFace; + aBuilder.MakeFace(aFace, theSurface, Precision::Confusion()); + aBuilder.NaturalRestriction(aFace, true); + return aFace; +} + +static void expectFiniteNoWireNaturalFaceSynthesizesTopology( + const occ::handle& theSurface) +{ + const TopoDS_Face aFace = makeNoWireNaturalFace(theSurface); + + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph::ShapesView::Result aRes = aGraph.Shapes().Add(aFace); + ASSERT_TRUE(aRes.IsOk()); + ASSERT_EQ(aGraph.Topo().Faces().Nb(), 1u); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + EXPECT_GT(BRepGraph_Tool::Face::NbWires(aGraph, aFaceId), 0); + EXPECT_GT(aGraph.Topo().Wires().Nb(), 0); + EXPECT_GT(aGraph.Topo().Edges().Nb(), 0); + EXPECT_GT(aGraph.Topo().Vertices().Nb(), 0); +} + +TEST(BRepGraph_BuildTest, NaturalBound_FiniteNoWireNaturalFaces_SynthesizeTopology) +{ + expectFiniteNoWireNaturalFaceSynthesizesTopology( + new Geom_SphericalSurface(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0)); + + expectFiniteNoWireNaturalFaceSynthesizesTopology( + new Geom_ToroidalSurface(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0, 3.0)); + + expectFiniteNoWireNaturalFaceSynthesizesTopology(new Geom_RectangularTrimmedSurface( + new Geom_CylindricalSurface(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0), + 0.0, + 2.0 * M_PI, + 0.0, + 10.0, + true, + true)); + + expectFiniteNoWireNaturalFaceSynthesizesTopology(new Geom_RectangularTrimmedSurface( + new Geom_ConicalSurface(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 0.2, 5.0), + 0.0, + 2.0 * M_PI, + 0.0, + 10.0, + true, + true)); + + expectFiniteNoWireNaturalFaceSynthesizesTopology( + new Geom_RectangularTrimmedSurface(new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), + 0.0, + 3.0, + 0.0, + 4.0, + true, + true)); +} + +TEST(BRepGraph_BuildTest, NaturalBound_Sphere_HasWiresAndEdges) +{ + BRepPrimAPI_MakeSphere aMaker(10.0); + const TopoDS_Shape aShape = aMaker.Shape(); + ASSERT_TRUE(aMaker.IsDone()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aRes = aGraph.Shapes().Add(aShape); + ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); + + for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + EXPECT_GT(BRepGraph_Tool::Face::NbWires(aGraph, aFaceId), 0) + << "Natural-bound sphere face must have explicit wires"; + } + EXPECT_GT(aGraph.Topo().Wires().Nb(), 0); + EXPECT_GT(aGraph.Topo().Edges().Nb(), 0); + EXPECT_GT(aGraph.Topo().Vertices().Nb(), 0); +} + +TEST(BRepGraph_BuildTest, NaturalBound_Cylinder_HasWiresAndEdges) +{ + BRepPrimAPI_MakeCylinder aMaker(5.0, 10.0); + const TopoDS_Shape aShape = aMaker.Shape(); + ASSERT_TRUE(aMaker.IsDone()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aRes = aGraph.Shapes().Add(aShape); + ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); + + for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + EXPECT_GT(BRepGraph_Tool::Face::NbWires(aGraph, aFaceId), 0) + << "Natural-bound cylinder face must have explicit wires"; + } +} + +TEST(BRepGraph_BuildTest, NaturalBound_Cone_HasWiresAndEdges) +{ + BRepPrimAPI_MakeCone aMaker(5.0, 2.0, 10.0); + const TopoDS_Shape aShape = aMaker.Shape(); + ASSERT_TRUE(aMaker.IsDone()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aRes = aGraph.Shapes().Add(aShape); + ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); + + for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + EXPECT_GT(BRepGraph_Tool::Face::NbWires(aGraph, aFaceId), 0) + << "Natural-bound cone face must have explicit wires"; + } +} + +TEST(BRepGraph_BuildTest, NaturalBound_Torus_HasWiresAndEdges) +{ + BRepPrimAPI_MakeTorus aMaker(10.0, 3.0); + const TopoDS_Shape aShape = aMaker.Shape(); + ASSERT_TRUE(aMaker.IsDone()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aRes = aGraph.Shapes().Add(aShape); + ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); + + for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + EXPECT_GT(BRepGraph_Tool::Face::NbWires(aGraph, aFaceId), 0) + << "Natural-bound torus face must have explicit wires"; + } +} + +TEST(BRepGraph_BuildTest, NaturalBound_InfinitePlane_ProducesWarnings) +{ + // An infinite plane has NaturalRestriction but infinite UV bounds. + // Build succeeds with a warning and preserves the unbounded face. + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + + BRep_Builder aBuilder; + TopoDS_Face aFace; + aBuilder.MakeFace(aFace, aPlane, Precision::Confusion()); + aBuilder.NaturalRestriction(aFace, true); + + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph::ShapesView::Result aRes = aGraph.Shapes().Add(aFace); + EXPECT_TRUE(aRes.IsOk()); + EXPECT_EQ(aRes.Status, BRepGraph::ShapesView::AddStatus::SuccessWithWarnings); + EXPECT_FALSE(aGraph.IsEmpty()); + ASSERT_EQ(aGraph.Topo().Faces().NbActive(), 1u); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + EXPECT_EQ(BRepGraph_Tool::Face::NbWires(aGraph, aFaceId), 0u); + + const BRepGraph_Validate::Result anAudit = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_TRUE(anAudit.IsValid()); + EXPECT_EQ(anAudit.NbIssues(BRepGraph_Validate::Severity::Error), 0); + EXPECT_EQ(anAudit.NbIssues(BRepGraph_Validate::Severity::Warning), 1); + EXPECT_FALSE(aGraph.Shapes().Reconstruct(aFaceId).IsNull()); +} + +TEST(BRepGraph_BuildTest, NaturalBound_PreservesExplicitWires) +{ + // A sphere face with NaturalRestriction but also explicit wires + // should keep the explicit wires, not synthesize new ones. + BRepPrimAPI_MakeSphere aMaker(10.0); + const TopoDS_Shape aShape = aMaker.Shape(); + ASSERT_TRUE(aMaker.IsDone()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aRes = aGraph.Shapes().Add(aShape); + + // Count the total wires - these are all from explicit source topology + // (BRepPrimAPI_MakeSphere already creates seam edges and wires). + const uint32_t aNbWires = aGraph.Topo().Wires().Nb(); + EXPECT_GT(aNbWires, 0); + + // Verify the graph round-trips correctly (no double-counting). + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); + const uint32_t aNbVertices = aGraph.Topo().Vertices().Nb(); + EXPECT_GT(aNbEdges, 0); + EXPECT_GT(aNbVertices, 0); +} + +TEST(BRepGraph_BuildTest, NaturalBound_PlaneSynthesis_SkipsPCurves) +{ + // A trimmed plane with NaturalRestriction should have wires/edges + // but no stored PCurves on coedges (planes are trivially derivable). + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + const occ::handle aTrimmed = + new Geom_RectangularTrimmedSurface(aPlane, -10.0, 10.0, -10.0, 10.0, false, false); + const TopoDS_Face aFace = makeNoWireNaturalFace(aTrimmed); + + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph::ShapesView::Result aRes = aGraph.Shapes().Add(aFace); + EXPECT_TRUE(aRes.IsOk()); + + // Face should have synthesized wires/edges. + EXPECT_GT(aGraph.Topo().Wires().Nb(), 0u); + EXPECT_GT(aGraph.Topo().Edges().Nb(), 0u); + + // Verify no stored PCurves on coedges, but PCurveAdaptor still works. + for (BRepGraph_WireIterator aWireIt(aGraph); aWireIt.More(); aWireIt.Next()) + { + for (BRepGraph_CoEdgesOfWire aCoEdgeIt(aGraph, aWireIt.CurrentId()); aCoEdgeIt.More(); + aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + EXPECT_FALSE(BRepGraph_Tool::CoEdge::HasPCurve(aGraph, aCoEdgeId)) + << "Plane synthesis should skip storing PCurves"; + + // PCurveAdaptor must still return a valid curve (generated on-the-fly). + const Geom2dAdaptor_Curve aPCurveAdaptor = + BRepGraph_Tool::CoEdge::PCurveAdaptor(aGraph, aCoEdgeId); + EXPECT_TRUE(aPCurveAdaptor.IsInitialized()) << "Plane PCurves should be generated on-the-fly"; + } + } +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_CacheKindRegistry_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_CacheKindRegistry_Test.cxx deleted file mode 100644 index b717b76ccd..0000000000 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_CacheKindRegistry_Test.cxx +++ /dev/null @@ -1,61 +0,0 @@ -// 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 - -#include - -TEST(BRepGraph_CacheKindRegistryTest, Register_SameGUID_SameSlot) -{ - const Standard_GUID aGUID("a1b2c3d4-1111-2222-3333-444455556666"); - const occ::handle aKind1 = new BRepGraph_CacheKind(aGUID, "SameGUID"); - const occ::handle aKind2 = new BRepGraph_CacheKind(aGUID, "SameGUID"); - const int aSlot1 = BRepGraph_CacheKindRegistry::Register(aKind1); - const int aSlot2 = BRepGraph_CacheKindRegistry::Register(aKind2); - EXPECT_EQ(aSlot1, aSlot2); -} - -TEST(BRepGraph_CacheKindRegistryTest, Register_DifferentGUID_DifferentSlot) -{ - const occ::handle aKind1 = - new BRepGraph_CacheKind(Standard_GUID("b1b2c3d4-aaaa-bbbb-cccc-ddddeeee0001"), "Kind1"); - const occ::handle aKind2 = - new BRepGraph_CacheKind(Standard_GUID("b1b2c3d4-aaaa-bbbb-cccc-ddddeeee0002"), "Kind2"); - const int aSlot1 = BRepGraph_CacheKindRegistry::Register(aKind1); - const int aSlot2 = BRepGraph_CacheKindRegistry::Register(aKind2); - EXPECT_NE(aSlot1, aSlot2); -} - -TEST(BRepGraph_CacheKindRegistryTest, FindSlot_ByGUID_ReturnsCorrectSlot) -{ - const occ::handle aKind = - new BRepGraph_CacheKind(Standard_GUID("c1c2c3c4-1111-2222-3333-aabbccddeeff"), "FindByGUID"); - const int aExpectedSlot = BRepGraph_CacheKindRegistry::Register(aKind); - - int aFoundSlot = -1; - const bool aOk = BRepGraph_CacheKindRegistry::FindSlot(aKind->ID(), aFoundSlot); - EXPECT_TRUE(aOk); - EXPECT_EQ(aFoundSlot, aExpectedSlot); -} - -TEST(BRepGraph_CacheKindRegistryTest, FindKind_BySlot_ReturnsCorrectDescriptor) -{ - const occ::handle aKind = - new BRepGraph_CacheKind(Standard_GUID("d1d2d3d4-5555-6666-7777-888899990000"), "FindBySlot"); - const int aSlot = BRepGraph_CacheKindRegistry::Register(aKind); - - const occ::handle aFound = BRepGraph_CacheKindRegistry::FindKind(aSlot); - ASSERT_FALSE(aFound.IsNull()); - EXPECT_TRUE(aFound->ID() == aKind->ID()); - EXPECT_TRUE(aFound->Name().IsEqual("FindBySlot")); -} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_CacheMesh_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_CacheMesh_Test.cxx new file mode 100644 index 0000000000..3701df098b --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_CacheMesh_Test.cxx @@ -0,0 +1,618 @@ +// 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. + +// Regression coverage for BRepGraph_CacheMesh freshness: verifies that a +// cached triangulation becomes stale when the owning Face's OwnGen bumps, +// whether the bump comes from a direct FaceDef mutation or from a face-owned +// surface use mutation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +BRepGraph makeBoxGraph() +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + return aGraph; +} + +occ::handle makeTrivialTriangulation() +{ + return new Poly_Triangulation(3, 1, false); +} + +BRepGraph_FaceId firstFaceId(const BRepGraph& theGraph) +{ + BRepGraph_FaceIterator aFaceIt(theGraph); + return aFaceIt.More() ? aFaceIt.CurrentId() : BRepGraph_FaceId(); +} + +BRepGraph_EdgeId firstEdgeId(const BRepGraph& theGraph) +{ + BRepGraph_EdgeIterator anEdgeIt(theGraph); + return anEdgeIt.More() ? anEdgeIt.CurrentId() : BRepGraph_EdgeId(); +} + +BRepGraph_VertexId firstVertexId(const BRepGraph& theGraph) +{ + BRepGraph_VertexIterator aVtxIt(theGraph); + return aVtxIt.More() ? aVtxIt.CurrentId() : BRepGraph_VertexId(); +} + +BRepGraph_CoEdgeId firstCoEdgeOfFace(const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) +{ + for (BRepGraph_RefsWireOfFace aWireRefIt(theGraph, theFaceId); aWireRefIt.More(); + aWireRefIt.Next()) + { + const BRepGraph_WireId aWireId = + theGraph.Refs().Wires().Entry(aWireRefIt.CurrentId()).ChildWireId; + for (BRepGraph_CoEdgesOfWire aCoEdgeIt(theGraph, aWireId); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + if (theGraph.Topo().CoEdges().Definition(aCoEdgeId).FaceId == theFaceId) + { + return aCoEdgeId; + } + } + } + return BRepGraph_CoEdgeId(); +} + +void writeFaceMesh(BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) +{ + const occ::handle aTri = makeTrivialTriangulation(); + theGraph.Editor().Faces().SetPersistentTriangulation(theFaceId, aTri); + theGraph.Mesh().Editor().Faces().SetCachedTriangulation(theFaceId, aTri); +} + +void writeCoEdgeMesh(BRepGraph& theGraph, + const BRepGraph_CoEdgeId theCoEdgeId, + const BRepGraph_FaceId theFaceId) +{ + const occ::handle aPoly2D = new Poly_Polygon2D(2); + theGraph.Mesh().Editor().CoEdges().SetCachedPolygon2D(theCoEdgeId, aPoly2D); + const occ::handle aTri = makeTrivialTriangulation(); + theGraph.Mesh().Editor().Faces().SetCachedTriangulation(theFaceId, aTri); + const occ::handle aPolyOnTri = + new Poly_PolygonOnTriangulation(2, false); + theGraph.Mesh().Editor().CoEdges().AppendCachedPolygonOnTri(theCoEdgeId, aPolyOnTri); +} + +void writeEdgeMesh(BRepGraph& theGraph, const BRepGraph_EdgeId theEdgeId) +{ + const occ::handle aPoly3D = new Poly_Polygon3D(2, false); + theGraph.Mesh().Editor().Edges().SetCachedPolygon3D(theEdgeId, aPoly3D); +} + +} // namespace + +TEST(BRepGraph_CacheMeshTest, CacheStaleAfterFaceMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const occ::handle aTri = makeTrivialTriangulation(); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTri); + + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTri); + + const BRepGraph_CacheMesh::FaceMeshEntry* aBefore = aGraph.Mesh().Cache().Faces().Entry(aFaceId); + ASSERT_NE(aBefore, nullptr) << "CachedMesh must be present immediately after write"; + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(aFaceId); + aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + const BRepGraph_CacheMesh::FaceMeshEntry* aAfter = aGraph.Mesh().Cache().Faces().Entry(aFaceId); + EXPECT_EQ(aAfter, nullptr) << "CachedMesh must become null (stale) after Face Mut bumps OwnGen"; +} + +TEST(BRepGraph_CacheMeshTest, CacheStaleAfterSurfaceRepMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const occ::handle aTri = makeTrivialTriangulation(); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTri); + + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTri); + + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + aGraph.Editor().Faces().ClearSurface(aFaceId); + + EXPECT_EQ(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr) + << "CachedMesh must become null after SurfaceRep Set propagates to Face OwnGen"; +} + +TEST(BRepGraph_CacheMeshTest, CacheSurvivesUnrelatedMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + BRepGraph_FaceIterator anOther(aGraph); + anOther.Next(); + ASSERT_TRUE(anOther.More()) << "Box should have >1 face for this test"; + const BRepGraph_FaceId anOtherFaceId = anOther.CurrentId(); + + const occ::handle aTri = makeTrivialTriangulation(); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTri); + + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTri); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(anOtherFaceId); + aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr) + << "Unrelated face mutation must not invalidate this face's cache"; +} + +TEST(BRepGraph_CacheMeshTest, ClearDropsLargeCacheAndAllowsSmallRegenerate) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const occ::handle aCheckTri = new Poly_Triangulation(3, 1, false); + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aCheckTri); + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + aGraph.CacheRegistry().ClearAll(); + EXPECT_EQ(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aCheckTri); + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + EXPECT_EQ(aGraph.Mesh().Cache().Faces().Entry(aFaceId)->Triangulation, aCheckTri); +} + +// ---- Step 15: New tests for CacheMesh freshness redesign ---- + +TEST(BRepGraph_CacheMeshTest, FaceCache_StaleAfterEdgeMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + writeFaceMesh(aGraph, aFaceId); + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + const BRepGraph_EdgeId anEdgeId = firstEdgeId(aGraph); + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Edges().Mut(anEdgeId); + aGraph.Editor().Edges().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_EQ(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr) + << "Face cache must become stale after edge mutation (SubtreeGen propagation)"; +} + +TEST(BRepGraph_CacheMeshTest, FaceCache_StaleAfterCoEdgeMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeFaceMesh(aGraph, aFaceId); + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().CoEdges().Mut(aCoEdgeId); + aGraph.Editor().CoEdges().SetOrientation(aGuard, TopAbs_REVERSED); + } + + EXPECT_EQ(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr) + << "Face cache must become stale after coedge mutation (SubtreeGen propagation)"; +} + +TEST(BRepGraph_CacheMeshTest, FaceCache_StaleAfterVertexMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_VertexId aVtxId = firstVertexId(aGraph); + ASSERT_TRUE(aVtxId.IsValid(aGraph.Topo().Vertices().Nb())); + + writeFaceMesh(aGraph, aFaceId); + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Vertices().Mut(aVtxId); + aGraph.Editor().Vertices().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_EQ(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr) + << "Face cache must become stale after vertex mutation (SubtreeGen propagation)"; +} + +TEST(BRepGraph_CacheMeshTest, EdgeCache_StaleAfterVertexMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_EdgeId anEdgeId = firstEdgeId(aGraph); + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + + const BRepGraph_VertexId aVtxId = firstVertexId(aGraph); + ASSERT_TRUE(aVtxId.IsValid(aGraph.Topo().Vertices().Nb())); + + writeEdgeMesh(aGraph, anEdgeId); + ASSERT_NE(aGraph.Mesh().Cache().Edges().Entry(anEdgeId), nullptr); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Vertices().Mut(aVtxId); + aGraph.Editor().Vertices().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_EQ(aGraph.Mesh().Cache().Edges().Entry(anEdgeId), nullptr) + << "Edge cache must become stale after vertex mutation"; +} + +TEST(BRepGraph_CacheMeshTest, EdgeCache_FreshAfterUnrelatedFaceMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_EdgeId anEdgeId = firstEdgeId(aGraph); + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + + writeEdgeMesh(aGraph, anEdgeId); + ASSERT_NE(aGraph.Mesh().Cache().Edges().Entry(anEdgeId), nullptr); + + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(aFaceId); + aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_NE(aGraph.Mesh().Cache().Edges().Entry(anEdgeId), nullptr) + << "Edge cache must survive unrelated face mutation"; +} + +TEST(BRepGraph_CacheMeshTest, CoEdge_Polygon2DFreshAfterFaceMeshChange) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygon2D(aCoEdgeId), + nullptr); + + const occ::handle aNewTri = new Poly_Triangulation(4, 1, false); + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aNewTri); + + EXPECT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygon2D(aCoEdgeId), + nullptr) + << "Polygon2D must stay fresh when only face mesh content changes"; +} + +TEST(BRepGraph_CacheMeshTest, CoEdge_PolygonOnTriStaleAfterFaceMeshChange) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr); + + const occ::handle aNewTri = new Poly_Triangulation(4, 1, false); + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aNewTri); + + EXPECT_EQ(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr) + << "PolygonOnTri must become stale when face mesh content changes"; +} + +TEST(BRepGraph_CacheMeshTest, CoEdge_PolygonOnTriStaleAfterFaceTopologyChange) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(aFaceId); + aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_EQ(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr) + << "PolygonOnTri must become stale when face topology changes"; +} + +TEST(BRepGraph_CacheMeshTest, CoEdge_PolygonOnTriStaleAfterCoEdgeMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().CoEdges().Mut(aCoEdgeId); + aGraph.Editor().CoEdges().SetOrientation(aGuard, TopAbs_REVERSED); + } + + EXPECT_EQ(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr) + << "PolygonOnTri must become stale when coedge topology changes"; +} + +TEST(BRepGraph_CacheMeshTest, CoEdge_FreshAfterUnrelatedEdgeMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygon2D(aCoEdgeId), + nullptr); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr); + + BRepGraph_EdgeIterator anEdgeIt(aGraph); + anEdgeIt.Next(); + const BRepGraph_EdgeId anOtherEdgeId = anEdgeIt.CurrentId(); + if (anOtherEdgeId.IsValid(aGraph.Topo().Edges().Nb())) + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Edges().Mut(anOtherEdgeId); + aGraph.Editor().Edges().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygon2D(aCoEdgeId), + nullptr) + << "Polygon2D must survive unrelated edge mutation"; +} + +TEST(BRepGraph_CacheMeshTest, CoEdge_UsesCoEdgeDefFaceId) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr); + + const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aCoEdgeId); + ASSERT_TRUE(aDef.FaceId.IsValid(aGraph.Topo().Faces().Nb())); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(aDef.FaceId); + aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_EQ(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr) + << "Coedge freshness must use CoEdgeDef::FaceId for face binding"; +} + +TEST(BRepGraph_CacheMeshTest, CoEdge_Polygon2DStaleAfterSlotRecipeChange) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygon2D(aCoEdgeId), + nullptr); + + aGraph.CacheRegistry().Find()->Clear(); + + EXPECT_EQ(aGraph.CacheRegistry().Find()->FindCoEdgePolygon2D(aCoEdgeId), + nullptr) + << "Polygon2D must become stale after cache clear (SlotGeneration bump)"; +} + +TEST(BRepGraph_CacheMeshTest, CoEdge_FaceNotYetMeshed_SnapshotZero) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + const occ::handle aPoly2D = new Poly_Polygon2D(2); + aGraph.Mesh().Editor().CoEdges().SetCachedPolygon2D(aCoEdgeId, aPoly2D); + const occ::handle aPolyOnTri = + new Poly_PolygonOnTriangulation(2, false); + aGraph.Mesh().Editor().CoEdges().AppendCachedPolygonOnTri(aCoEdgeId, aPolyOnTri); + + EXPECT_EQ(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr) + << "PolygonOnTri must be stale when face has never been meshed (snapshot=0 vs default=0)"; + + writeFaceMesh(aGraph, aFaceId); + + EXPECT_EQ(aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId), + nullptr) + << "PolygonOnTri must remain stale after face is meshed (MeshGeneration bumped to 1)"; +} + +TEST(BRepGraph_CacheMeshTest, Needs_DetectsChildDrivenFaceStaleness) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + writeFaceMesh(aGraph, aFaceId); + ASSERT_FALSE( + aGraph.CacheRegistry().Find()->Needs(aGraph, BRepGraph_NodeId(aFaceId))); + + const BRepGraph_VertexId aVtxId = firstVertexId(aGraph); + ASSERT_TRUE(aVtxId.IsValid(aGraph.Topo().Vertices().Nb())); + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Vertices().Mut(aVtxId); + aGraph.Editor().Vertices().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_TRUE( + aGraph.CacheRegistry().Find()->Needs(aGraph, BRepGraph_NodeId(aFaceId))) + << "Needs(face) must detect staleness driven by child vertex mutation"; +} + +TEST(BRepGraph_CacheMeshTest, Needs_DetectsStaleCoedgeOnTri) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_FALSE( + aGraph.CacheRegistry().Find()->Needs(aGraph, BRepGraph_NodeId(aFaceId))); + + const occ::handle aNewTri = new Poly_Triangulation(4, 1, false); + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aNewTri); + + EXPECT_TRUE( + aGraph.CacheRegistry().Find()->Needs(aGraph, BRepGraph_NodeId(aFaceId))) + << "Needs(face) must detect coedge PolygonOnTri staleness from face mesh change"; +} + +TEST(BRepGraph_CacheMeshTest, VertexPropagation_ReachesFaces) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_VertexId aVtxId = firstVertexId(aGraph); + ASSERT_TRUE(aVtxId.IsValid(aGraph.Topo().Vertices().Nb())); + + writeFaceMesh(aGraph, aFaceId); + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + { + BRepGraph_MutGuard aGuard = aGraph.Editor().Vertices().Mut(aVtxId); + aGraph.Editor().Vertices().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); + } + + EXPECT_EQ(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr) + << "Vertex mutation must propagate through edge -> wire -> face chain"; +} + +TEST(BRepGraph_CacheMeshTest, MeshGeneration_MonotonicAcrossClear) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + const BRepGraph_CacheMesh::CoEdgeMeshEntry* aCoEntry = + aGraph.CacheRegistry().Find()->FindCoEdgePolygonOnTri(aCoEdgeId); + ASSERT_NE(aCoEntry, nullptr); + + aGraph.Mesh().Editor().Faces().Clear(aFaceId); + writeFaceMesh(aGraph, aFaceId); + + const BRepGraph_CacheMesh::CoEdgeMeshEntry* aAfterEntry = + aGraph.CacheRegistry().Find()->findCoEdgeEntryRaw( + BRepGraph_CacheMesh::DefaultDisplaySlot, + aCoEdgeId); + ASSERT_NE(aAfterEntry, nullptr); + const BRepGraph_CacheMesh::FaceMeshEntry* aFaceEntry = + aGraph.CacheRegistry().Find()->findFaceEntryRaw( + BRepGraph_CacheMesh::DefaultDisplaySlot, + aFaceId); + ASSERT_NE(aFaceEntry, nullptr); + EXPECT_GT(aFaceEntry->MeshGeneration, aAfterEntry->FaceMeshGeneration) + << "Face MeshGeneration must exceed coedge snapshot after clear+rewrite"; +} + +TEST(BRepGraph_CacheMeshTest, FaceClear_PreservesCoEdgePolygon2D) +{ + BRepGraph aGraph = makeBoxGraph(); + const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + writeCoEdgeMesh(aGraph, aCoEdgeId, aFaceId); + ASSERT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygon2D(aCoEdgeId), + nullptr); + + aGraph.Mesh().Editor().Faces().Clear(aFaceId); + + EXPECT_NE(aGraph.CacheRegistry().Find()->FindCoEdgePolygon2D(aCoEdgeId), + nullptr) + << "FaceOps::Clear must not destroy coedge Polygon2D"; +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_CacheRegistry_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_CacheRegistry_Test.cxx new file mode 100644 index 0000000000..8ad88a031b --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_CacheRegistry_Test.cxx @@ -0,0 +1,472 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace +{ + +class TestCache : public BRepGraph_Cache +{ +public: + DEFINE_STANDARD_RTTI_INLINE(TestCache, BRepGraph_Cache) + + TestCache(const Standard_GUID& theID, const TCollection_AsciiString& theName) + : myID(theID), + myName(theName) + { + } + + const Standard_GUID& ID() const override { return myID; } + + const TCollection_AsciiString& Name() const override { return myName; } + +private: + Standard_GUID myID; + TCollection_AsciiString myName; +}; + +class EntryTestCache : public BRepGraph_Cache +{ +public: + DEFINE_STANDARD_RTTI_INLINE(EntryTestCache, BRepGraph_Cache) + + static const Standard_GUID& GetID() + { + static const Standard_GUID THE_ID("f1f2f3f4-1234-4321-8888-010203040506"); + return THE_ID; + } + + const Standard_GUID& ID() const override { return GetID(); } + + const TCollection_AsciiString& Name() const override + { + static const TCollection_AsciiString THE_NAME("EntryTestCache"); + return THE_NAME; + } + + bool Attached() const noexcept { return IsAttached(); } + + bool SetNodeOwn(const BRepGraph_NodeId theNode, const int theValue) + { + NodeValue anEntry; + if (!anEntry.BindOwnGen(*this, theNode)) + { + return false; + } + anEntry.Value = theValue; + set(myNodeOwnValues, theNode, anEntry); + return true; + } + + bool GetNodeOwn(const BRepGraph_NodeId theNode, int& theValue) const + { + const NodeValue* anEntry = myNodeOwnValues.Seek(theNode); + if (anEntry == nullptr || !anEntry->IsFreshOwn(*this, theNode)) + { + return false; + } + theValue = anEntry->Value; + return true; + } + + bool SetNodeSubtree(const BRepGraph_NodeId theNode, const int theValue) + { + NodeValue anEntry; + if (!anEntry.BindSubtreeGen(*this, theNode)) + { + return false; + } + anEntry.Value = theValue; + set(myNodeSubtreeValues, theNode, anEntry); + return true; + } + + bool GetNodeSubtree(const BRepGraph_NodeId theNode, int& theValue) const + { + const NodeValue* anEntry = myNodeSubtreeValues.Seek(theNode); + if (anEntry == nullptr || !anEntry->IsFreshSubtree(*this, theNode)) + { + return false; + } + theValue = anEntry->Value; + return true; + } + + bool SetRefOwn(const BRepGraph_RefId theRef, const int theValue) + { + RefValue anEntry; + if (!anEntry.BindOwnGen(*this, theRef)) + { + return false; + } + anEntry.Value = theValue; + set(myRefOwnValues, theRef, anEntry); + return true; + } + + bool GetRefOwn(const BRepGraph_RefId theRef, int& theValue) const + { + const RefValue* anEntry = myRefOwnValues.Seek(theRef); + if (anEntry == nullptr || !anEntry->IsFreshOwn(*this, theRef)) + { + return false; + } + theValue = anEntry->Value; + return true; + } + + bool SetItemOwn(const BRepGraph_ItemId theItem, const int theValue) + { + ItemValue anEntry; + if (!anEntry.BindOwnGen(*this, theItem)) + { + return false; + } + anEntry.Value = theValue; + set(myItemOwnValues, theItem, anEntry); + return true; + } + + bool GetItemOwn(const BRepGraph_ItemId theItem, int& theValue) const + { + const ItemValue* anEntry = myItemOwnValues.Seek(theItem); + if (anEntry == nullptr || !anEntry->IsFreshOwn(*this, theItem)) + { + return false; + } + theValue = anEntry->Value; + return true; + } + + bool ResolveChildForTest(const BRepGraph_RefId theRef, BRepGraph_NodeId& theNode) const + { + return ResolveActiveRefChild(theRef, theNode); + } + + void Clear() noexcept override + { + ++myClearCount; + myNodeOwnValues.Clear(); + myNodeSubtreeValues.Clear(); + myRefOwnValues.Clear(); + myItemOwnValues.Clear(); + } + + int ClearCount() const noexcept { return myClearCount; } + + int NbValues() const noexcept + { + return static_cast(myNodeOwnValues.Size() + myNodeSubtreeValues.Size() + + myRefOwnValues.Size() + myItemOwnValues.Size()); + } + +private: + struct NodeValue : NodeEntry + { + int Value = 0; + }; + + struct RefValue : RefEntry + { + int Value = 0; + }; + + struct ItemValue : ItemEntry + { + int Value = 0; + }; + + template + static void set(NCollection_DataMap& theMap, + const KeyT theKey, + const ValueT& theValue) + { + if (theMap.IsBound(theKey)) + { + theMap.ChangeFind(theKey) = theValue; + } + else + { + theMap.Bind(theKey, theValue); + } + } + + NCollection_DataMap myNodeOwnValues; + NCollection_DataMap myNodeSubtreeValues; + NCollection_DataMap myRefOwnValues; + NCollection_DataMap myItemOwnValues; + int myClearCount = 0; +}; + +static BRepGraph makeBoxGraph() +{ + BRepGraph aGraph; + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + [[maybe_unused]] const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().Add(aBoxMaker.Shape()); + return aGraph; +} + +} // namespace + +TEST(BRepGraph_CacheRegistryTest, Register_SameGUID_SameSlot) +{ + const Standard_GUID aGUID("a1b2c3d4-1111-2222-3333-444455556666"); + const occ::handle aKind1 = new TestCache(aGUID, "SameGUID"); + const occ::handle aKind2 = new TestCache(aGUID, "SameGUID"); + BRepGraph_CacheRegistry aRegistry; + const uint32_t aSlot1 = aRegistry.RegisterCache(aKind1); + const uint32_t aSlot2 = aRegistry.RegisterCache(aKind2); + EXPECT_EQ(aSlot1, aSlot2); +} + +TEST(BRepGraph_CacheRegistryTest, Register_DifferentGUID_DifferentSlot) +{ + const occ::handle aKind1 = + new TestCache(Standard_GUID("b1b2c3d4-aaaa-bbbb-cccc-ddddeeee0001"), "Kind1"); + const occ::handle aKind2 = + new TestCache(Standard_GUID("b1b2c3d4-aaaa-bbbb-cccc-ddddeeee0002"), "Kind2"); + BRepGraph_CacheRegistry aRegistry; + const uint32_t aSlot1 = aRegistry.RegisterCache(aKind1); + const uint32_t aSlot2 = aRegistry.RegisterCache(aKind2); + EXPECT_NE(aSlot1, aSlot2); +} + +TEST(BRepGraph_CacheRegistryTest, FindSlot_ByGUID_ReturnsCorrectSlot) +{ + const occ::handle aKind = + new TestCache(Standard_GUID("c1c2c3c4-1111-2222-3333-aabbccddeeff"), "FindByGUID"); + BRepGraph_CacheRegistry aRegistry; + const uint32_t aExpectedSlot = aRegistry.RegisterCache(aKind); + + uint32_t aFoundSlot = 0; + ASSERT_TRUE(aRegistry.FindSlot(aKind->ID(), aFoundSlot)); + EXPECT_EQ(aFoundSlot, aExpectedSlot); +} + +TEST(BRepGraph_CacheRegistryTest, FindCache_BySlot_ReturnsCorrectDescriptor) +{ + const occ::handle aKind = + new TestCache(Standard_GUID("d1d2d3d4-5555-6666-7777-888899990000"), "FindBySlot"); + BRepGraph_CacheRegistry aRegistry; + const uint32_t aSlot = aRegistry.RegisterCache(aKind); + + const occ::handle aFound = aRegistry.Cache(aSlot); + ASSERT_FALSE(aFound.IsNull()); + EXPECT_TRUE(aFound->ID() == aKind->ID()); + EXPECT_TRUE(aFound->Name().IsEqual("FindBySlot")); +} + +TEST(BRepGraph_CacheRegistryTest, Cache_InvalidSlot_ReturnsNull) +{ + BRepGraph_CacheRegistry aRegistry; + EXPECT_TRUE(aRegistry.Cache(0).IsNull()); +} + +TEST(BRepGraph_CacheRegistryTest, SameCache_CanUseDifferentGraphLocalSlots) +{ + const occ::handle aShared = + new TestCache(Standard_GUID("e1e2e3e4-1111-2222-3333-888899990001"), "Shared"); + const occ::handle aPrefix = + new TestCache(Standard_GUID("e1e2e3e4-1111-2222-3333-888899990002"), "Prefix"); + + BRepGraph_CacheRegistry aRegistry1; + BRepGraph_CacheRegistry aRegistry2; + const uint32_t aSlot1 = aRegistry1.RegisterCache(aShared); + [[maybe_unused]] const uint32_t aPrefixSlot = aRegistry2.RegisterCache(aPrefix); + const uint32_t aSlot2 = aRegistry2.RegisterCache(aShared); + + EXPECT_EQ(aSlot1, 0u); + EXPECT_EQ(aSlot2, 1u); +} + +TEST(BRepGraph_CacheRegistryTest, Unregister_CompactsSlotsAndUpdatesIterator) +{ + BRepGraph aGraph = makeBoxGraph(); + const occ::handle aFirst = + new TestCache(Standard_GUID("c8f2fd46-7c54-42a1-a73e-83c6ae2c7401"), "First"); + const occ::handle aSecond = + new TestCache(Standard_GUID("ac165657-f0b6-4e51-9a1b-ef9d33c12096"), "Second"); + + const uint32_t aFirstSlot = aGraph.CacheRegistry().RegisterCache(aFirst); + const uint32_t aSecondSlot = aGraph.CacheRegistry().RegisterCache(aSecond); + ASSERT_EQ(aFirstSlot, 0u); + ASSERT_EQ(aSecondSlot, 1u); + ASSERT_EQ(aGraph.CacheRegistry().NbCaches(), 2u); + + aGraph.CacheRegistry().UnregisterCache(aFirst->ID()); + + EXPECT_EQ(aGraph.CacheRegistry().NbCaches(), 1u); + uint32_t aFoundSlot = 0; + ASSERT_TRUE(aGraph.CacheRegistry().FindSlot(aSecond->ID(), aFoundSlot)); + EXPECT_EQ(aFoundSlot, 0u); + uint32_t aIterCount = 0; + for (const occ::handle aCache : aGraph.CacheRegistry().CacheIter()) + { + ASSERT_FALSE(aCache.IsNull()); + EXPECT_TRUE(aCache->ID() == aSecond->ID()); + ++aIterCount; + } + EXPECT_EQ(aIterCount, 1u); +} + +TEST(BRepGraph_CacheRegistryTest, ResolveActiveRefChild_RejectsInvalidAndRemovedRefs) +{ + BRepGraph aGraph = makeBoxGraph(); + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + + const BRepGraph_SolidId aSolid = BRepGraph_SolidId::Start(); + ASSERT_FALSE(aGraph.Topo().Solids().Relations(aSolid).ShellRefIds.IsEmpty()); + const BRepGraph_ShellRefId aShellRef = + aGraph.Topo().Solids().Relations(aSolid).ShellRefIds.First(); + + BRepGraph_NodeId aNode; + ASSERT_TRUE(aCache->ResolveChildForTest(BRepGraph_RefId(aShellRef), aNode)); + EXPECT_EQ(aNode.NodeKind, BRepGraph_NodeId::Kind::Shell); + + EXPECT_FALSE(aCache->ResolveChildForTest(BRepGraph_RefId::Invalid(), aNode)); + EXPECT_FALSE( + aCache->ResolveChildForTest(BRepGraph_RefId(BRepGraph_RefId::Kind::Shell, 999999), aNode)); + + aGraph.Editor().Gen().RemoveRef(aShellRef); + EXPECT_FALSE(aCache->ResolveChildForTest(BRepGraph_RefId(aShellRef), aNode)); +} + +TEST(BRepGraph_CacheRegistryTest, NodeOwnEntry_MissesAfterOwnGenerationChange) +{ + BRepGraph aGraph = makeBoxGraph(); + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + + const BRepGraph_NodeId anEdge(BRepGraph_EdgeId::Start()); + ASSERT_TRUE(aCache->SetNodeOwn(anEdge, 42)); + + int aValue = 0; + EXPECT_TRUE(aCache->GetNodeOwn(anEdge, aValue)); + EXPECT_EQ(aValue, 42); + + aGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()).MarkDirty(); + + EXPECT_FALSE(aCache->GetNodeOwn(anEdge, aValue)); +} + +TEST(BRepGraph_CacheRegistryTest, NodeSubtreeEntry_MissesAfterChildGenerationChange) +{ + BRepGraph aGraph = makeBoxGraph(); + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + + const BRepGraph_NodeId aWire(BRepGraph_WireId::Start()); + ASSERT_TRUE(aCache->SetNodeSubtree(aWire, 77)); + + int aValue = 0; + EXPECT_TRUE(aCache->GetNodeSubtree(aWire, aValue)); + EXPECT_EQ(aValue, 77); + + aGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()).MarkDirty(); + + EXPECT_FALSE(aCache->GetNodeSubtree(aWire, aValue)); +} + +TEST(BRepGraph_CacheRegistryTest, RefOwnEntry_MissesAfterRefGenerationChange) +{ + BRepGraph aGraph = makeBoxGraph(); + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + + const BRepGraph_ProductId aProductId = BRepGraph_ProductId::Start(); + ASSERT_FALSE(aGraph.Topo().Products().Relations(aProductId).OccurrenceRefIds.IsEmpty()); + const BRepGraph_OccurrenceRefId anOccRef = + aGraph.Topo().Products().Relations(aProductId).OccurrenceRefIds.First(); + ASSERT_TRUE(aCache->SetRefOwn(BRepGraph_RefId(anOccRef), 99)); + + int aValue = 0; + EXPECT_TRUE(aCache->GetRefOwn(BRepGraph_RefId(anOccRef), aValue)); + EXPECT_EQ(aValue, 99); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); + ASSERT_TRUE(BRepGraph_Transform::MoveRef(aGraph, anOccRef, aTrsf)); + + EXPECT_FALSE(aCache->GetRefOwn(BRepGraph_RefId(anOccRef), aValue)); +} + +TEST(BRepGraph_CacheRegistryTest, ItemOwnEntry_WorksForNodeAndRef) +{ + BRepGraph aGraph = makeBoxGraph(); + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + + const BRepGraph_ItemId aFaceItem(BRepGraph_FaceId::Start()); + ASSERT_TRUE(aCache->SetItemOwn(aFaceItem, 11)); + + const BRepGraph_SolidId aSolid = BRepGraph_SolidId::Start(); + ASSERT_FALSE(aGraph.Topo().Solids().Relations(aSolid).ShellRefIds.IsEmpty()); + const BRepGraph_ShellRefId aShellRef = + aGraph.Topo().Solids().Relations(aSolid).ShellRefIds.First(); + const BRepGraph_ItemId aRefItem(aShellRef); + ASSERT_TRUE(aCache->SetItemOwn(aRefItem, 22)); + + int aValue = 0; + EXPECT_TRUE(aCache->GetItemOwn(aFaceItem, aValue)); + EXPECT_EQ(aValue, 11); + EXPECT_TRUE(aCache->GetItemOwn(aRefItem, aValue)); + EXPECT_EQ(aValue, 22); +} + +TEST(BRepGraph_CacheRegistryTest, Unregister_DetachesAndClearsRepresentation) +{ + BRepGraph aGraph = makeBoxGraph(); + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + ASSERT_TRUE(aCache->SetNodeOwn(BRepGraph_NodeId(BRepGraph_EdgeId::Start()), 1)); + ASSERT_EQ(aCache->NbValues(), 1); + + aGraph.CacheRegistry().UnregisterCache(EntryTestCache::GetID()); + + EXPECT_FALSE(aCache->Attached()); + EXPECT_EQ(aCache->NbValues(), 0); + EXPECT_GE(aCache->ClearCount(), 1); + + int aValue = 0; + EXPECT_FALSE(aCache->GetNodeOwn(BRepGraph_NodeId(BRepGraph_EdgeId::Start()), aValue)); +} + +TEST(BRepGraph_CacheRegistryTest, GraphDestruction_DetachesAndClearsExternalCacheHandle) +{ + occ::handle aCache; + { + BRepGraph aGraph = makeBoxGraph(); + aCache = aGraph.CacheRegistry().Ensure(); + ASSERT_TRUE(aCache->SetNodeOwn(BRepGraph_NodeId(BRepGraph_EdgeId::Start()), 5)); + ASSERT_TRUE(aCache->Attached()); + ASSERT_EQ(aCache->NbValues(), 1); + } + + ASSERT_FALSE(aCache.IsNull()); + EXPECT_FALSE(aCache->Attached()); + EXPECT_EQ(aCache->NbValues(), 0); + EXPECT_GE(aCache->ClearCount(), 1); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx index f117b710ab..20306c7f57 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ChildExplorer_Test.cxx @@ -15,12 +15,12 @@ #include #include #include -#include +#include #include #include #include #include -#include +#include #include #include @@ -65,9 +65,9 @@ TEST(BRepGraph_ChildExplorerTest, Box_EdgeOccurrences_Count24) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -86,9 +86,9 @@ TEST(BRepGraph_ChildExplorerTest, Box_FaceOccurrences_Count6) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aFaceCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -106,9 +106,9 @@ TEST(BRepGraph_ChildExplorerTest, Box_VertexOccurrences) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Count unique vertices. int aCount = 0; @@ -130,9 +130,9 @@ TEST(BRepGraph_ChildExplorerTest, Face_EdgeOccurrences_4) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -150,9 +150,9 @@ TEST(BRepGraph_ChildExplorerTest, InvalidRoot_Empty) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_NodeId(), BRepGraph_NodeId::Kind::Edge); EXPECT_FALSE(anExp.More()); @@ -162,9 +162,9 @@ TEST(BRepGraph_ChildExplorerTest, RootEqualsTarget_ReturnsSelf) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_FaceId::Start(), BRepGraph_NodeId::Kind::Face); ASSERT_TRUE(anExp.More()); @@ -177,9 +177,9 @@ TEST(BRepGraph_ChildExplorerTest, AvoidKind_Shell_SkipsContainedFaces) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_SolidId::Start(), @@ -193,9 +193,9 @@ TEST(BRepGraph_ChildExplorerTest, AvoidKind_EmitBoundary_ReturnsFacesInsteadOfEd { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aFaceCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -216,9 +216,9 @@ TEST(BRepGraph_ChildExplorerTest, AvoidKind_SameAsTarget_IsIgnored) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aFaceCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -239,9 +239,9 @@ TEST(BRepGraph_ChildExplorerTest, AllDescendants_Recursive_YieldsAllKinds) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aShellCount = 0; int aFaceCount = 0; @@ -290,9 +290,9 @@ TEST(BRepGraph_ChildExplorerTest, AllDescendants_AvoidFaceBoundary_StopsBelowFac { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aShellCount = 0; int aFaceCount = 0; @@ -323,9 +323,9 @@ TEST(BRepGraph_ChildExplorerTest, NoCumLoc_IdentityLocation) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_SolidId::Start(), BRepGraph_NodeId::Kind::Edge, false, true); @@ -340,9 +340,9 @@ TEST(BRepGraph_ChildExplorerTest, NoCumOri_ForwardOrientation) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_SolidId::Start(), BRepGraph_NodeId::Kind::Edge, true, false); @@ -359,9 +359,9 @@ TEST(BRepGraph_ChildExplorerTest, GlobalLocation_Box_Identity) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // All paths in a simple box should compose to identity. BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_SolidId::Start(), BRepGraph_NodeId::Kind::Edge); @@ -375,9 +375,9 @@ TEST(BRepGraph_ChildExplorerTest, GlobalOrientation_BoxEdges_ForwardOrReversed) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_SolidId::Start(), BRepGraph_NodeId::Kind::Edge); for (; anExp.More(); anExp.Next()) @@ -400,9 +400,8 @@ TEST(BRepGraph_ChildExplorerTest, Compound_FaceCount) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, aComp); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = aGraph.Shapes().Add(aComp); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -422,9 +421,9 @@ TEST(BRepGraph_ChildExplorerTest, NodeOf_Kind_Face) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_SolidId::Start(), BRepGraph_NodeId::Kind::Edge); ASSERT_TRUE(anExp.More()); @@ -451,9 +450,8 @@ TEST(BRepGraph_ChildExplorerTest, DeepCompound_NoStackOverflow) } BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, aInner); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = aGraph.Shapes().Add(aInner); + ASSERT_FALSE(aGraph.IsEmpty()); // Should not crash (stack overflow) and should find the box's faces. int aCount = 0; @@ -472,9 +470,9 @@ TEST(BRepGraph_ChildExplorerTest, Recreate_ResetAndReexplore) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes19 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes19 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aFaceCount = 0; BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_SolidId::Start(), BRepGraph_NodeId::Kind::Face); @@ -503,9 +501,9 @@ TEST(BRepGraph_ChildExplorerTest, CoEdgeTarget_Reachable) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes20 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes20 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // CoEdge target from Solid must find all coedges (24 edge occurrences = 24 coedges). int aCount = 0; @@ -525,9 +523,9 @@ TEST(BRepGraph_ChildExplorerTest, CoEdgeTarget_FromFace_Count4) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes21 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes21 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -546,21 +544,21 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_ShellFaces_CountAndOrder) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes22 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes22 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_ShellId aShellId(0); - const NCollection_DynamicArray& aFaceRefIds = + const NCollection_LinearVector& aFaceRefIds = aGraph.Refs().Faces().IdsOf(aShellId); NCollection_DynamicArray anExpectedFaceIds; for (const BRepGraph_FaceRefId& aFaceRefId : aFaceRefIds) { const BRepGraphInc::FaceRef& aRef = aGraph.Refs().Faces().Entry(aFaceRefId); - if (!aRef.IsRemoved) + if (!aFaceRefId.IsRemoved(aGraph)) { - anExpectedFaceIds.Append(aRef.FaceDefId.Index); + anExpectedFaceIds.Append(aRef.ChildFaceId.Index); } } @@ -574,8 +572,8 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_ShellFaces_CountAndOrder) anActualFaceIds.Append(anIt.Current().DefId.Index); } - ASSERT_EQ(anActualFaceIds.Length(), anExpectedFaceIds.Length()); - for (int i = 0; i < anExpectedFaceIds.Length(); ++i) + ASSERT_EQ(anActualFaceIds.Size(), anExpectedFaceIds.Size()); + for (size_t i = 0; i < anExpectedFaceIds.Size(); ++i) { EXPECT_EQ(anActualFaceIds.Value(i), anExpectedFaceIds.Value(i)); } @@ -585,9 +583,9 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_ShellFaces_ExposeParentAndRef) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes23 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes23 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_ShellId aShellId(0); int anOrdinal = 0; @@ -607,16 +605,16 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_ProductShapeRoot_ViaOccurrenceR { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes24 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes24 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_ProductId aProductId = - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); + const BRepGraph_ProductId aProductId = aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + aGraph.Editor().Products().AppendDocumentRoot(aProductId); ASSERT_TRUE(aProductId.IsValid()); // In the new model, products reference children through OccurrenceRefIds. - // The shape root is reached via an occurrence whose ChildDefId is the topology node. + // The shape root is reached via an occurrence whose ChildNodeId is the topology node. BRepGraph_ChildExplorer anIt(aGraph, aProductId, BRepGraph_ChildExplorer::TraversalMode::DirectChildren); @@ -635,9 +633,9 @@ TEST(BRepGraph_ChildExplorerTest, RootEqualsTarget_LinkKindNone) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes25 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes25 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_FaceId::Start(), BRepGraph_NodeId::Kind::Face); ASSERT_TRUE(anExp.More()); @@ -650,26 +648,27 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_ProductOccurrences_ExposeOccurr { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes26 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes26 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_ProductId aPart = - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); - const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + aGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(aPart.IsValid()); ASSERT_TRUE(anAssembly.IsValid()); const BRepGraph_OccurrenceId anOcc0 = - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location()); + aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()); const BRepGraph_OccurrenceId anOcc1 = - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location()); + aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()); ASSERT_TRUE(anOcc0.IsValid()); ASSERT_TRUE(anOcc1.IsValid()); - const NCollection_DynamicArray& anOccurrenceRefs = + const NCollection_LinearVector& anOccurrenceRefs = aGraph.Refs().Occurrences().IdsOf(anAssembly); - ASSERT_EQ(anOccurrenceRefs.Length(), 2); + ASSERT_EQ(anOccurrenceRefs.Size(), 2); int anOrdinal = 0; for (BRepGraph_ChildExplorer anIt(aGraph, @@ -688,25 +687,22 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_RemovedFaceRef_IsSkipped) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes27 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes27 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_ShellId aShellId(0); - const NCollection_DynamicArray& aFaceRefIds = + const NCollection_LinearVector& aFaceRefIds = aGraph.Refs().Faces().IdsOf(aShellId); - ASSERT_GT(aFaceRefIds.Length(), 0); + ASSERT_GT(aFaceRefIds.Size(), 0); + const size_t aNbFaceRefsBefore = aFaceRefIds.Size(); const BRepGraph_FaceRefId aRemovedRef = aFaceRefIds.Value(0); - const BRepGraph_FaceId aRemovedFaceId = aGraph.Refs().Faces().Entry(aRemovedRef).FaceDefId; + const BRepGraph_FaceId aRemovedFaceId = aGraph.Refs().Faces().Entry(aRemovedRef).ChildFaceId; - { - BRepGraph_MutGuard aFaceRef = - aGraph.Editor().Faces().MutRef(aRemovedRef); - aGraph.Editor().Gen().RemoveRef(aRemovedRef); - } + aGraph.Editor().Gen().RemoveRef(aRemovedRef); - int aCount = 0; + size_t aCount = 0; for (BRepGraph_ChildExplorer anIt = makeDirectChildExplorer(aGraph, aShellId, BRepGraph_NodeId::Kind::Face); anIt.More(); @@ -716,25 +712,25 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_RemovedFaceRef_IsSkipped) ++aCount; } - EXPECT_EQ(aCount, aFaceRefIds.Length() - 1); + EXPECT_EQ(aCount, aNbFaceRefsBefore - 1); } TEST(BRepGraph_ChildExplorerTest, DirectChildren_WireChildren_AreCoEdges) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes28 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes28 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_FaceId aFaceId(0); - const NCollection_DynamicArray& aWireRefs = + const NCollection_LinearVector& aWireRefs = aGraph.Refs().Wires().IdsOf(aFaceId); - ASSERT_GT(aWireRefs.Length(), 0); + ASSERT_GT(aWireRefs.Size(), 0); - const BRepGraph_WireId aWireId = aGraph.Refs().Wires().Entry(aWireRefs.Value(0)).WireDefId; - const NCollection_DynamicArray& aCoEdgeRefs = - aGraph.Refs().CoEdges().IdsOf(aWireId); + const BRepGraph_WireId aWireId = aGraph.Refs().Wires().Entry(aWireRefs.Value(0)).ChildWireId; + const NCollection_LinearVector& aCoEdges = + aGraph.Topo().Wires().Relations(aWireId).CoEdgeIds; // Wire's direct children are CoEdges (no 1:1 collapse). int aCount = 0; @@ -747,7 +743,7 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_WireChildren_AreCoEdges) ++aCount; } - EXPECT_EQ(aCount, aCoEdgeRefs.Length()); + EXPECT_EQ(aCount, aCoEdges.Size()); } TEST(BRepGraph_ChildExplorerTest, DirectChildren_CompoundChildren_Basic) @@ -760,9 +756,8 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_CompoundChildren_Basic) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes29 = - BRepGraph_Builder::Add(aGraph, aComp); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes29 = aGraph.Shapes().Add(aComp); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_ChildExplorer anIt = makeDirectChildExplorer(aGraph, @@ -793,9 +788,8 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_ChainedTraversal_ParityWithRecu BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes30 = - BRepGraph_Builder::Add(aGraph, aComp); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes30 = aGraph.Shapes().Add(aComp); + ASSERT_FALSE(aGraph.IsEmpty()); NCollection_DataMap aExpectedLoc; NCollection_DataMap aExpectedOri; @@ -856,14 +850,15 @@ TEST(BRepGraph_ChildExplorerTest, Recursive_SharedProduct_ChildrenHaveDistinctCo { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes31 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes31 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_ProductId aPart = - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + aGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(aPart.IsValid()); - const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().CreateEmptyProduct(); ASSERT_TRUE(anAssembly.IsValid()); gp_Trsf aT1; @@ -872,9 +867,9 @@ TEST(BRepGraph_ChildExplorerTest, Recursive_SharedProduct_ChildrenHaveDistinctCo aT2.SetTranslation(gp_Vec(20.0, 0.0, 0.0)); const BRepGraph_OccurrenceId anOcc1 = - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location(aT1)); + aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location(aT1)); const BRepGraph_OccurrenceId anOcc2 = - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location(aT2)); + aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location(aT2)); ASSERT_TRUE(anOcc1.IsValid()); ASSERT_TRUE(anOcc2.IsValid()); @@ -901,37 +896,75 @@ TEST(BRepGraph_ChildExplorerTest, Recursive_SharedProduct_ChildrenHaveDistinctCo EXPECT_FALSE(aLoc1.IsEqual(aLoc2)); } +TEST(BRepGraph_ChildExplorerTest, Recursive_ProductOccurrenceChain_ReachesNestedOccurrences) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(); + const BRepGraph_ProductId aSubAssembly = aGraph.Editor().Products().Add(); + const BRepGraph_ProductId aTopAssembly = aGraph.Editor().Products().Add(); + ASSERT_TRUE(aPart.IsValid()); + ASSERT_TRUE(aSubAssembly.IsValid()); + ASSERT_TRUE(aTopAssembly.IsValid()); + + const BRepGraph_OccurrenceId aSubOccurrence = + aGraph.Editor().Products().Append(aTopAssembly, aSubAssembly, TopLoc_Location()); + const BRepGraph_OccurrenceId aPartOccurrence = + aGraph.Editor().Products().Append(aSubAssembly, aPart, TopLoc_Location()); + ASSERT_TRUE(aSubOccurrence.IsValid()); + ASSERT_TRUE(aPartOccurrence.IsValid()); + + bool hasSubOccurrence = false; + bool hasPartOccurrence = false; + int aCount = 0; + for (BRepGraph_ChildExplorer anIt(aGraph, aTopAssembly, BRepGraph_NodeId::Kind::Occurrence); + anIt.More(); + anIt.Next()) + { + hasSubOccurrence |= anIt.Current().DefId == BRepGraph_NodeId(aSubOccurrence); + hasPartOccurrence |= anIt.Current().DefId == BRepGraph_NodeId(aPartOccurrence); + ++aCount; + } + + EXPECT_EQ(aCount, 2); + EXPECT_TRUE(hasSubOccurrence); + EXPECT_TRUE(hasPartOccurrence); +} + TEST(BRepGraph_ChildExplorerTest, Recursive_ProductPartRootContext_ComposedWithOccurrence) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes32 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes32 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_ProductId aPart = - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + aGraph.Editor().Products().AppendDocumentRoot(aPart); ASSERT_TRUE(aPart.IsValid()); - const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(anAssembly.IsValid()); gp_Trsf aOccTrsf; aOccTrsf.SetTranslation(gp_Vec(10.0, 0.0, 0.0)); const BRepGraph_OccurrenceId anOcc = - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location(aOccTrsf)); + aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location(aOccTrsf)); ASSERT_TRUE(anOcc.IsValid()); gp_Trsf aRootTrsf; aRootTrsf.SetTranslation(gp_Vec(0.0, 20.0, 0.0)); // Set the root location on the topology-root occurrence ref of the part product. { - const BRepGraphInc::ProductDef& aPartDef = aGraph.Topo().Products().Definition(aPart); - for (const BRepGraph_OccurrenceRefId& aRefId : aPartDef.OccurrenceRefIds) + const BRepGraphInc::ProductRelations& aPartRelations = + aGraph.Topo().Products().Relations(aPart); + for (const BRepGraph_OccurrenceRefId& aRefId : aPartRelations.OccurrenceRefIds) { const BRepGraphInc::OccurrenceRef& aOccRef = aGraph.Refs().Occurrences().Entry(aRefId); const BRepGraphInc::OccurrenceDef& anOccDef = - aGraph.Topo().Occurrences().Definition(aOccRef.OccurrenceDefId); - if (BRepGraph_NodeId::IsTopologyKind(anOccDef.ChildDefId.NodeKind)) + aGraph.Topo().Occurrences().Definition(aOccRef.ChildOccurrenceId); + if (BRepGraph_NodeId::IsTopologyKind(anOccDef.ChildNodeId.NodeKind)) { BRepGraph_MutGuard aMutRef = aGraph.Editor().Occurrences().MutRef(aRefId); @@ -978,9 +1011,8 @@ TEST(BRepGraph_ChildExplorerTest, DirectChildren_HighFanout_DirectChildrenComple BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes33 = - BRepGraph_Builder::Add(aGraph, aComp); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes33 = aGraph.Shapes().Add(aComp); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_ChildExplorer anIt = makeDirectChildExplorer(aGraph, @@ -1009,9 +1041,8 @@ TEST(BRepGraph_ChildExplorerTest, HighFanout_CompletesAllChildren) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes34 = - BRepGraph_Builder::Add(aGraph, aComp); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes34 = aGraph.Shapes().Add(aComp); + ASSERT_FALSE(aGraph.IsEmpty()); int aFaceCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -1041,9 +1072,8 @@ TEST(BRepGraph_ChildExplorerTest, StructuredBindings_NodeInstance) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes35 = - BRepGraph_Builder::Add(aGraph, aComp); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes35 = aGraph.Shapes().Add(aComp); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_ChildExplorer anExp(aGraph, @@ -1065,9 +1095,9 @@ TEST(BRepGraph_ChildExplorerTest, RangeFor_NodeInstance) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes36 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes36 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (const BRepGraphInc::NodeInstance& aUsage : diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Compact_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Compact_Test.cxx index 306a62698a..bc6563352e 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Compact_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Compact_Test.cxx @@ -11,16 +11,19 @@ // 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 @@ -28,19 +31,27 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include -#include +#include +#include +#include +#include #include +#include #include #include #include +#include + #include namespace @@ -82,10 +93,17 @@ TopoDS_Compound makeBoxWithLooseEdge() int countHistoryRecordsByOp(const BRepGraph& theGraph, const TCollection_AsciiString& theOp) { - int aCount = 0; - for (size_t aRecIdx = 0; aRecIdx < theGraph.History().NbRecords(); ++aRecIdx) + const BRepGraph_LayerHistory* aHistory = + theGraph.LayerRegistry().Find().get(); + if (aHistory == nullptr) { - if (theGraph.History().Record(aRecIdx).OperationName == theOp) + return 0; + } + + int aCount = 0; + for (size_t aRecIdx = 0; aRecIdx < aHistory->NbRecords(); ++aRecIdx) + { + if (aHistory->Record(aRecIdx).OperationName == theOp) { ++aCount; } @@ -128,9 +146,8 @@ TEST(BRepGraph_CompactTest, NoRemovedNodes_Noop) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); const uint32_t aNbEdgesBefore = aGraph.Topo().Edges().Nb(); @@ -151,12 +168,12 @@ TEST(BRepGraph_CompactTest, AfterDeduplicate_RemovesNodes) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Run geometry dedup which replaces duplicate surface/curve handles directly. - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); BRepGraph_Compact::Options anOpts; anOpts.HistoryMode = false; @@ -173,37 +190,37 @@ TEST(BRepGraph_CompactTest, IndexDensity_NoGaps) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); // After compaction, there should be no removed defs. for (BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); aVertexId.IsValid(aGraph.Topo().Vertices().Nb()); ++aVertexId) { - EXPECT_FALSE(aGraph.Topo().Vertices().Definition(aVertexId).IsRemoved); + EXPECT_FALSE(aVertexId.IsRemoved(aGraph)); } for (BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); anEdgeId.IsValid(aGraph.Topo().Edges().Nb()); ++anEdgeId) { - EXPECT_FALSE(aGraph.Topo().Edges().Definition(anEdgeId).IsRemoved); + EXPECT_FALSE(anEdgeId.IsRemoved(aGraph)); } for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); aFaceId.IsValid(aGraph.Topo().Faces().Nb()); ++aFaceId) { - EXPECT_FALSE(aGraph.Topo().Faces().Definition(aFaceId).IsRemoved); + EXPECT_FALSE(aFaceId.IsRemoved(aGraph)); } for (BRepGraph_WireId aWireId = BRepGraph_WireId::Start(); aWireId.IsValid(aGraph.Topo().Wires().Nb()); ++aWireId) { - EXPECT_FALSE(aGraph.Topo().Wires().Definition(aWireId).IsRemoved); + EXPECT_FALSE(aWireId.IsRemoved(aGraph)); } } @@ -211,12 +228,12 @@ TEST(BRepGraph_CompactTest, CrossReferences_Valid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(aGraph); EXPECT_TRUE(aValResult.IsValid()); @@ -226,19 +243,19 @@ TEST(BRepGraph_CompactTest, HistoryMode_RecordsMapping) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Use full entity merge so that duplicate topology nodes are actually removed. // This ensures Compact must remap surviving indices and produces >= 1 record. BRepGraph_Deduplicate::Options aDedupOpts; aDedupOpts.MergeEntitiesWhenSafe = true; - (void)BRepGraph_Deduplicate::Perform(aGraph, aDedupOpts); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, aDedupOpts); BRepGraph_Compact::Options anOpts; anOpts.HistoryMode = true; - (void)BRepGraph_Compact::Perform(aGraph, anOpts); + std::ignore = BRepGraph_Compact::Perform(aGraph, anOpts); const int aNbRemapRecords = countHistoryRecordsByOp(aGraph, TCollection_AsciiString("Compact:Remap")); @@ -251,12 +268,12 @@ TEST(BRepGraph_CompactTest, FullPipeline_Deduplicate_Compact_Validate) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Full dedup (replaces duplicate handles directly on defs). - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // Compact. Without MergeEntitiesWhenSafe, no topology nodes are removed, // so NbNodesAfter == NbNodesBefore. @@ -275,9 +292,8 @@ TEST(BRepGraph_CompactTest, RemovalCompact_PreservesBounds_AndDoesNotGrowTopolog BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().CoEdges().Nb(), 0); ASSERT_GT(aGraph.Topo().Faces().Nb(), 2); @@ -304,14 +320,14 @@ TEST(BRepGraph_CompactTest, RemovalCompact_PreservesClosedTopologyAndValidShape) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, makeBoxWithLooseEdge()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(makeBoxWithLooseEdge()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_EdgeId aLooseEdge; for (BRepGraph_Iterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - if (aGraph.Topo().Edges().Faces(anEdgeIt.CurrentId()).IsEmpty()) + if (!aGraph.Topo().Edges().FacesOf(anEdgeIt.CurrentId()).More()) { aLooseEdge = anEdgeIt.CurrentId(); break; @@ -328,9 +344,9 @@ TEST(BRepGraph_CompactTest, RemovalCompact_PreservesClosedTopologyAndValidShape) for (BRepGraph_Iterator aWireIt(aGraph); aWireIt.More(); aWireIt.Next()) { - EXPECT_TRUE(aWireIt.Current().IsClosed); + EXPECT_TRUE(BRepGraph_Tool::Wire::IsClosed(aGraph, aWireIt.CurrentId())); } - EXPECT_TRUE(aGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).IsClosed); + EXPECT_TRUE(BRepGraph_Tool::Shell::IsClosed(aGraph, BRepGraph_ShellId::Start())); const TopoDS_Shape aRootShape = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(aGraph.RootProductIds().Value(0))); @@ -347,13 +363,13 @@ TEST(BRepGraph_CompactTest, AuditMode_PassesAfterDedupCompact) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Mode::Audit).IsValid()); - (void)BRepGraph_Deduplicate::Perform(aGraph); - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Mode::Audit).IsValid()); } @@ -362,14 +378,14 @@ TEST(BRepGraph_CompactTest, AuditMode_PassesAfterRemovalCompact) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, makeBoxWithLooseEdge()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = + aGraph.Shapes().Add(makeBoxWithLooseEdge()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_EdgeId aLooseEdge; for (BRepGraph_Iterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - if (aGraph.Topo().Edges().Faces(anEdgeIt.CurrentId()).IsEmpty()) + if (!aGraph.Topo().Edges().FacesOf(anEdgeIt.CurrentId()).More()) { aLooseEdge = anEdgeIt.CurrentId(); break; @@ -378,7 +394,7 @@ TEST(BRepGraph_CompactTest, AuditMode_PassesAfterRemovalCompact) ASSERT_TRUE(aLooseEdge.IsValid()); aGraph.Editor().Gen().RemoveNode(aLooseEdge); - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Mode::Audit).IsValid()); } @@ -387,15 +403,15 @@ TEST(BRepGraph_CompactTest, Compact_PreservesTopologyUIDs) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Collect the set of all original topology UIDs before dedup+compact. // Helper: record UIDs for all defs of a given kind. - auto collectUIDs = [&](BRepGraph_NodeId::Kind theKind, int theCount) { - NCollection_Map aMap; - for (int anIdx = 0; anIdx < theCount; ++anIdx) + auto collectUIDs = [&](BRepGraph_NodeId::Kind theKind, uint32_t theCount) { + NCollection_FlatMap aMap; + for (uint32_t anIdx = 0; anIdx < theCount; ++anIdx) { const BRepGraph_UID aUID = aGraph.UIDs().Of(BRepGraph_NodeId(theKind, anIdx)); EXPECT_TRUE(aUID.IsValid()); @@ -404,39 +420,39 @@ TEST(BRepGraph_CompactTest, Compact_PreservesTopologyUIDs) return aMap; }; - const NCollection_Map anOrigVertexUIDs = + const NCollection_FlatMap anOrigVertexUIDs = collectUIDs(BRepGraph_NodeId::Kind::Vertex, aGraph.Topo().Vertices().Nb()); - const NCollection_Map anOrigEdgeUIDs = + const NCollection_FlatMap anOrigEdgeUIDs = collectUIDs(BRepGraph_NodeId::Kind::Edge, aGraph.Topo().Edges().Nb()); - const NCollection_Map anOrigWireUIDs = + const NCollection_FlatMap anOrigWireUIDs = collectUIDs(BRepGraph_NodeId::Kind::Wire, aGraph.Topo().Wires().Nb()); - const NCollection_Map anOrigFaceUIDs = + const NCollection_FlatMap anOrigFaceUIDs = collectUIDs(BRepGraph_NodeId::Kind::Face, aGraph.Topo().Faces().Nb()); - const NCollection_Map anOrigShellUIDs = + const NCollection_FlatMap anOrigShellUIDs = collectUIDs(BRepGraph_NodeId::Kind::Shell, aGraph.Topo().Shells().Nb()); - const NCollection_Map anOrigSolidUIDs = + const NCollection_FlatMap anOrigSolidUIDs = collectUIDs(BRepGraph_NodeId::Kind::Solid, aGraph.Topo().Solids().Nb()); - const NCollection_Map anOrigCompoundUIDs = + const NCollection_FlatMap anOrigCompoundUIDs = collectUIDs(BRepGraph_NodeId::Kind::Compound, aGraph.Topo().Compounds().Nb()); - const NCollection_Map anOrigCompSolidUIDs = + const NCollection_FlatMap anOrigCompSolidUIDs = collectUIDs(BRepGraph_NodeId::Kind::CompSolid, aGraph.Topo().CompSolids().Nb()); // Geometry is now stored inline on defs; no separate geometry UIDs to collect. const uint32_t aGenBefore = aGraph.UIDs().Generation(); // Run dedup + compact. - (void)BRepGraph_Deduplicate::Perform(aGraph); - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); // Generation must be preserved across compact. EXPECT_EQ(aGraph.UIDs().Generation(), aGenBefore); // Helper: verify surviving defs of a kind retain original UIDs. - auto verifyUIDs = [&](BRepGraph_NodeId::Kind theKind, - int theCount, - const NCollection_Map& theOriginals, - const char* theLabel) { - for (int anIdx = 0; anIdx < theCount; ++anIdx) + auto verifyUIDs = [&](BRepGraph_NodeId::Kind theKind, + uint32_t theCount, + const NCollection_FlatMap& theOriginals, + const char* theLabel) { + for (uint32_t anIdx = 0; anIdx < theCount; ++anIdx) { const BRepGraph_NodeId aNewId(theKind, anIdx); const BRepGraph_UID aNewUID = aGraph.UIDs().Of(aNewId); @@ -466,7 +482,27 @@ TEST(BRepGraph_CompactTest, Compact_PreservesTopologyUIDs) anOrigCompSolidUIDs, "CompSolid"); - // Geometry is now stored inline on defs; no separate geometry UIDs to verify. + ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); +} + +TEST(BRepGraph_CompactTest, Compact_PreservesRepresentationUIDs) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceSurfaceRepId aSurfaceRepId = + aGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).SurfaceRepId; + ASSERT_TRUE(aSurfaceRepId.IsValid()); + + std::ignore = BRepGraph_Compact::Perform(aGraph); + + const BRepGraph_FaceSurfaceRepId aCompactedRepId = + aGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).SurfaceRepId; + EXPECT_TRUE(aCompactedRepId.IsValid()); + EXPECT_EQ(aCompactedRepId, aSurfaceRepId); } TEST(BRepGraph_CompactTest, OwnGen_SurvivesCompact) @@ -476,9 +512,9 @@ TEST(BRepGraph_CompactTest, OwnGen_SurvivesCompact) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Mutate edge 0 twice so OwnGen == THE_EXPECTED_OWN_GEN. aGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.1); @@ -487,8 +523,8 @@ TEST(BRepGraph_CompactTest, OwnGen_SurvivesCompact) THE_EXPECTED_OWN_GEN); // Run dedup + compact. - (void)BRepGraph_Deduplicate::Perform(aGraph); - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); // Edge 0 may have been remapped. Find the edge that carries the mutated // tolerance and verify both the tolerance value and OwnGen are preserved. @@ -516,9 +552,8 @@ TEST(BRepGraph_CompactTest, UIDRoundTrip_AfterCompaction) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Faces().Nb(), 3); ASSERT_GE(aGraph.Topo().Edges().Nb(), 3); @@ -566,24 +601,25 @@ TEST(BRepGraph_CompactTest, CoEdgeUID_AfterCompaction) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Wires().Nb(), 1); // Capture a CoEdge from Face 1 (Face 0 will be removed before compact). ASSERT_GE(aGraph.Topo().Faces().Nb(), 2); BRepGraph_CoEdgeId aCoEdgeId; { - const BRepGraphInc::FaceDef& aFace1 = aGraph.Topo().Faces().Definition(BRepGraph_FaceId(1)); - ASSERT_FALSE(aFace1.WireRefIds.IsEmpty()); + const BRepGraphInc::FaceRelations& aFace1Relations = + aGraph.Topo().Faces().Relations(BRepGraph_FaceId(1)); + ASSERT_FALSE(aFace1Relations.WireRefIds.IsEmpty()); - const BRepGraphInc::WireRef& aWireRef = aGraph.Refs().Wires().Entry(aFace1.WireRefIds.First()); - const BRepGraphInc::WireDef& aWire = aGraph.Topo().Wires().Definition(aWireRef.WireDefId); - ASSERT_FALSE(aWire.CoEdgeRefIds.IsEmpty()); - - const BRepGraphInc::CoEdgeRef& aRef = aGraph.Refs().CoEdges().Entry(aWire.CoEdgeRefIds.First()); - aCoEdgeId = aRef.CoEdgeDefId; + const BRepGraphInc::WireRef& aWireRef = + aGraph.Refs().Wires().Entry(aFace1Relations.WireRefIds.First()); + const BRepGraphInc::WireRelations& aWireRelations = + aGraph.Topo().Wires().Relations(aWireRef.ChildWireId); + ASSERT_FALSE(aWireRelations.CoEdgeIds.IsEmpty()); + aCoEdgeId = aWireRelations.CoEdgeIds.First(); } ASSERT_TRUE(aCoEdgeId.IsValid()) << "No surviving CoEdge found in the graph"; @@ -592,7 +628,8 @@ TEST(BRepGraph_CompactTest, CoEdgeUID_AfterCompaction) // Remove one face and compact to trigger index remapping. aGraph.Editor().Gen().RemoveNode(BRepGraph_FaceId::Start()); - (void)BRepGraph_Compact::Perform(aGraph); + const BRepGraph_Compact::Result aCompactResult = BRepGraph_Compact::Perform(aGraph); + ASSERT_GT(aCompactResult.NbNodesBefore, aCompactResult.NbNodesAfter); // CoEdge UID must resolve to a valid CoEdgeId after compact. const BRepGraph_NodeId aResolved = aGraph.UIDs().NodeIdFrom(aCoEdgeUID); @@ -604,14 +641,14 @@ TEST(BRepGraph_CompactTest, CoEdgeUID_AfterCompaction) TEST(BRepGraph_CompactTest, UIDRoundTrip_RefUIDs_AfterCompaction) { // Verify that all transferred RefUID kinds survive compaction. - // Checks VertexRef, CoEdgeRef, WireRef, FaceRef, ShellRef (present in a box). + // Checks VertexRef, WireRef, FaceRef, ShellRef plus direct CoEdge node identity. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // VertexRef - from Edge 0 start vertex ref. BRepGraph_VertexRefId aVertexRefId; @@ -624,17 +661,20 @@ TEST(BRepGraph_CompactTest, UIDRoundTrip_RefUIDs_AfterCompaction) } } - // CoEdgeRef - from Face 1 wire (Face 0 will be removed before compact). - BRepGraph_CoEdgeRefId aCoEdgeRefId; + // CoEdge - from Face 1 wire (Face 0 will be removed before compact). + BRepGraph_CoEdgeId aCoEdgeId; { - const BRepGraphInc::FaceDef& aFace = aGraph.Topo().Faces().Definition(BRepGraph_FaceId(1)); - if (!aFace.WireRefIds.IsEmpty()) + const BRepGraphInc::FaceRelations& aFaceRelations = + aGraph.Topo().Faces().Relations(BRepGraph_FaceId(1)); + if (!aFaceRelations.WireRefIds.IsEmpty()) { - const BRepGraphInc::WireRef& aWireRef = aGraph.Refs().Wires().Entry(aFace.WireRefIds.First()); - const BRepGraphInc::WireDef& aWire = aGraph.Topo().Wires().Definition(aWireRef.WireDefId); - if (!aWire.CoEdgeRefIds.IsEmpty()) + const BRepGraphInc::WireRef& aWireRef = + aGraph.Refs().Wires().Entry(aFaceRelations.WireRefIds.First()); + const BRepGraphInc::WireRelations& aWireRelations = + aGraph.Topo().Wires().Relations(aWireRef.ChildWireId); + if (!aWireRelations.CoEdgeIds.IsEmpty()) { - aCoEdgeRefId = aWire.CoEdgeRefIds.First(); + aCoEdgeId = aWireRelations.CoEdgeIds.First(); } } } @@ -643,10 +683,11 @@ TEST(BRepGraph_CompactTest, UIDRoundTrip_RefUIDs_AfterCompaction) BRepGraph_WireRefId aWireRefId; ASSERT_GE(aGraph.Topo().Faces().Nb(), 2); { - const BRepGraphInc::FaceDef& aFace = aGraph.Topo().Faces().Definition(BRepGraph_FaceId(1)); - if (!aFace.WireRefIds.IsEmpty()) + const BRepGraphInc::FaceRelations& aFaceRelations = + aGraph.Topo().Faces().Relations(BRepGraph_FaceId(1)); + if (!aFaceRelations.WireRefIds.IsEmpty()) { - aWireRefId = aFace.WireRefIds.First(); + aWireRefId = aFaceRelations.WireRefIds.First(); } } @@ -659,7 +700,7 @@ TEST(BRepGraph_CompactTest, UIDRoundTrip_RefUIDs_AfterCompaction) { const BRepGraphInc::FaceRef& aFR = aGraph.Refs().Faces().Entry(aRefIt.CurrentId()); // Skip Face 0 ref - that face will be removed before compact. - if (aFR.FaceDefId.Index != 0) + if (aFR.ChildFaceId.Index != 0) { aFaceRefId = aRefIt.CurrentId(); break; @@ -691,14 +732,15 @@ TEST(BRepGraph_CompactTest, UIDRoundTrip_RefUIDs_AfterCompaction) } // Capture RefUIDs before compact. - BRepGraph_RefUID aVertexRefUID, aCoEdgeRefUID, aWireRefUID, aFaceRefUID, aShellRefUID; + BRepGraph_RefUID aVertexRefUID, aWireRefUID, aFaceRefUID, aShellRefUID; + BRepGraph_UID aCoEdgeUID; if (aVertexRefId.IsValid()) { aVertexRefUID = aGraph.UIDs().Of(aVertexRefId); } - if (aCoEdgeRefId.IsValid()) + if (aCoEdgeId.IsValid()) { - aCoEdgeRefUID = aGraph.UIDs().Of(aCoEdgeRefId); + aCoEdgeUID = aGraph.UIDs().Of(aCoEdgeId); } if (aWireRefId.IsValid()) { @@ -715,7 +757,7 @@ TEST(BRepGraph_CompactTest, UIDRoundTrip_RefUIDs_AfterCompaction) // Remove one face to trigger compaction. aGraph.Editor().Gen().RemoveNode(BRepGraph_FaceId::Start()); - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); // Each surviving RefUID must resolve to a valid RefId of the correct kind. if (aVertexRefUID.IsValid()) @@ -725,12 +767,12 @@ TEST(BRepGraph_CompactTest, UIDRoundTrip_RefUIDs_AfterCompaction) EXPECT_EQ(aResolved.RefKind, BRepGraph_RefId::Kind::Vertex) << "VertexRef UID resolved to wrong kind"; } - if (aCoEdgeRefUID.IsValid()) + if (aCoEdgeUID.IsValid()) { - const BRepGraph_RefId aResolved = aGraph.UIDs().RefIdFrom(aCoEdgeRefUID); - EXPECT_TRUE(aResolved.IsValid()) << "CoEdgeRef UID lost after compaction"; - EXPECT_EQ(aResolved.RefKind, BRepGraph_RefId::Kind::CoEdge) - << "CoEdgeRef UID resolved to wrong kind"; + const BRepGraph_NodeId aResolved = aGraph.UIDs().NodeIdFrom(aCoEdgeUID); + EXPECT_TRUE(aResolved.IsValid()) << "CoEdge UID lost after compaction"; + EXPECT_EQ(aResolved.NodeKind, BRepGraph_NodeId::Kind::CoEdge) + << "CoEdge UID resolved to wrong kind"; } if (aWireRefUID.IsValid()) { @@ -761,15 +803,14 @@ TEST(BRepGraph_CompactTest, FindNodeStillWorksAfterCompact) { // Regression for Bug B2: BRepGraph_Compact must preserve the TShape-to-NodeId // bindings so that BRepGraph::Shapes().FindNode() / HasNode() still resolves - // original BRepGraph_Builder::Add()-time shapes after compaction. + // original BRepGraph::ShapesView::Add()-time shapes after compaction. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Pick one face from the original build input. TopoDS_Shape aFace; @@ -783,7 +824,7 @@ TEST(BRepGraph_CompactTest, FindNodeStillWorksAfterCompact) const BRepGraph_NodeId aNodeIdBefore = aGraph.Shapes().FindNode(aFace); ASSERT_TRUE(aNodeIdBefore.IsValid()) << "FindNode returned invalid node before compact"; - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); // After compact the TShape binding must survive so the original face is still locatable. EXPECT_TRUE(aGraph.Shapes().HasNode(aFace)) @@ -792,3 +833,455 @@ TEST(BRepGraph_CompactTest, FindNodeStillWorksAfterCompact) EXPECT_TRUE(aNodeIdAfter.IsValid()) << "FindNode returned invalid node after compact - TShape bindings were lost"; } + +TEST(BRepGraph_CompactTest, OwnGen_PreservedForAllTopologyKindsAfterCompact) +{ + constexpr uint32_t THE_EXPECTED_GEN = 3; + + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); + + // Mutate one entity of each topology kind to set a known OwnGen value. + // After compact, the OwnGen should survive on the corresponding remapped entity. + + // Vertex: set point + aGraph.Editor().Vertices().SetPoint(BRepGraph_VertexId::Start(), gp_Pnt(11.0, 21.0, 31.0)); + aGraph.Editor().Vertices().SetPoint(BRepGraph_VertexId::Start(), gp_Pnt(12.0, 22.0, 32.0)); + aGraph.Editor().Vertices().SetPoint(BRepGraph_VertexId::Start(), gp_Pnt(13.0, 23.0, 33.0)); + + // Edge: set tolerance + aGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.1); + aGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.2); + aGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.3); + + // Wire: MarkDirty directly + aGraph.Editor().Wires().Mut(BRepGraph_WireId::Start()).MarkDirty(); + aGraph.Editor().Wires().Mut(BRepGraph_WireId::Start()).MarkDirty(); + aGraph.Editor().Wires().Mut(BRepGraph_WireId::Start()).MarkDirty(); + + // Face: set tolerance + aGraph.Editor().Faces().SetTolerance(BRepGraph_FaceId::Start(), 0.01); + aGraph.Editor().Faces().SetTolerance(BRepGraph_FaceId::Start(), 0.02); + aGraph.Editor().Faces().SetTolerance(BRepGraph_FaceId::Start(), 0.03); + + // Shell: MarkDirty + aGraph.Editor().Shells().Mut(BRepGraph_ShellId::Start()).MarkDirty(); + aGraph.Editor().Shells().Mut(BRepGraph_ShellId::Start()).MarkDirty(); + aGraph.Editor().Shells().Mut(BRepGraph_ShellId::Start()).MarkDirty(); + + // Solid: MarkDirty + aGraph.Editor().Solids().Mut(BRepGraph_SolidId::Start()).MarkDirty(); + aGraph.Editor().Solids().Mut(BRepGraph_SolidId::Start()).MarkDirty(); + aGraph.Editor().Solids().Mut(BRepGraph_SolidId::Start()).MarkDirty(); + + // Verify OwnGen before compact. + EXPECT_EQ(aGraph.Topo().Vertices().Definition(BRepGraph_VertexId::Start()).OwnGen, + THE_EXPECTED_GEN); + EXPECT_EQ(aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, THE_EXPECTED_GEN); + EXPECT_EQ(aGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).OwnGen, THE_EXPECTED_GEN); + EXPECT_EQ(aGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).OwnGen, THE_EXPECTED_GEN); + EXPECT_EQ(aGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).OwnGen, THE_EXPECTED_GEN); + EXPECT_EQ(aGraph.Topo().Solids().Definition(BRepGraph_SolidId::Start()).OwnGen, THE_EXPECTED_GEN); + + std::ignore = BRepGraph_Compact::Perform(aGraph); + + // After compact (no removals, just rebuild), all OwnGen values should survive. + // Since nothing was removed, the indices should remain at Start(). + EXPECT_EQ(aGraph.Topo().Vertices().Definition(BRepGraph_VertexId::Start()).OwnGen, + THE_EXPECTED_GEN) + << "Vertex OwnGen not preserved after compact"; + EXPECT_EQ(aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).OwnGen, THE_EXPECTED_GEN) + << "Edge OwnGen not preserved after compact"; + EXPECT_EQ(aGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).OwnGen, THE_EXPECTED_GEN) + << "Wire OwnGen not preserved after compact"; + EXPECT_EQ(aGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).OwnGen, THE_EXPECTED_GEN) + << "Face OwnGen not preserved after compact"; + EXPECT_EQ(aGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).OwnGen, THE_EXPECTED_GEN) + << "Shell OwnGen not preserved after compact"; + EXPECT_EQ(aGraph.Topo().Solids().Definition(BRepGraph_SolidId::Start()).OwnGen, THE_EXPECTED_GEN) + << "Solid OwnGen not preserved after compact"; +} + +TEST(BRepGraph_CompactTest, Compact_PreservesDeletedItemUidHistory) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + occ::handle aHistory = + aGraph.LayerRegistry().Ensure(); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_ItemUID aFaceUid = aGraph.UIDs().Of(BRepGraph_ItemId(aFaceId)); + ASSERT_TRUE(aFaceUid.IsValid()); + + aGraph.Editor().Gen().RemoveNode(aFaceId); + ASSERT_TRUE(aHistory->IsDeleted(aFaceUid)); + ASSERT_TRUE(aHistory->HasKnownInput(aFaceUid)); + + std::ignore = BRepGraph_Compact::Perform(aGraph); + + EXPECT_TRUE(aHistory->IsDeleted(aFaceUid)); + EXPECT_TRUE(aHistory->HasKnownInput(aFaceUid)); + EXPECT_TRUE(aHistory->DeletedItemUids().Contains(aFaceUid)); +} + +TEST(BRepGraph_CompactTest, Compact_PreservesItemUidHistoryMappings) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + ASSERT_GE(aGraph.Topo().Faces().Nb(), 2u); + + occ::handle aHistory = + aGraph.LayerRegistry().Ensure(); + const BRepGraph_FaceId aOriginalFace = BRepGraph_FaceId::Start(); + const BRepGraph_FaceId aReplacementFace(1); + const BRepGraph_ItemUID anOriginalUid = aGraph.UIDs().Of(BRepGraph_ItemId(aOriginalFace)); + const BRepGraph_ItemUID aReplacementUid = aGraph.UIDs().Of(BRepGraph_ItemId(aReplacementFace)); + ASSERT_TRUE(anOriginalUid.IsValid()); + ASSERT_TRUE(aReplacementUid.IsValid()); + + NCollection_LinearVector aReplacements; + aReplacements.Append(aReplacementUid); + aHistory->RecordItemUid("CompactItemHistory", anOriginalUid, aReplacements.ToArray1()); + ASSERT_TRUE(aHistory->HasKnownInput(anOriginalUid)); + + std::ignore = BRepGraph_Compact::Perform(aGraph); + + const NCollection_LinearVector* aModified = + aHistory->FindModified(anOriginalUid); + ASSERT_NE(aModified, nullptr); + ASSERT_EQ(aModified->Size(), size_t(1)); + EXPECT_EQ(aModified->Value(0), aReplacementUid); + EXPECT_TRUE(aHistory->HasKnownInput(anOriginalUid)); +} + +TEST(BRepGraph_CompactTest, Compact_AfterDedupMerge_NoBoundsErrors) +{ + // Build 3 identical boxes via deep copy so dedup can merge topology nodes. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + + BRepBuilderAPI_Copy aCopy1(aBoxMaker.Shape(), true); + BRepBuilderAPI_Copy aCopy2(aBoxMaker.Shape(), true); + BRepBuilderAPI_Copy aCopy3(aBoxMaker.Shape(), true); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aCopy1.Shape()); + aBuilder.Add(aCompound, aCopy2.Shape()); + aBuilder.Add(aCompound, aCopy3.Shape()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_Deduplicate::Options aDedupOpts; + aDedupOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, aDedupOpts); + + const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_NE(aRes.NbNodesAfter, 0u); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8u); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12u); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6u); + + const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aValResult.IsValid()); +} + +TEST(BRepGraph_CompactTest, Compact_FaceWithInnerWires_WireCountPreserved) +{ + // Build a box with a cylindrical hole so that some faces have inner wires. + BRepPrimAPI_MakeBox aBoxMaker(20.0, 20.0, 20.0); + + gp_Ax2 anAxes(gp_Pnt(10.0, 10.0, 0.0), gp::DZ()); + BRepPrimAPI_MakeCylinder aCylMaker(anAxes, 4.0, 25.0); + + BRepAlgoAPI_Cut aCut(aBoxMaker.Shape(), aCylMaker.Shape()); + ASSERT_TRUE(aCut.IsDone()); + + BRepBuilderAPI_Copy aCopy1(aCut.Shape(), true); + BRepBuilderAPI_Copy aCopy2(aCut.Shape(), true); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aCopy1.Shape()); + aBuilder.Add(aCompound, aCopy2.Shape()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + + // Verify before dedup that the shape produces faces with inner wires (WireRefIds > 1). + bool aHasInnerWires = false; + for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + aFaceId.IsValid(aGraph.Topo().Faces().Nb()); + ++aFaceId) + { + if (aGraph.Topo().Faces().Relations(aFaceId).WireRefIds.Size() > 1) + { + aHasInnerWires = true; + break; + } + } + ASSERT_TRUE(aHasInnerWires); + + BRepGraph_Deduplicate::Options aDedupOpts; + aDedupOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, aDedupOpts); + + const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_NE(aRes.NbNodesAfter, 0u); + + const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aValResult.IsValid()); +} + +TEST(BRepGraph_CompactTest, HistorySurvivesCompactWithRemappedIds) +{ + BRepPrimAPI_MakeBox aBoxMaker1(10.0, 20.0, 30.0); + BRepPrimAPI_MakeBox aBoxMaker2(10.0, 20.0, 30.0); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aBoxMaker1.Shape()); + aBuilder.Add(aCompound, aBoxMaker2.Shape()); + + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + + aGraph.LayerRegistry().Ensure()->SetEnabled(true); + const BRepGraph_NodeId aVertexNode = BRepGraph_VertexId::Start(); + NCollection_LinearVector aRepl; + aRepl.Append(aVertexNode); + aGraph.LayerRegistry().Ensure()->Record( + TCollection_AsciiString("Test:Modify"), + aVertexNode, + aRepl.ToArray1()); + const size_t aNbBefore = aGraph.LayerRegistry().Ensure()->NbRecords(); + ASSERT_GT(aNbBefore, 0u); + + BRepGraph_Deduplicate::Options aDedupOpts; + aDedupOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, aDedupOpts); + + BRepGraph_Compact::Options aCompactOpts; + aCompactOpts.HistoryMode = true; + std::ignore = BRepGraph_Compact::Perform(aGraph, aCompactOpts); + + EXPECT_GE(aGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore + 1); +} + +namespace +{ +class StubMeshDriver : public BRepGraph_CacheMesh::Driver +{ +public: + DEFINE_STANDARD_RTTI_INLINE(StubMeshDriver, BRepGraph_CacheMesh::Driver) + + explicit StubMeshDriver(uint64_t theHash) + : myHash(theHash) + { + } + + const Standard_GUID& ID() const override + { + static const Standard_GUID THE_ID("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); + return THE_ID; + } + + uint64_t RecipeHash() const override { return myHash; } + + bool Fill(BRepGraph&, + BRepGraph_CacheMesh::SlotId, + const BRepGraph_CacheMesh::DirtySet&, + const Message_ProgressRange&) override + { + return true; + } + +private: + uint64_t myHash; +}; +} // namespace + +TEST(BRepGraph_CompactTest, CacheMesh_DriverSurvivesCompact) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + // Register CacheMesh, register a driver, and populate a face entry. + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + ASSERT_FALSE(aCache.IsNull()); + + constexpr uint64_t THE_RECIPE_HASH = 42; + auto aDriver = new StubMeshDriver(THE_RECIPE_HASH); + aCache->RegisterDriver(BRepGraph_CacheMesh::DefaultDisplaySlot, aDriver); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const occ::handle aTri = new Poly_Triangulation(3, 1, false); + aTri->SetNode(1, gp_Pnt(0.0, 0.0, 0.0)); + aTri->SetNode(2, gp_Pnt(1.0, 0.0, 0.0)); + aTri->SetNode(3, gp_Pnt(0.0, 1.0, 0.0)); + aTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTri); + ASSERT_NE(aCache->FindFaceMesh(aFaceId), nullptr); + + // Verify driver is registered before compact. + ASSERT_FALSE(aCache->DriverOf(BRepGraph_CacheMesh::DefaultDisplaySlot).IsNull()); + ASSERT_TRUE(aCache->State(BRepGraph_CacheMesh::DefaultDisplaySlot).HasDriver); + + // Compact. + const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbRemovedVertices, 0u); + + // Cache object must survive compaction. + occ::handle aCacheAfter = aGraph.CacheRegistry().Find(); + ASSERT_FALSE(aCacheAfter.IsNull()); + EXPECT_EQ(aCache.get(), aCacheAfter.get()) << "Cache handle must be the same object"; + + // Driver must survive compaction. + const occ::handle& aDriverAfter = + aCacheAfter->DriverOf(BRepGraph_CacheMesh::DefaultDisplaySlot); + ASSERT_FALSE(aDriverAfter.IsNull()); + EXPECT_EQ(aDriverAfter->RecipeHash(), THE_RECIPE_HASH); + EXPECT_TRUE(aCacheAfter->State(BRepGraph_CacheMesh::DefaultDisplaySlot).HasDriver); + + // No removed nodes - compact short-circuits, cache representation is untouched. + EXPECT_NE(aCacheAfter->FindFaceMesh(aFaceId), nullptr) + << "Cache entry must survive when compact short-circuits (no removed nodes)"; +} + +TEST(BRepGraph_CompactTest, CacheMesh_DriverSurvivesCompactWithDedup) +{ + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); + + // Register CacheMesh with driver and populate entries for all faces. + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + ASSERT_FALSE(aCache.IsNull()); + + constexpr uint64_t THE_RECIPE_HASH = 99; + auto aDriver = new StubMeshDriver(THE_RECIPE_HASH); + aCache->RegisterDriver(BRepGraph_CacheMesh::DefaultDisplaySlot, aDriver); + + const occ::handle aTri = new Poly_Triangulation(3, 1, false); + aTri->SetNode(1, gp_Pnt(0.0, 0.0, 0.0)); + aTri->SetNode(2, gp_Pnt(1.0, 0.0, 0.0)); + aTri->SetNode(3, gp_Pnt(0.0, 1.0, 0.0)); + aTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); + + const uint32_t aNbFaces = aGraph.Topo().Faces().Nb(); + ASSERT_GT(aNbFaces, 0u); + for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); aFaceId.IsValid(aNbFaces); ++aFaceId) + { + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTri); + } + + // Deduplicate + Compact. + BRepGraph_Deduplicate::Options aDedupOpts; + aDedupOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, aDedupOpts); + + const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_GT(aRes.NbRemovedVertices + aRes.NbRemovedEdges + aRes.NbRemovedFaces, 0u) + << "Deduplicate should have removed some nodes"; + + // Cache object and driver must survive. + occ::handle aCacheAfter = aGraph.CacheRegistry().Find(); + ASSERT_FALSE(aCacheAfter.IsNull()); + EXPECT_EQ(aCache.get(), aCacheAfter.get()); + + const occ::handle& aDriverAfter = + aCacheAfter->DriverOf(BRepGraph_CacheMesh::DefaultDisplaySlot); + ASSERT_FALSE(aDriverAfter.IsNull()); + EXPECT_EQ(aDriverAfter->RecipeHash(), THE_RECIPE_HASH); + + // Cache representation is cleared - no entries should remain. + for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + aFaceId.IsValid(aGraph.Topo().Faces().Nb()); + ++aFaceId) + { + EXPECT_EQ(aCacheAfter->FindFaceMesh(aFaceId), nullptr) + << "Cache representation must be cleared after compact"; + } +} + +TEST(BRepGraph_CompactTest, CacheMesh_CanCopyFreshEntriesThroughCompact) +{ + BRepGraph aGraph; + std::ignore = aGraph.Shapes().Add(makeTwoCopiedFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); + + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + ASSERT_FALSE(aCache.IsNull()); + + constexpr uint64_t THE_RECIPE_HASH = 77; + auto aDriver = new StubMeshDriver(THE_RECIPE_HASH); + aCache->RegisterDriver(BRepGraph_CacheMesh::DefaultDisplaySlot, aDriver); + + const occ::handle aTri = new Poly_Triangulation(3, 1, false); + aTri->SetNode(1, gp_Pnt(0.0, 0.0, 0.0)); + aTri->SetNode(2, gp_Pnt(1.0, 0.0, 0.0)); + aTri->SetNode(3, gp_Pnt(0.0, 1.0, 0.0)); + aTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); + + BRepGraph_Deduplicate::Options aDedupOpts; + aDedupOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, aDedupOpts); + + const uint32_t aNbFaces = aGraph.Topo().Faces().Nb(); + ASSERT_GT(aNbFaces, 0u); + for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); aFaceId.IsValid(aNbFaces); ++aFaceId) + { + if (aFaceId.IsRemoved(aGraph)) + { + continue; + } + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTri); + ASSERT_NE(aCache->FindFaceMesh(aFaceId), nullptr); + } + + BRepGraph_Compact::Options aCompactOpts; + aCompactOpts.CacheMode = BRepGraph_Compact::Options::CachePolicy::CopyFresh; + const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph, aCompactOpts); + EXPECT_GT(aRes.NbRemovedVertices + aRes.NbRemovedEdges + aRes.NbRemovedFaces, 0u); + + occ::handle aCacheAfter = aGraph.CacheRegistry().Find(); + ASSERT_FALSE(aCacheAfter.IsNull()); + EXPECT_NE(aCacheAfter.get(), aCache.get()); + ASSERT_FALSE(aCacheAfter->DriverOf(BRepGraph_CacheMesh::DefaultDisplaySlot).IsNull()); + EXPECT_EQ(aCacheAfter->DriverOf(BRepGraph_CacheMesh::DefaultDisplaySlot)->RecipeHash(), + THE_RECIPE_HASH); + + for (BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + aFaceId.IsValid(aGraph.Topo().Faces().Nb()); + ++aFaceId) + { + const BRepGraph_CacheMesh::FaceMeshEntry* anEntry = aCacheAfter->FindFaceMesh(aFaceId); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Triangulation.get(), aTri.get()); + } +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Convenience_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Convenience_Test.cxx index 9217688197..d269ccb253 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Convenience_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Convenience_Test.cxx @@ -14,12 +14,12 @@ #include #include #include -#include +#include #include #include #include #include -#include +#include #include #include #include @@ -35,8 +35,7 @@ protected: BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBox); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = myGraph.Shapes().Add(aBox); } BRepGraph myGraph; @@ -90,30 +89,27 @@ TEST_F(BRepGraph_ConvenienceTest, NodeId_Factories_EqualToConstructor) TEST_F(BRepGraph_ConvenienceTest, EdgeDef_StartVertex_Valid) { ASSERT_GT(myGraph.Topo().Edges().Nb(), 0); - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraphInc::VertexRef& aStart = BRepGraph_Tool::Edge::StartVertexRef(myGraph, anEdgeId); - EXPECT_TRUE(aStart.VertexDefId.IsValid()); + const BRepGraph_EdgeId anEdgeId(0); + const BRepGraph_VertexRefId aStart = BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdgeId); + EXPECT_TRUE(aStart.IsValid()); } TEST_F(BRepGraph_ConvenienceTest, EdgeDef_EndVertex_Valid) { ASSERT_GT(myGraph.Topo().Edges().Nb(), 0); - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraphInc::VertexRef& anEnd = BRepGraph_Tool::Edge::EndVertexRef(myGraph, anEdgeId); - EXPECT_TRUE(anEnd.VertexDefId.IsValid()); + const BRepGraph_EdgeId anEdgeId(0); + const BRepGraph_VertexRefId anEnd = BRepGraph_Tool::Edge::EndVertexId(myGraph, anEdgeId); + EXPECT_TRUE(anEnd.IsValid()); } TEST_F(BRepGraph_ConvenienceTest, EdgeDef_StartEnd_DifferForNonClosed) { ASSERT_GT(myGraph.Topo().Edges().Nb(), 0); - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraphInc::EdgeDef& anEdge = myGraph.Topo().Edges().Definition(anEdgeId); - if (!anEdge.IsClosed) + const BRepGraph_EdgeId anEdgeId(0); + if (!BRepGraph_Tool::Edge::IsClosed(myGraph, anEdgeId)) { - const BRepGraph_VertexId aStartId = - BRepGraph_Tool::Edge::StartVertexRef(myGraph, anEdgeId).VertexDefId; - const BRepGraph_VertexId anEndId = - BRepGraph_Tool::Edge::EndVertexRef(myGraph, anEdgeId).VertexDefId; + const BRepGraph_VertexRefId aStartId = BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdgeId); + const BRepGraph_VertexRefId anEndId = BRepGraph_Tool::Edge::EndVertexId(myGraph, anEdgeId); EXPECT_NE(aStartId, anEndId); } } @@ -127,11 +123,42 @@ TEST_F(BRepGraph_ConvenienceTest, EdgeDef_RefIds_AreValid) EXPECT_TRUE(anEdge.EndVertexRefId.IsValid()); } +TEST_F(BRepGraph_ConvenienceTest, EdgeOps_FindByVertices_FindsDirectedEdge) +{ + ASSERT_GT(myGraph.Topo().Edges().Nb(), 0); + const BRepGraph_EdgeId anEdgeId(0); + const BRepGraph_VertexRefId aStartRef = BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdgeId); + const BRepGraph_VertexRefId anEndRef = BRepGraph_Tool::Edge::EndVertexId(myGraph, anEdgeId); + const BRepGraph_Tool::VertexUsage aStart = BRepGraph_Tool::Vertex::Usage(myGraph, aStartRef); + const BRepGraph_Tool::VertexUsage anEnd = BRepGraph_Tool::Vertex::Usage(myGraph, anEndRef); + ASSERT_TRUE(aStart.IsValid()); + ASSERT_TRUE(anEnd.IsValid()); + + EXPECT_EQ(BRepGraph_Tool::Edge::FindByVertices(myGraph, aStart.DefId, anEnd.DefId), anEdgeId); +} + +TEST_F(BRepGraph_ConvenienceTest, EdgeOps_FindByVertices_ReverseRequiresExplicitFlag) +{ + ASSERT_GT(myGraph.Topo().Edges().Nb(), 0); + const BRepGraph_EdgeId anEdgeId(0); + const BRepGraph_VertexRefId aStartRef = BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdgeId); + const BRepGraph_VertexRefId anEndRef = BRepGraph_Tool::Edge::EndVertexId(myGraph, anEdgeId); + const BRepGraph_Tool::VertexUsage aStart = BRepGraph_Tool::Vertex::Usage(myGraph, aStartRef); + const BRepGraph_Tool::VertexUsage anEnd = BRepGraph_Tool::Vertex::Usage(myGraph, anEndRef); + ASSERT_TRUE(aStart.IsValid()); + ASSERT_TRUE(anEnd.IsValid()); + ASSERT_NE(aStart.DefId, anEnd.DefId); + + EXPECT_FALSE(BRepGraph_Tool::Edge::FindByVertices(myGraph, anEnd.DefId, aStart.DefId).IsValid()); + EXPECT_EQ(BRepGraph_Tool::Edge::FindByVertices(myGraph, anEnd.DefId, aStart.DefId, true), + anEdgeId); +} + // ---------- Part D: FaceDef::Surface ---------- TEST_F(BRepGraph_ConvenienceTest, FaceSurface_Valid) { - const BRepGraph::TopoView aDefs = myGraph.Topo(); + const BRepGraph::TopoView& aDefs = myGraph.Topo(); ASSERT_GT(aDefs.Faces().Nb(), 0); EXPECT_TRUE(aDefs.Faces().Definition(BRepGraph_FaceId::Start()).SurfaceRepId.IsValid()); } @@ -145,9 +172,9 @@ TEST_F(BRepGraph_ConvenienceTest, FaceSurface_AllBoxFaces) } } -// ---------- Part E: DefsView::FindPCurve ---------- +// ---------- Part E: DefsView::FindPCurveCoEdgeId ---------- -TEST_F(BRepGraph_ConvenienceTest, FindPCurve_ValidPair) +TEST_F(BRepGraph_ConvenienceTest, FindPCurveCoEdgeId_ValidPair) { // Find an edge/face pair that has a PCurve. for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) @@ -156,22 +183,24 @@ TEST_F(BRepGraph_ConvenienceTest, FindPCurve_ValidPair) for (BRepGraph_EdgeIterator anEdgeIt(myGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraphInc::CoEdgeDef* aPCurve = - BRepGraph_Tool::Edge::FindPCurve(myGraph, anEdgeIt.CurrentId(), aFaceId); - if (aPCurve != nullptr) + const BRepGraph_CoEdgeId aPCurveId = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(myGraph, anEdgeIt.CurrentId(), aFaceId); + if (aPCurveId.IsValid()) { - EXPECT_TRUE(aPCurve->Curve2DRepId.IsValid()); + const BRepGraphInc::CoEdgeDef& aPCurve = myGraph.Topo().CoEdges().Definition(aPCurveId); + EXPECT_TRUE(aPCurve.Curve2DRepId.IsValid()); return; } } } } -TEST_F(BRepGraph_ConvenienceTest, FindPCurve_InvalidPair_ReturnsNull) +TEST_F(BRepGraph_ConvenienceTest, FindPCurveCoEdgeId_InvalidPair_ReturnsNull) { - EXPECT_EQ( - BRepGraph_Tool::Edge::FindPCurve(myGraph, BRepGraph_EdgeId::Start(), BRepGraph_FaceId(9999)), - nullptr); + EXPECT_FALSE(BRepGraph_Tool::Edge::FindPCurveCoEdgeId(myGraph, + BRepGraph_EdgeId::Start(), + BRepGraph_FaceId(9999)) + .IsValid()); } // ---------- Part F: RefsView::FaceRefIdsOf ---------- @@ -180,46 +209,59 @@ TEST_F(BRepGraph_ConvenienceTest, ShellFaceRefs_Box_SixFaces) { const BRepGraph::RefsView& aRefs = myGraph.Refs(); ASSERT_EQ(myGraph.Topo().Shells().Nb(), 1); - EXPECT_EQ(aRefs.Faces().IdsOf(BRepGraph_ShellId::Start()).Length(), 6); + EXPECT_EQ(aRefs.Faces().IdsOf(BRepGraph_ShellId::Start()).Size(), 6); } TEST_F(BRepGraph_ConvenienceTest, ShellFaceRefs_AllValid) { const BRepGraph::RefsView& aRefs = myGraph.Refs(); - const NCollection_DynamicArray& aFaceRefIds = + const NCollection_LinearVector& aFaceRefIds = aRefs.Faces().IdsOf(BRepGraph_ShellId::Start()); - for (int aFaceIter = 0; aFaceIter < aFaceRefIds.Length(); ++aFaceIter) + for (size_t aFaceIter = 0; aFaceIter < aFaceRefIds.Size(); ++aFaceIter) { const BRepGraphInc::FaceRef& aFaceRef = aRefs.Faces().Entry(aFaceRefIds.Value(aFaceIter)); - EXPECT_TRUE(aFaceRef.FaceDefId.IsValid()) << "Shell face ref " << aFaceIter; + EXPECT_TRUE(aFaceRef.ChildFaceId.IsValid()) << "Shell face ref " << aFaceIter; } } TEST_F(BRepGraph_ConvenienceTest, ShellFaceRefs_InvalidShell_Empty) { const BRepGraph::RefsView& aRefs = myGraph.Refs(); - EXPECT_EQ(aRefs.Faces().IdsOf(BRepGraph_ShellId(100)).Length(), 0); + EXPECT_EQ(aRefs.Faces().IdsOf(BRepGraph_ShellId(100)).Size(), 0); +} + +TEST_F(BRepGraph_ConvenienceTest, ShapesView_RemoveShape_RemovesFoundNode) +{ + ASSERT_GT(myGraph.Topo().Faces().Nb(), 0); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const TopoDS_Shape aFace = myGraph.Shapes().Original(aFaceId); + ASSERT_FALSE(aFace.IsNull()); + ASSERT_TRUE(myGraph.Shapes().FindNode(aFace).IsValid()); + + EXPECT_TRUE(myGraph.Shapes().RemoveShape(aFace)); + EXPECT_FALSE(myGraph.Shapes().FindNode(aFace).IsValid()); + EXPECT_TRUE(myGraph.Topo().Gen().IsRemoved(aFaceId)); + EXPECT_FALSE(myGraph.Shapes().RemoveShape(aFace)); } // ---------- Integration: Cylinder with seam edge ---------- -TEST_F(BRepGraph_ConvenienceTest, FindPCurve_WithOrientation_SeamEdge) +TEST_F(BRepGraph_ConvenienceTest, FindPCurveCoEdgeId_WithOrientation_SeamEdge) { BRepPrimAPI_MakeCylinder aCylMaker(5.0, 10.0); const TopoDS_Shape& aCyl = aCylMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCyl); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aCyl); + ASSERT_TRUE(aBuildRes2.IsOk()); - const BRepGraph::TopoView aDefs = aGraph.Topo(); + const BRepGraph::TopoView& aDefs = aGraph.Topo(); // Look for seam edges via the connectivity-derived IsSeam query. for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aDefs.Edges().CoEdges(anEdgeIt.CurrentId()); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) @@ -230,15 +272,19 @@ TEST_F(BRepGraph_ConvenienceTest, FindPCurve_WithOrientation_SeamEdge) continue; } - // Found seam edge - verify FindPCurve returns distinct entries for each orientation. - const BRepGraph_FaceId aFaceDefId = aCE.FaceDefId; - const BRepGraphInc::CoEdgeDef* aPCF = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeIt.CurrentId(), aFaceDefId, TopAbs_FORWARD); - const BRepGraphInc::CoEdgeDef* aPCR = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeIt.CurrentId(), aFaceDefId, TopAbs_REVERSED); - EXPECT_NE(aPCF, nullptr); - EXPECT_NE(aPCR, nullptr); - if (aPCF != nullptr && aPCR != nullptr) + // Found seam edge - verify FindPCurveCoEdgeId returns distinct entries for each orientation. + const BRepGraph_FaceId aFaceId = aCE.FaceId; + const BRepGraph_CoEdgeId aPCF = BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, + anEdgeIt.CurrentId(), + aFaceId, + TopAbs_FORWARD); + const BRepGraph_CoEdgeId aPCR = BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, + anEdgeIt.CurrentId(), + aFaceId, + TopAbs_REVERSED); + EXPECT_TRUE(aPCF.IsValid()); + EXPECT_TRUE(aPCR.IsValid()); + if (aPCF.IsValid() && aPCR.IsValid()) { EXPECT_NE(aPCF, aPCR); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx index 34ed18d76c..37662fc0e0 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Copy_Test.cxx @@ -12,11 +12,14 @@ // commercial license or contractual agreement. #include +#include #include +#include #include #include #include -#include +#include +#include #include #include #include @@ -24,15 +27,28 @@ #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 @@ -45,18 +61,131 @@ namespace { -class CopyTestCacheValue : public BRepGraph_CacheValue +class CopyTestCacheService : public BRepGraph_Cache { public: - DEFINE_STANDARD_RTTI_INLINE(CopyTestCacheValue, BRepGraph_CacheValue) - CopyTestCacheValue() = default; + DEFINE_STANDARD_RTTI_INLINE(CopyTestCacheService, BRepGraph_Cache) + + static const Standard_GUID& GetID() + { + static const Standard_GUID THE_ID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10026"); + return THE_ID; + } + + const Standard_GUID& ID() const override { return GetID(); } + + const TCollection_AsciiString& Name() const override + { + static const TCollection_AsciiString THE_NAME("CopyTestCacheService"); + return THE_NAME; + } + + void Set(const BRepGraph_NodeId theNode) { myNodes.Add(theNode); } + + bool Has(const BRepGraph_NodeId theNode) const { return myNodes.Contains(theNode); } + +private: + NCollection_FlatMap myNodes; }; -const occ::handle& copyTestCacheKind() +template +uint32_t countActive(const BRepGraph& theGraph, const uint32_t theCount) { - static const occ::handle THE_KIND = - new BRepGraph_CacheKind(Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10026"), "CopyTestAttr"); - return THE_KIND; + uint32_t aCount = 0; + for (IdT anId(0); anId.IsValid(theCount); ++anId) + { + if (!anId.IsRemoved(theGraph)) + { + ++aCount; + } + } + return aCount; +} + +static TopoDS_Edge makeEdgeWithInternalVertex() +{ + BRep_Builder aBB; + BRepBuilderAPI_MakeEdge aMakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); + TopoDS_Edge anEdge = aMakeEdge.Edge(); + + TopoDS_Vertex anIntVtx; + aBB.MakeVertex(anIntVtx, gp_Pnt(5, 0, 0), Precision::Confusion()); + aBB.Add(anEdge, anIntVtx.Oriented(TopAbs_INTERNAL)); + return anEdge; +} + +BRepGraph_CoEdgeId firstCoEdgeOfFace(const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) +{ + for (BRepGraph_RefsWireOfFace aWireRefIt(theGraph, theFaceId); aWireRefIt.More(); + aWireRefIt.Next()) + { + const BRepGraph_WireId aWireId = + theGraph.Refs().Wires().Entry(aWireRefIt.CurrentId()).ChildWireId; + for (BRepGraph_CoEdgesOfWire aCoEdgeIt(theGraph, aWireId); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + if (theGraph.Topo().CoEdges().Definition(aCoEdgeId).FaceId == theFaceId) + { + return aCoEdgeId; + } + } + } + return BRepGraph_CoEdgeId(); +} + +void initMeshHandles(const occ::handle& theTriangulation, + const occ::handle& thePolygon3D, + const occ::handle& thePolygon2D, + const occ::handle& thePolygonOnTri) +{ + theTriangulation->SetNode(1, gp_Pnt(1.0, 2.0, 3.0)); + theTriangulation->SetNode(2, gp_Pnt(4.0, 5.0, 6.0)); + theTriangulation->SetNode(3, gp_Pnt(7.0, 8.0, 9.0)); + theTriangulation->SetTriangle(1, Poly_Triangle(1, 2, 3)); + + thePolygon3D->ChangeNodes().SetValue(1, gp_Pnt(10.0, 11.0, 12.0)); + thePolygon3D->ChangeNodes().SetValue(2, gp_Pnt(13.0, 14.0, 15.0)); + + thePolygon2D->ChangeNodes().SetValue(1, gp_Pnt2d(16.0, 17.0)); + thePolygon2D->ChangeNodes().SetValue(2, gp_Pnt2d(18.0, 19.0)); + + thePolygonOnTri->SetNode(1, 2); + thePolygonOnTri->SetNode(2, 3); +} + +void expectMeshHandlesCopied(const occ::handle& theCopiedTriangulation, + const occ::handle& theCopiedPolygon3D, + const occ::handle& theCopiedPolygon2D, + const occ::handle& theCopiedPolygonOnTri, + const occ::handle& theSourceTriangulation, + const occ::handle& theSourcePolygon3D, + const occ::handle& theSourcePolygon2D, + const occ::handle& theSourcePolygonOnTri) +{ + ASSERT_FALSE(theCopiedTriangulation.IsNull()); + ASSERT_FALSE(theCopiedPolygon3D.IsNull()); + ASSERT_FALSE(theCopiedPolygon2D.IsNull()); + ASSERT_FALSE(theCopiedPolygonOnTri.IsNull()); + + EXPECT_NE(theCopiedTriangulation.get(), theSourceTriangulation.get()); + EXPECT_NE(theCopiedPolygon3D.get(), theSourcePolygon3D.get()); + EXPECT_NE(theCopiedPolygon2D.get(), theSourcePolygon2D.get()); + EXPECT_NE(theCopiedPolygonOnTri.get(), theSourcePolygonOnTri.get()); + + EXPECT_EQ(theCopiedTriangulation->NbNodes(), theSourceTriangulation->NbNodes()); + EXPECT_EQ(theCopiedTriangulation->NbTriangles(), theSourceTriangulation->NbTriangles()); + EXPECT_TRUE(theCopiedTriangulation->Node(2).IsEqual(theSourceTriangulation->Node(2), 0.0)); + + EXPECT_EQ(theCopiedPolygon3D->NbNodes(), theSourcePolygon3D->NbNodes()); + EXPECT_TRUE( + theCopiedPolygon3D->Nodes().Value(2).IsEqual(theSourcePolygon3D->Nodes().Value(2), 0.0)); + + EXPECT_EQ(theCopiedPolygon2D->NbNodes(), theSourcePolygon2D->NbNodes()); + EXPECT_TRUE( + theCopiedPolygon2D->Nodes().Value(2).IsEqual(theSourcePolygon2D->Nodes().Value(2), 0.0)); + + EXPECT_EQ(theCopiedPolygonOnTri->NbNodes(), theSourcePolygonOnTri->NbNodes()); + EXPECT_EQ(theCopiedPolygonOnTri->Node(2), theSourcePolygonOnTri->Node(2)); } } // namespace @@ -68,12 +197,12 @@ TEST(BRepGraph_CopyTest, CopyBox_FaceCount) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); EXPECT_EQ(aCopyGraph.Topo().Faces().Nb(), 6); EXPECT_EQ(aCopyGraph.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); @@ -89,6 +218,46 @@ TEST(BRepGraph_CopyTest, CopyBox_FaceCount) EXPECT_EQ(aNbFaces, 6); } +TEST(BRepGraph_CopyTest, CopyGraph_RemovedOccurrenceActiveCountsMatchFlags) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); + const BRepGraph_OccurrenceId anOccId = + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); + ASSERT_TRUE(anOccId.IsValid()); + + const NCollection_LinearVector& anOccurrenceRefs = + aGraph.Refs().Occurrences().IdsOf(aAssemblyId); + ASSERT_EQ(anOccurrenceRefs.Size(), 1); + const BRepGraph_OccurrenceRefId anOccRefId = anOccurrenceRefs.Value(0); + + aGraph.Editor().Gen().RemoveSubgraph(anOccId); + ASSERT_TRUE(anOccId.IsRemoved(aGraph)); + ASSERT_TRUE(anOccRefId.IsRemoved(aGraph)); + + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + EXPECT_TRUE(anOccId.IsRemoved(aCopyGraph)); + EXPECT_TRUE(anOccRefId.IsRemoved(aCopyGraph)); + EXPECT_EQ(aCopyGraph.Topo().Occurrences().NbActive(), + countActive( + aCopyGraph, + static_cast(aCopyGraph.Topo().Occurrences().Nb()))); + EXPECT_EQ(aCopyGraph.Refs().Occurrences().NbActive(), + countActive( + aCopyGraph, + static_cast(aCopyGraph.Refs().Occurrences().Nb()))); +} + TEST(BRepGraph_CopyTest, CopyBox_AreaPreserved) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); @@ -100,12 +269,12 @@ TEST(BRepGraph_CopyTest, CopyBox_AreaPreserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); TopoDS_Shape aCopy = aCopyGraph.Shapes().Reconstruct(BRepGraph_SolidId::Start()); ASSERT_FALSE(aCopy.IsNull()); @@ -129,12 +298,12 @@ TEST(BRepGraph_CopyTest, CopyBox_GeometryIsIndependent) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); // Deep copy: surface handles must be different objects. ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); @@ -143,6 +312,315 @@ TEST(BRepGraph_CopyTest, CopyBox_GeometryIsIndependent) BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId::Start()).get()); } +TEST(BRepGraph_CopyTest, CopyGraph_DropsRuntimeFaceCacheMesh) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + occ::handle aTriangulation = new Poly_Triangulation(3, 1, false); + + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTriangulation); + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + const BRepGraph_CacheMesh::FaceMeshEntry* aCopiedEntry = + aCopyGraph.Mesh().Cache().Faces().Entry(aFaceId); + EXPECT_EQ(aCopiedEntry, nullptr); +} + +TEST(BRepGraph_CopyTest, CopyGraph_CanCopyFreshRuntimeFaceCacheMesh) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + occ::handle aTriangulation = new Poly_Triangulation(3, 1, false); + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTriangulation); + ASSERT_NE(aGraph.Mesh().Cache().Faces().Entry(aFaceId), nullptr); + + BRepGraph aCopyGraph; + ASSERT_TRUE(BRepGraph_Copy::Perform(aGraph, + aCopyGraph, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Copy, + BRepGraph_Copy::CachePolicy::CopyFresh)); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + const BRepGraph_CacheMesh::FaceMeshEntry* aCopiedEntry = + aCopyGraph.Mesh().Cache().Faces().Entry(aFaceId); + ASSERT_NE(aCopiedEntry, nullptr); + EXPECT_EQ(aCopiedEntry->Triangulation.get(), aTriangulation.get()); +} + +TEST(BRepGraph_CopyTest, CopyGraph_DropsRuntimeEdgeAndCoEdgeCacheMesh) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId anEdgeId = aGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId; + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + + occ::handle aPolygon3D = new Poly_Polygon3D(2, false); + occ::handle aPolygon2D = new Poly_Polygon2D(2); + occ::handle aTriangulation = new Poly_Triangulation(3, 1, false); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTriangulation); + occ::handle aPolygonOnTri = + new Poly_PolygonOnTriangulation(2, false); + + aGraph.Mesh().Editor().Edges().SetCachedPolygon3D(anEdgeId, aPolygon3D); + aGraph.Mesh().Editor().CoEdges().SetCachedPolygon2D(aCoEdgeId, aPolygon2D); + aGraph.Mesh().Editor().CoEdges().AppendCachedPolygonOnTri(aCoEdgeId, aPolygonOnTri); + ASSERT_NE(aGraph.Mesh().Cache().Edges().Entry(anEdgeId), nullptr); + ASSERT_TRUE(aGraph.Mesh().Cache().CoEdges().Has(aCoEdgeId)); + + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + const BRepGraph_CacheMesh::EdgeMeshEntry* aCopiedEdge = + aCopyGraph.Mesh().Cache().Edges().Entry(anEdgeId); + EXPECT_EQ(aCopiedEdge, nullptr); + + EXPECT_FALSE(aCopyGraph.Mesh().Cache().CoEdges().Has(aCoEdgeId)); +} + +TEST(BRepGraph_CopyTest, CopyNode_CopiesPersistentMeshAndDropsCache) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId anEdgeId = aGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId; + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + + occ::handle aTriangulation = new Poly_Triangulation(3, 1, false); + occ::handle aPolygon3D = new Poly_Polygon3D(2, false); + occ::handle aPolygon2D = new Poly_Polygon2D(2); + occ::handle aPolygonOnTri = + new Poly_PolygonOnTriangulation(2, false); + initMeshHandles(aTriangulation, aPolygon3D, aPolygon2D, aPolygonOnTri); + + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTriangulation); + aGraph.Editor().Edges().SetPersistentPolygon3D(anEdgeId, aPolygon3D); + aGraph.Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, aPolygon2D); + aGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, aPolygonOnTri); + + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTriangulation); + aGraph.Mesh().Editor().Edges().SetCachedPolygon3D(anEdgeId, aPolygon3D); + aGraph.Mesh().Editor().CoEdges().SetCachedPolygon2D(aCoEdgeId, aPolygon2D); + aGraph.Mesh().Editor().CoEdges().AppendCachedPolygonOnTri(aCoEdgeId, aPolygonOnTri); + + BRepGraph aCopyGraph; + [[maybe_unused]] const BRepGraph_NodeId aCopiedNodeId = + BRepGraph_Copy::CopyNode(aGraph, aCopyGraph, BRepGraph_NodeId(aFaceId)); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + const BRepGraph_FaceId aCopyFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_CoEdgeId aCopyCoEdgeId = firstCoEdgeOfFace(aCopyGraph, aCopyFaceId); + ASSERT_TRUE(aCopyCoEdgeId.IsValid(aCopyGraph.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId aCopyEdgeId = + aCopyGraph.Topo().CoEdges().Definition(aCopyCoEdgeId).ChildEdgeId; + ASSERT_TRUE(aCopyEdgeId.IsValid(aCopyGraph.Topo().Edges().Nb())); + + const BRepGraph_CacheMesh::FaceMeshEntry* aCopiedFace = + aCopyGraph.Mesh().Cache().Faces().Entry(aCopyFaceId); + EXPECT_EQ(aCopiedFace, nullptr); + + const BRepGraph_CacheMesh::EdgeMeshEntry* aCopiedEdge = + aCopyGraph.Mesh().Cache().Edges().Entry(aCopyEdgeId); + EXPECT_EQ(aCopiedEdge, nullptr); + + EXPECT_FALSE(aCopyGraph.Mesh().Cache().CoEdges().Has(aCopyCoEdgeId)); + + expectMeshHandlesCopied( + aCopyGraph.Mesh().Persistent().Faces().Triangulation(aCopyFaceId), + aCopyGraph.Mesh().Persistent().Edges().Polygon3D(aCopyEdgeId), + aCopyGraph.Mesh().Persistent().CoEdges().PolygonOnSurface(aCopyCoEdgeId), + aCopyGraph.Mesh().Persistent().CoEdges().PolygonOnTriangulation(aCopyCoEdgeId), + aTriangulation, + aPolygon3D, + aPolygon2D, + aPolygonOnTri); +} + +TEST(BRepGraph_CopyTest, CopyGraph_CopiesPersistentMeshHandles) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId anEdgeId = aGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId; + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + + occ::handle aTriangulation = new Poly_Triangulation(3, 1, false); + occ::handle aPolygon3D = new Poly_Polygon3D(2, false); + occ::handle aPolygon2D = new Poly_Polygon2D(2); + occ::handle aPolygonOnTri = + new Poly_PolygonOnTriangulation(2, false); + initMeshHandles(aTriangulation, aPolygon3D, aPolygon2D, aPolygonOnTri); + + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTriangulation); + aGraph.Editor().Edges().SetPersistentPolygon3D(anEdgeId, aPolygon3D); + aGraph.Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, aPolygon2D); + aGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, aPolygonOnTri); + + BRepGraph aCopyGraph; + ASSERT_TRUE(BRepGraph_Copy::Perform(aGraph, + aCopyGraph, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Copy)); + + expectMeshHandlesCopied( + aCopyGraph.Mesh().Persistent().Faces().Triangulation(aFaceId), + aCopyGraph.Mesh().Persistent().Edges().Polygon3D(anEdgeId), + aCopyGraph.Mesh().Persistent().CoEdges().PolygonOnSurface(aCoEdgeId), + aCopyGraph.Mesh().Persistent().CoEdges().PolygonOnTriangulation(aCoEdgeId), + aTriangulation, + aPolygon3D, + aPolygon2D, + aPolygonOnTri); +} + +TEST(BRepGraph_CopyTest, CopyGraph_NonEmptyTargetUsesSetterPathForPersistentMesh) +{ + BRepGraph aSource; + aSource.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aSourceBuild = + aSource.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aSource.IsEmpty()); + + BRepGraph aTarget; + aTarget.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aTargetBuild = + aTarget.Shapes().Add(BRepPrimAPI_MakeBox(1.0, 2.0, 3.0).Shape()); + ASSERT_FALSE(aTarget.IsEmpty()); + + const BRepGraph_FaceId aSourceFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_CoEdgeId aSourceCoEdgeId = firstCoEdgeOfFace(aSource, aSourceFaceId); + ASSERT_TRUE(aSourceCoEdgeId.IsValid(aSource.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId aSourceEdgeId = + aSource.Topo().CoEdges().Definition(aSourceCoEdgeId).ChildEdgeId; + ASSERT_TRUE(aSourceEdgeId.IsValid(aSource.Topo().Edges().Nb())); + + occ::handle aTriangulation = new Poly_Triangulation(3, 1, false); + occ::handle aPolygon3D = new Poly_Polygon3D(2, false); + occ::handle aPolygon2D = new Poly_Polygon2D(2); + occ::handle aPolygonOnTri = + new Poly_PolygonOnTriangulation(2, false); + initMeshHandles(aTriangulation, aPolygon3D, aPolygon2D, aPolygonOnTri); + + aSource.Editor().Faces().SetPersistentTriangulation(aSourceFaceId, aTriangulation); + aSource.Editor().Edges().SetPersistentPolygon3D(aSourceEdgeId, aPolygon3D); + aSource.Editor().CoEdges().SetPersistentPolygon2D(aSourceCoEdgeId, aPolygon2D); + aSource.Editor().CoEdges().SetPersistentPolygonOnTri(aSourceCoEdgeId, aPolygonOnTri); + + const uint32_t aTargetFaceCount = aTarget.Topo().Faces().Nb(); + const uint32_t aTargetEdgeCount = aTarget.Topo().Edges().Nb(); + const uint32_t aTargetCoEdgeCount = aTarget.Topo().CoEdges().Nb(); + + ASSERT_TRUE(BRepGraph_Copy::Perform(aSource, + aTarget, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Copy)); + + const BRepGraph_FaceId aCopiedFaceId(aTargetFaceCount + aSourceFaceId.Index); + const BRepGraph_CoEdgeId aCopiedCoEdgeId(aTargetCoEdgeCount + aSourceCoEdgeId.Index); + const BRepGraph_EdgeId aCopiedEdgeId(aTargetEdgeCount + aSourceEdgeId.Index); + ASSERT_TRUE(aCopiedFaceId.IsValid(aTarget.Topo().Faces().Nb())); + ASSERT_TRUE(aCopiedEdgeId.IsValid(aTarget.Topo().Edges().Nb())); + ASSERT_TRUE(aCopiedCoEdgeId.IsValid(aTarget.Topo().CoEdges().Nb())); + + expectMeshHandlesCopied( + aTarget.Mesh().Persistent().Faces().Triangulation(aCopiedFaceId), + aTarget.Mesh().Persistent().Edges().Polygon3D(aCopiedEdgeId), + aTarget.Mesh().Persistent().CoEdges().PolygonOnSurface(aCopiedCoEdgeId), + aTarget.Mesh().Persistent().CoEdges().PolygonOnTriangulation(aCopiedCoEdgeId), + aTriangulation, + aPolygon3D, + aPolygon2D, + aPolygonOnTri); +} + +TEST(BRepGraph_CopyTest, CopyNode_DoesNotReviveRemovedPersistentMeshReps) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId anEdgeId = aGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId; + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + + occ::handle aTriangulation = new Poly_Triangulation(3, 1, false); + occ::handle aPolygon3D = new Poly_Polygon3D(2, false); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTriangulation); + aGraph.Editor().Edges().SetPersistentPolygon3D(anEdgeId, aPolygon3D); + ASSERT_TRUE(aGraph.Mesh().Persistent().Faces().Has(aFaceId)); + ASSERT_TRUE(aGraph.Mesh().Persistent().Edges().Has(anEdgeId)); + + aGraph.Editor().Faces().ClearPersistentTriangulation(aFaceId); + aGraph.Editor().Edges().ClearPersistentPolygon3D(anEdgeId); + ASSERT_FALSE(aGraph.Mesh().Persistent().Faces().Has(aFaceId)); + ASSERT_FALSE(aGraph.Mesh().Persistent().Edges().Has(anEdgeId)); + + BRepGraph aCopyGraph; + [[maybe_unused]] const BRepGraph_NodeId aCopiedNodeId = + BRepGraph_Copy::CopyNode(aGraph, aCopyGraph, BRepGraph_NodeId(aFaceId)); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + const BRepGraph_FaceId aCopyFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_CoEdgeId aCopyCoEdgeId = firstCoEdgeOfFace(aCopyGraph, aCopyFaceId); + ASSERT_TRUE(aCopyCoEdgeId.IsValid(aCopyGraph.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId aCopyEdgeId = + aCopyGraph.Topo().CoEdges().Definition(aCopyCoEdgeId).ChildEdgeId; + ASSERT_TRUE(aCopyEdgeId.IsValid(aCopyGraph.Topo().Edges().Nb())); + + EXPECT_FALSE(aCopyGraph.Mesh().Persistent().Faces().Has(aCopyFaceId)); + EXPECT_FALSE(aCopyGraph.Mesh().Persistent().Edges().Has(aCopyEdgeId)); +} + TEST(BRepGraph_CopyTest, CopyBox_SharedGeometry) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); @@ -150,13 +628,13 @@ TEST(BRepGraph_CopyTest, CopyBox_SharedGeometry) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // theCopyGeom = false: geometry is shared. - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, false); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Share); + ASSERT_FALSE(aCopyGraph.IsEmpty()); EXPECT_EQ(aCopyGraph.Topo().Faces().Nb(), 6); // Light copy: surface handles must be the same objects. @@ -166,113 +644,195 @@ TEST(BRepGraph_CopyTest, CopyBox_SharedGeometry) BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId::Start()).get()); } -TEST(BRepGraph_CopyTest, CopyBox_PreservesFreshNodeCache) +TEST(BRepGraph_CopyTest, CopyBox_DoesNotCopyTransientCacheServices) { const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_FaceId aFaceId(0); - const occ::handle anAttr = new CopyTestCacheValue(); - aGraph.Cache().Set(aFaceId, copyTestCacheKind(), anAttr); - ASSERT_TRUE(aGraph.Cache().Has(aFaceId, copyTestCacheKind())); + const BRepGraph_FaceId aFaceId(0); + occ::handle aCache = aGraph.CacheRegistry().Ensure(); + aCache->Set(aFaceId); + ASSERT_TRUE(aCache->Has(aFaceId)); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); - EXPECT_TRUE(aCopyGraph.Cache().Has(aFaceId, copyTestCacheKind())); - EXPECT_FALSE(aCopyGraph.Cache().Get(aFaceId, copyTestCacheKind()).IsNull()); - EXPECT_TRUE(aCopyGraph.Cache().CacheKindIter(aFaceId).More()); + EXPECT_TRUE(aCopyGraph.CacheRegistry().Find().IsNull()); } -TEST(BRepGraph_CopyTest, CopyBox_DoesNotPreserveStaleNodeCache) +TEST(BRepGraph_CopyTest, CopyGraph_PreservesSupplementAttachmentsAndUids) { - const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); - BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + const occ::handle aRegisteredLayer = + aGraph.LayerRegistry().Ensure(); + ASSERT_FALSE(aRegisteredLayer.IsNull()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildResSupplement = + aGraph.Shapes().Add(makeEdgeWithInternalVertex()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_FaceId aFaceId(0); - aGraph.Cache().Set(aFaceId, copyTestCacheKind(), new CopyTestCacheValue()); - ASSERT_TRUE(aGraph.Cache().Has(aFaceId, copyTestCacheKind())); + const occ::handle aSrcLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aSrcLayer.IsNull()); + ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); + const BRepGraph_EdgeId aEdgeId = BRepGraph_EdgeId::Start(); + const NCollection_LinearVector& aSrcAttached = aSrcLayer->AttachedTo(aEdgeId); + ASSERT_EQ(aSrcAttached.Size(), 1); + const uint64_t aUid = aSrcAttached.First(); + + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + const occ::handle aDstLayer = + aCopyGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aDstLayer.IsNull()); + + const NCollection_LinearVector& aDstAttached = aDstLayer->AttachedTo(aEdgeId); + ASSERT_EQ(aDstAttached.Size(), 1); + EXPECT_EQ(aDstAttached.First(), aUid); + + const BRepGraph_LayerTopoSupplement::Entry* aDstEntry = aDstLayer->FindByUid(aUid); + ASSERT_NE(aDstEntry, nullptr); + EXPECT_EQ(aDstEntry->BaseOwner, BRepGraph_NodeId(aEdgeId)); + EXPECT_EQ(aDstEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::EdgeInternalVertex); + EXPECT_EQ(aDstEntry->Shape.ShapeType(), TopAbs_VERTEX); + EXPECT_EQ(aDstEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRecon = aCopyGraph.Shapes().Reconstruct(aEdgeId); + ASSERT_FALSE(aRecon.IsNull()); + + int aFoundInternal = 0; + for (TopoDS_Iterator aVIt(aRecon, false); aVIt.More(); aVIt.Next()) { - BRepGraph_MutGuard aFace = aGraph.Editor().Faces().Mut(aFaceId); - aGraph.Editor().Faces().SetTolerance(aFace, aFace->Tolerance + 0.1); + if (aVIt.Value().ShapeType() == TopAbs_VERTEX && aVIt.Value().Orientation() == TopAbs_INTERNAL) + { + ++aFoundInternal; + } } - - ASSERT_FALSE(aGraph.Cache().Has(aFaceId, copyTestCacheKind())); - ASSERT_TRUE(aGraph.Cache().Get(aFaceId, copyTestCacheKind()).IsNull()); - ASSERT_FALSE(aGraph.Cache().CacheKindIter(aFaceId).More()); - - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); - - EXPECT_FALSE(aCopyGraph.Cache().Has(aFaceId, copyTestCacheKind())); - EXPECT_TRUE(aCopyGraph.Cache().Get(aFaceId, copyTestCacheKind()).IsNull()); - EXPECT_FALSE(aCopyGraph.Cache().CacheKindIter(aFaceId).More()); + EXPECT_EQ(aFoundInternal, 1); } -TEST(BRepGraph_CopyTest, CopyBox_PreservesFreshFaceRefCache) +TEST(BRepGraph_CopyTest, CopyGraph_CopiesHistoryThroughLayerRemap) { - const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); - BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); - ASSERT_GT(aGraph.Refs().Faces().Nb(), 0); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + ASSERT_GE(aGraph.Topo().Faces().Nb(), 2u); - const BRepGraph_FaceRefId aFaceRef(0); - aGraph.Cache().Set(aFaceRef, copyTestCacheKind(), new CopyTestCacheValue()); - ASSERT_TRUE(aGraph.Cache().Has(aFaceRef, copyTestCacheKind())); + const BRepGraph_FaceId aOriginalFace = BRepGraph_FaceId::Start(); + const BRepGraph_FaceId aReplacementFace = BRepGraph_FaceId(1); + const BRepGraph_UID anOriginalUID = aGraph.UIDs().Of(aOriginalFace); + const BRepGraph_UID aReplacementUID = aGraph.UIDs().Of(aReplacementFace); + ASSERT_TRUE(anOriginalUID.IsValid()); + ASSERT_TRUE(aReplacementUID.IsValid()); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + NCollection_LinearVector aReplacements; + aReplacements.Append(aReplacementFace); + aGraph.LayerRegistry().Ensure()->Record("CopyHistory", + aOriginalFace, + aReplacements.ToArray1()); - EXPECT_TRUE(aCopyGraph.Cache().Has(aFaceRef, copyTestCacheKind())); - EXPECT_FALSE(aCopyGraph.Cache().Get(aFaceRef, copyTestCacheKind()).IsNull()); - EXPECT_TRUE(aCopyGraph.Cache().CacheKindIter(aFaceRef).More()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + const occ::handle aCopyHistory = + aCopyGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aCopyHistory.IsNull()); + + const BRepGraph_NodeId aCopyOriginal = aCopyGraph.UIDs().NodeIdFrom(anOriginalUID); + const BRepGraph_NodeId aCopyReplacement = aCopyGraph.UIDs().NodeIdFrom(aReplacementUID); + ASSERT_TRUE(aCopyOriginal.IsValid()); + ASSERT_TRUE(aCopyReplacement.IsValid()); + + const NCollection_LinearVector* aModified = + aCopyHistory->FindModified(aCopyOriginal); + ASSERT_NE(aModified, nullptr); + ASSERT_EQ(aModified->Size(), size_t(1)); + EXPECT_EQ(aModified->Value(0), aCopyReplacement); + + const BRepGraph_ItemUID aCopyOriginalUID = aCopyGraph.UIDs().Of(BRepGraph_ItemId(aCopyOriginal)); + const NCollection_LinearVector* anItemModified = + aCopyHistory->FindModified(aCopyOriginalUID); + ASSERT_NE(anItemModified, nullptr); + ASSERT_EQ(anItemModified->Size(), size_t(1)); + EXPECT_EQ(anItemModified->Value(0), aCopyGraph.UIDs().Of(BRepGraph_ItemId(aCopyReplacement))); } -TEST(BRepGraph_CopyTest, CopyBox_DoesNotPreserveStaleFaceRefCache) +TEST(BRepGraph_CopyTest, CopyGraph_PreservesRefUIDs) { - const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); - BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); - ASSERT_GT(aGraph.Refs().Faces().Nb(), 0); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_FaceRefId aFaceRef(0); - aGraph.Cache().Set(aFaceRef, copyTestCacheKind(), new CopyTestCacheValue()); - ASSERT_TRUE(aGraph.Cache().Has(aFaceRef, copyTestCacheKind())); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + const BRepGraphInc::FaceRelations& aFaceRelations = aGraph.Topo().Faces().Relations(aFaceId); + ASSERT_FALSE(aFaceRelations.WireRefIds.IsEmpty()); + const BRepGraph_WireRefId aWireRefId = aFaceRelations.WireRefIds.First(); + const BRepGraph_RefUID aWireRefUID = aGraph.UIDs().Of(aWireRefId); + ASSERT_TRUE(aWireRefUID.IsValid()); - { - BRepGraph_MutGuard aRef = aGraph.Editor().Faces().MutRef(aFaceRef); - aGraph.Editor().Faces().SetRefOrientation(aRef, TopAbs::Reverse(aRef->Orientation)); - } + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); - ASSERT_FALSE(aGraph.Cache().Has(aFaceRef, copyTestCacheKind())); - ASSERT_TRUE(aGraph.Cache().Get(aFaceRef, copyTestCacheKind()).IsNull()); - ASSERT_FALSE(aGraph.Cache().CacheKindIter(aFaceRef).More()); + ASSERT_TRUE(aWireRefId.IsValid(aCopyGraph.Refs().Wires().Nb())); + EXPECT_EQ(aCopyGraph.UIDs().Of(aWireRefId), aWireRefUID); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + const BRepGraph_RefId aResolvedRefId = aCopyGraph.UIDs().RefIdFrom(aWireRefUID); + EXPECT_TRUE(aResolvedRefId.IsValid()); + EXPECT_EQ(aResolvedRefId, BRepGraph_RefId(aWireRefId)); - EXPECT_FALSE(aCopyGraph.Cache().Has(aFaceRef, copyTestCacheKind())); - EXPECT_TRUE(aCopyGraph.Cache().Get(aFaceRef, copyTestCacheKind()).IsNull()); - EXPECT_FALSE(aCopyGraph.Cache().CacheKindIter(aFaceRef).More()); + const BRepGraph_VersionStamp aStamp = aCopyGraph.UIDs().StampOf(aWireRefId); + EXPECT_TRUE(aStamp.IsValid()); + EXPECT_EQ(aStamp.ItemUID(), BRepGraph_ItemUID::Reference(aWireRefUID.Kind, aWireRefUID.Counter)); +} + +TEST(BRepGraph_CopyTest, CopyGraph_PreservesDeletedHistory) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + occ::handle aHistory = + aGraph.LayerRegistry().Ensure(); + const BRepGraph_FaceId aDeletedFace = BRepGraph_FaceId::Start(); + const BRepGraph_ItemUID aDeletedFaceUid = aGraph.UIDs().Of(BRepGraph_ItemId(aDeletedFace)); + ASSERT_TRUE(aDeletedFaceUid.IsValid()); + + aGraph.Editor().Gen().RemoveNode(aDeletedFace); + ASSERT_TRUE(aDeletedFace.IsRemoved(aGraph)); + ASSERT_TRUE(aHistory->IsDeleted(aDeletedFace)); + ASSERT_TRUE(aHistory->IsDeleted(aDeletedFaceUid)); + ASSERT_EQ(aHistory->NbRecords(), size_t(1)); + + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); + + occ::handle aCopyHistory = + aCopyGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aCopyHistory.IsNull()); + + EXPECT_TRUE(aCopyHistory->IsDeleted(aDeletedFace)); + EXPECT_TRUE(aCopyHistory->IsDeleted(aDeletedFaceUid)); + EXPECT_TRUE(aCopyHistory->HasKnownInput(aDeletedFaceUid)); + EXPECT_TRUE(aCopyHistory->DeletedItemUids().Contains(aDeletedFaceUid)); + EXPECT_EQ(aCopyHistory->NbRecords(), aHistory->NbRecords()); } TEST(BRepGraph_CopyTest, CopyCylinder_FaceCount) @@ -282,12 +842,12 @@ TEST(BRepGraph_CopyTest, CopyCylinder_FaceCount) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, aCyl); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = aGraph.Shapes().Add(aCyl); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); EXPECT_EQ(aCopyGraph.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); } @@ -298,15 +858,16 @@ TEST(BRepGraph_CopyTest, CopySingleFace) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); - const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); - const BRepGraph_NodeId aFaceNode(BRepGraph_NodeId::Kind::Face, aFaceId.Index); - BRepGraph aCopyGraph = BRepGraph_Copy::CopyNode(aGraph, aFaceNode, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_NodeId aFaceNode(BRepGraph_NodeId::Kind::Face, aFaceId.Index); + BRepGraph aCopyGraph; + [[maybe_unused]] const BRepGraph_NodeId aCopiedNodeId = + BRepGraph_Copy::CopyNode(aGraph, aCopyGraph, aFaceNode); + ASSERT_FALSE(aCopyGraph.IsEmpty()); EXPECT_EQ(aCopyGraph.Topo().Faces().Nb(), 1); // A box face has 1 wire with 4 edges and 4 vertices. @@ -344,15 +905,15 @@ TEST(BRepGraph_CopyTest, CopyFacesOnly_Compound) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(aGraph.Topo().Faces().Nb(), 6); ASSERT_EQ(aGraph.Topo().Solids().Nb(), 0); ASSERT_EQ(aGraph.Topo().Shells().Nb(), 0); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); EXPECT_EQ(aCopyGraph.Topo().Faces().Nb(), 6); EXPECT_EQ(aCopyGraph.Topo().Solids().Nb(), 0); EXPECT_EQ(aCopyGraph.Topo().Shells().Nb(), 0); @@ -368,21 +929,21 @@ TEST(BRepGraph_CopyTest, CopyBox_SameParameter_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); // All edges in the copied graph must preserve SameParameter = true. - const int aNbEdges = aCopyGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = aCopyGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - const BRepGraphInc::EdgeDef& anEdge = aCopyGraph.Topo().Edges().Definition(anEdgeId); - EXPECT_TRUE(anEdge.SameParameter) + EXPECT_TRUE(BRepGraph_Tool::Edge::SameParameter(aCopyGraph, anEdgeId)) << "Copied edge " << anEdgeId.Index << " lost SameParameter flag"; - EXPECT_TRUE(anEdge.SameRange) << "Copied edge " << anEdgeId.Index << " lost SameRange flag"; + EXPECT_TRUE(BRepGraph_Tool::Edge::SameRange(aCopyGraph, anEdgeId)) + << "Copied edge " << anEdgeId.Index << " lost SameRange flag"; } } @@ -406,20 +967,20 @@ TEST(BRepGraph_CopyTest, FusedBoxes_Regularity_AreaPreserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, aFused); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = aGraph.Shapes().Add(aFused); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); // Verify node counts match. EXPECT_EQ(aCopyGraph.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); EXPECT_EQ(aCopyGraph.Topo().Edges().Nb(), aGraph.Topo().Edges().Nb()); // Verify area is preserved by summing individual face areas. - double aCopyArea = 0.0; - const int aNbFaces = aCopyGraph.Topo().Faces().Nb(); + double aCopyArea = 0.0; + const uint32_t aNbFaces = aCopyGraph.Topo().Faces().Nb(); for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) { TopoDS_Shape aFace = aCopyGraph.Shapes().Reconstruct(aFaceId); @@ -440,16 +1001,16 @@ TEST(BRepGraph_CopyTest, CopyBox_UIDsPreserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraph aCopyGraph = BRepGraph_Copy::Perform(aGraph, true); - ASSERT_TRUE(aCopyGraph.IsDone()); + BRepGraph aCopyGraph; + BRepGraph_Copy::Perform(aGraph, aCopyGraph, BRepGraph_Copy::GeomPolicy::Copy); + ASSERT_FALSE(aCopyGraph.IsEmpty()); // Helper to check UIDs for a given node kind. - auto checkUIDs = [&](BRepGraph_NodeId::Kind theKind, int theCount, const char* theLabel) { - for (int anIdx = 0; anIdx < theCount; ++anIdx) + auto checkUIDs = [&](BRepGraph_NodeId::Kind theKind, uint32_t theCount, const char* theLabel) { + for (uint32_t anIdx = 0; anIdx < theCount; ++anIdx) { BRepGraph_NodeId aNodeId(theKind, anIdx); BRepGraph_UID anOrigUID = aGraph.UIDs().Of(aNodeId); @@ -470,5 +1031,11 @@ TEST(BRepGraph_CopyTest, CopyBox_UIDsPreserved) checkUIDs(BRepGraph_NodeId::Kind::Face, aGraph.Topo().Faces().Nb(), "face"); checkUIDs(BRepGraph_NodeId::Kind::Shell, aGraph.Topo().Shells().Nb(), "shell"); checkUIDs(BRepGraph_NodeId::Kind::Solid, aGraph.Topo().Solids().Nb(), "solid"); - // Geometry is now stored inline on face/edge defs; no separate geometry UIDs to check. + + const BRepGraph_FaceSurfaceRepId aSurfaceRepId = + aGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).SurfaceRepId; + ASSERT_TRUE(aSurfaceRepId.IsValid()); + const BRepGraph_FaceSurfaceRepId aCopySurfaceRepId = + aCopyGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).SurfaceRepId; + EXPECT_TRUE(aCopySurfaceRepId.IsValid()); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Deduplicate_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Deduplicate_Test.cxx index b4d07cdd12..951dd9db90 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Deduplicate_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Deduplicate_Test.cxx @@ -15,30 +15,32 @@ #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 "BRepGraph_RefTestTools.hxx" #include #include #include #include #include +#include #include #include #include @@ -46,6 +48,10 @@ #include #include +#include +#include +#include + #include namespace @@ -108,19 +114,19 @@ int nbUniqueEdgeCurveDefs(const BRepGraph& theGraph) //================================================================================================= -int nbPCurveEntries(const BRepGraph& theGraph) +size_t nbPCurveEntries(const BRepGraph& theGraph) { - int aCount = 0; + size_t aCount = 0; for (BRepGraph_EdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = theGraph.Topo().Edges().CoEdges(anEdgeId); - for (int i = 0; i < aCoEdgeIdxs.Length(); ++i) + for (size_t i = 0; i < aCoEdgeIdxs.Size(); ++i) { const BRepGraphInc::CoEdgeDef& aCE = theGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(i)); - if (aCE.FaceDefId.IsValid()) + if (aCE.FaceId.IsValid()) { ++aCount; } @@ -133,10 +139,17 @@ int nbPCurveEntries(const BRepGraph& theGraph) int countHistoryRecordsByOp(const BRepGraph& theGraph, const TCollection_AsciiString& theOp) { - int aCount = 0; - for (size_t aRecIdx = 0; aRecIdx < theGraph.History().NbRecords(); ++aRecIdx) + const BRepGraph_LayerHistory* aHistory = + theGraph.LayerRegistry().Find().get(); + if (aHistory == nullptr) { - if (theGraph.History().Record(aRecIdx).OperationName == theOp) + return 0; + } + + int aCount = 0; + for (size_t aRecIdx = 0; aRecIdx < aHistory->NbRecords(); ++aRecIdx) + { + if (aHistory->Record(aRecIdx).OperationName == theOp) { ++aCount; } @@ -246,15 +259,15 @@ TopoDS_Compound makeTwoIdenticalBoxes() return aCompound; } -int nbUniquePCurveNodes(const BRepGraph& theGraph) +uint32_t nbUniquePCurveNodes(const BRepGraph& theGraph) { - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = theGraph.Topo().Edges().CoEdges(anEdgeId); - aCount += aCoEdgeIdxs.Length(); + aCount += static_cast(aCoEdgeIdxs.Size()); } return aCount; } @@ -265,40 +278,77 @@ int addDuplicatePCurvesToAllEdges(BRepGraph& theGraph) for (BRepGraph_FullEdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = theGraph.Topo().Edges().CoEdges(anEdgeId); if (aCoEdgeIdxs.IsEmpty()) { continue; } - const BRepGraphInc::CoEdgeDef& aCE = theGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(0)); + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIdxs.Value(0); + const BRepGraphInc::CoEdgeDef& aCE = theGraph.Topo().CoEdges().Definition(aCoEdgeId); if (!aCE.Curve2DRepId.IsValid()) { continue; } - const occ::handle& aDupPCurve = BRepGraph_Tool::CoEdge::PCurve(theGraph, aCE); - theGraph.Editor().CoEdges().AddPCurve(anEdgeId, - aCE.FaceDefId, - aDupPCurve, - aCE.ParamFirst, - aCE.ParamLast, - aCE.Orientation); + const std::pair aRange = BRepGraph_Tool::CoEdge::Range(theGraph, aCoEdgeId); + const occ::handle& aDupPCurve = + BRepGraph_Tool::CoEdge::PCurve(theGraph, aCoEdgeId); + std::ignore = theGraph.Editor().CoEdges().Add(anEdgeId, + aCE.FaceId, + aDupPCurve, + aRange.first, + aRange.second, + aCE.Orientation); ++aDupCount; } return aDupCount; } +//================================================================================================= + +TopoDS_Compound makeThreeCopiedIdenticalEdges() +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + TopExp_Explorer anExp(aBox, TopAbs_EDGE); + const TopoDS_Shape aEdge = anExp.Current(); + BRepBuilderAPI_Copy aCopy1(aEdge, true); + BRepBuilderAPI_Copy aCopy2(aEdge, true); + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aEdge); + aBuilder.Add(aCompound, aCopy1.Shape()); + aBuilder.Add(aCompound, aCopy2.Shape()); + return aCompound; +} + +//================================================================================================= + +TopoDS_Compound makeTwoFaceTouchingBoxes() +{ + BRepPrimAPI_MakeBox aBoxMaker1(gp_Pnt(0, 0, 0), 10.0, 10.0, 10.0); + BRepPrimAPI_MakeBox aBoxMaker2(gp_Pnt(0, 0, 10), 10.0, 10.0, 10.0); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aBoxMaker1.Shape()); + aBuilder.Add(aCompound, aBoxMaker2.Shape()); + return aCompound; +} + } // namespace TEST(BRepGraph_DeduplicateTest, AnalyzeOnly_DoesNotRewrite) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(nbUniqueFaceSurfaceDefs(aGraph), 2); @@ -315,9 +365,9 @@ TEST(BRepGraph_DeduplicateTest, AnalyzeOnly_ReportsCanonicalCandidates) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.AnalyzeOnly = true; @@ -334,9 +384,9 @@ TEST(BRepGraph_DeduplicateTest, CanonicalizeSurfaces_RewritesAndRecordsHistory) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(nbUniqueFaceSurfaceDefs(aGraph), 2); @@ -344,22 +394,24 @@ TEST(BRepGraph_DeduplicateTest, CanonicalizeSurfaces_RewritesAndRecordsHistory) anOpts.AnalyzeOnly = false; anOpts.HistoryMode = true; - const size_t aHistoryBefore = aGraph.History().NbRecords(); + const size_t aHistoryBefore = + aGraph.LayerRegistry().Ensure()->NbRecords(); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); EXPECT_EQ(aRes.NbSurfaceRewrites, 1); EXPECT_EQ(nbUniqueFaceSurfaceDefs(aGraph), 1); // 1 surface canonicalize + 4 curve canonicalizes = 5 history records. - EXPECT_EQ(aGraph.History().NbRecords(), aHistoryBefore + 5); + EXPECT_EQ(aGraph.LayerRegistry().Ensure()->NbRecords(), + aHistoryBefore + 5); } TEST(BRepGraph_DeduplicateTest, CanonicalizeCurves_RewritesAndReducesUnique) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(nbUniqueEdgeCurveDefs(aGraph), 8); @@ -375,11 +427,11 @@ TEST(BRepGraph_DeduplicateTest, HistoryModeOff_DoesNotAddHistory) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - ASSERT_EQ(aGraph.History().NbRecords(), 0); + ASSERT_EQ(aGraph.LayerRegistry().Ensure()->NbRecords(), 0); BRepGraph_Deduplicate::Options anOpts; anOpts.HistoryMode = false; @@ -387,34 +439,34 @@ TEST(BRepGraph_DeduplicateTest, HistoryModeOff_DoesNotAddHistory) const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); EXPECT_EQ(aRes.NbSurfaceRewrites, 1); EXPECT_EQ(aRes.NbCurveRewrites, 4); - EXPECT_EQ(aGraph.History().NbRecords(), 0); + EXPECT_EQ(aGraph.LayerRegistry().Ensure()->NbRecords(), 0); } TEST(BRepGraph_DeduplicateTest, RestoresHistoryEnabledFlag) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - aGraph.History().SetEnabled(false); - ASSERT_FALSE(aGraph.History().IsEnabled()); + aGraph.LayerRegistry().Ensure()->SetEnabled(false); + ASSERT_FALSE(aGraph.LayerRegistry().Ensure()->IsEnabled()); BRepGraph_Deduplicate::Options anOpts; anOpts.HistoryMode = true; - (void)BRepGraph_Deduplicate::Perform(aGraph, anOpts); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); - EXPECT_FALSE(aGraph.History().IsEnabled()); + EXPECT_FALSE(aGraph.LayerRegistry().Ensure()->IsEnabled()); } TEST(BRepGraph_DeduplicateTest, DefaultOverload_PerformWorks) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); EXPECT_EQ(aRes.NbCanonicalSurfaces, 1); @@ -434,9 +486,9 @@ TEST(BRepGraph_DeduplicateTest, SingleFace_NoSurfaceRewrite) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, anExp.Current()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(anExp.Current()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); EXPECT_EQ(aRes.NbSurfaceRewrites, 0); @@ -447,8 +499,8 @@ TEST(BRepGraph_DeduplicateTest, SingleFace_NoSurfaceRewrite) TEST(BRepGraph_DeduplicateTest, NotDoneGraph_ReturnsEmptyResult) { BRepGraph aGraph; - // Do not call BRepGraph_Builder::Add() - graph is not done. - ASSERT_FALSE(aGraph.IsDone()); + // Do not call BRepGraph::ShapesView::Add() - graph is not done. + ASSERT_TRUE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); EXPECT_EQ(aRes.NbCanonicalSurfaces, 0); @@ -459,15 +511,18 @@ TEST(BRepGraph_DeduplicateTest, NotDoneGraph_ReturnsEmptyResult) EXPECT_EQ(aRes.NbNullifiedCurves, 0); EXPECT_EQ(aRes.NbHistoryRecords, 0); EXPECT_FALSE(aRes.IsEntityMergeApplied); + EXPECT_EQ(aRes.NbReorderedWires, 0); + EXPECT_EQ(aRes.NbToleranceOrderedWires, 0); + EXPECT_EQ(aRes.NbPartialOrderedWires, 0); } TEST(BRepGraph_DeduplicateTest, Idempotent_SecondRunNoRewrites) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes1 = BRepGraph_Deduplicate::Perform(aGraph); ASSERT_EQ(aRes1.NbSurfaceRewrites, 1); @@ -493,9 +548,8 @@ TEST(BRepGraph_DeduplicateTest, FullBox_AllSurfacesUnique) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // A box has 6 faces with 6 distinct Geom_Plane instances (different origins/normals). // No surface dedup should occur - all 6 are canonical. @@ -508,9 +562,9 @@ TEST(BRepGraph_DeduplicateTest, ResultCountersConsistency) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // TwoCopiedFaces: 2 surfaces, 8 curves, 8 PCurves. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); @@ -539,9 +593,8 @@ TEST(BRepGraph_DeduplicateTest, EmptyCompound_NoRewrites) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); EXPECT_EQ(aRes.NbSurfaceRewrites, 0); @@ -553,9 +606,9 @@ TEST(BRepGraph_DeduplicateTest, MultipleCopies_NWayDedup) const int aNbCopies = 4; BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, makeNCopiedIdenticalFaces(aNbCopies)); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = + aGraph.Shapes().Add(makeNCopiedIdenticalFaces(aNbCopies)); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(aGraph.Topo().Faces().Nb(), aNbCopies); @@ -569,9 +622,9 @@ TEST(BRepGraph_DeduplicateTest, MixedGeometry_OnlyIdenticalDeduped) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, makeMixedCompound()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(makeMixedCompound()); + ASSERT_FALSE(aGraph.IsEmpty()); // 2 box face copies + 1 cylinder face = 3 faces, 3 surfaces, 11 curves. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 3); @@ -595,11 +648,11 @@ TEST(BRepGraph_DeduplicateTest, AfterDedup_AllCopiedFacesShareCanonicalSurface) const int aNbCopies = 3; BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, makeNCopiedIdenticalFaces(aNbCopies)); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aGraph.Shapes().Add(makeNCopiedIdenticalFaces(aNbCopies)); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // All face defs should share the same Surface handle. NCollection_Map aSurfIds; @@ -619,9 +672,9 @@ TEST(BRepGraph_DeduplicateTest, HistoryRecordNames_MatchExpectedOps) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.HistoryMode = true; @@ -645,9 +698,9 @@ TEST(BRepGraph_DeduplicateTest, AnalyzeOnly_CurveAndPCurveCountsReported) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.AnalyzeOnly = true; @@ -688,17 +741,20 @@ TEST(BRepGraph_DeduplicateTest, DefaultResultStruct_AllZeroed) EXPECT_EQ(aRes.NbNullifiedCurves, 0); EXPECT_EQ(aRes.NbHistoryRecords, 0); EXPECT_FALSE(aRes.IsEntityMergeApplied); + EXPECT_EQ(aRes.NbReorderedWires, 0); + EXPECT_EQ(aRes.NbToleranceOrderedWires, 0); + EXPECT_EQ(aRes.NbPartialOrderedWires, 0); } TEST(BRepGraph_DeduplicateTest, MergeDefsWhenSafe_MergesVertices) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - const int aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; @@ -707,7 +763,7 @@ TEST(BRepGraph_DeduplicateTest, MergeDefsWhenSafe_MergesVertices) // Two copied faces share identical vertex positions -> vertices should merge. EXPECT_GT(aRes.NbMergedVertices, 0); EXPECT_TRUE(aRes.IsEntityMergeApplied); - (void)aNbVerticesBefore; + std::ignore = aNbVerticesBefore; } // --------------------------------------------------------------------------- @@ -718,9 +774,9 @@ TEST(BRepGraph_DeduplicateTest, NestedCompound_AllCopiesDeduped) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes19 = - BRepGraph_Builder::Add(aGraph, makeNestedCompound()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes19 = + aGraph.Shapes().Add(makeNestedCompound()); + ASSERT_FALSE(aGraph.IsEmpty()); // 3 copies of the same face across nested compounds: 3 surfaces, 12 curves. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 3); @@ -743,9 +799,9 @@ TEST(BRepGraph_DeduplicateTest, ThreeDistinctPrimitives_MinimalDedup) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes20 = - BRepGraph_Builder::Add(aGraph, makeThreeDistinctPrimitives()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes20 = + aGraph.Shapes().Add(makeThreeDistinctPrimitives()); + ASSERT_FALSE(aGraph.IsEmpty()); // Box(6 faces) + Sphere(1 face) + Cone(3 faces) = 10 faces, 18 edges. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 10); @@ -764,9 +820,9 @@ TEST(BRepGraph_DeduplicateTest, TwoIdenticalBoxes_SurfacesAndCurvesDeduped) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes21 = - BRepGraph_Builder::Add(aGraph, makeTwoIdenticalBoxes()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes21 = + aGraph.Shapes().Add(makeTwoIdenticalBoxes()); + ASSERT_FALSE(aGraph.IsEmpty()); // Two identical boxes: 12 faces, 24 edges, 12 geom surfaces, 24 geom curves. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 12); @@ -793,9 +849,9 @@ TEST(BRepGraph_DeduplicateTest, CurveRewriteCount_MatchesDuplicateEdgeCurves) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes22 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes22 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // TwoCopiedFaces: 8 geom curves, 4 canonical -> 4 duplicates. ASSERT_EQ(aGraph.Topo().Edges().Nb(), 8); @@ -815,13 +871,13 @@ TEST(BRepGraph_DeduplicateTest, AfterDedup_AllCopiedEdgesShareCanonicalCurve) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes23 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes23 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(nbUniqueEdgeCurveDefs(aGraph), 8); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // After dedup: 8 unique curves reduced to 4 canonical. EXPECT_EQ(nbUniqueEdgeCurveDefs(aGraph), 4); @@ -839,9 +895,9 @@ TEST(BRepGraph_DeduplicateTest, MultiplePCurveDups_AllEdgesDeduped) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes24 = - BRepGraph_Builder::Add(aGraph, aCopy.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes24 = + aGraph.Shapes().Add(aCopy.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const int aDupCount = addDuplicatePCurvesToAllEdges(aGraph); ASSERT_GT(aDupCount, 0); @@ -857,14 +913,14 @@ TEST(BRepGraph_DeduplicateTest, PCurveDup_AnalyzeOnly_CountsButNoRewrite) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes25 = - BRepGraph_Builder::Add(aGraph, aCopy.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes25 = + aGraph.Shapes().Add(aCopy.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const int aDupCount = addDuplicatePCurvesToAllEdges(aGraph); ASSERT_GT(aDupCount, 0); - const int aUniqueBefore = nbUniquePCurveNodes(aGraph); + const uint32_t aUniqueBefore = nbUniquePCurveNodes(aGraph); BRepGraph_Deduplicate::Options anOpts; anOpts.AnalyzeOnly = true; @@ -883,9 +939,9 @@ TEST(BRepGraph_DeduplicateTest, NoPCurveDuplicates_ZeroPCurveRewrites) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes26 = - BRepGraph_Builder::Add(aGraph, aCopy.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes26 = + aGraph.Shapes().Add(aCopy.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); } @@ -898,9 +954,9 @@ TEST(BRepGraph_DeduplicateTest, HistoryFindOriginal_TracesBackToCanonical) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes27 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes27 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.HistoryMode = true; @@ -909,20 +965,24 @@ TEST(BRepGraph_DeduplicateTest, HistoryFindOriginal_TracesBackToCanonical) ASSERT_EQ(aRes.NbHistoryRecords, 5); // For each history record, FindOriginal on the replacement should trace back. - for (size_t aRecIdx = 0; aRecIdx < aGraph.History().NbRecords(); ++aRecIdx) + for (size_t aRecIdx = 0; + aRecIdx < aGraph.LayerRegistry().Ensure()->NbRecords(); + ++aRecIdx) { - const BRepGraph_HistoryRecord& aRec = aGraph.History().Record(aRecIdx); - for (NCollection_DataMap>::Iterator + const BRepGraph_LayerHistory::Event& aRec = + aGraph.LayerRegistry().Ensure()->Record(aRecIdx); + for (NCollection_DataMap>::Iterator aMapIter(aRec.Mapping); aMapIter.More(); aMapIter.Next()) { const BRepGraph_NodeId& anOriginal = aMapIter.Key(); - const NCollection_DynamicArray& aReplacements = aMapIter.Value(); - for (int aReplIdx = 0; aReplIdx < aReplacements.Length(); ++aReplIdx) + const NCollection_LinearVector& aReplacements = aMapIter.Value(); + for (size_t aReplIdx = 0; aReplIdx < aReplacements.Size(); ++aReplIdx) { const BRepGraph_NodeId aTraced = - aGraph.History().FindOriginal(aReplacements.Value(aReplIdx)); + aGraph.LayerRegistry().Ensure()->FindOriginal( + aReplacements.Value(aReplIdx)); // FindOriginal should eventually reach a root - the canonical node. EXPECT_TRUE(aTraced.IsValid()); // The original node from the record should match one of the trace results. @@ -936,9 +996,9 @@ TEST(BRepGraph_DeduplicateTest, HistoryFindDerived_ContainsCanonicalNode) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes28 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes28 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.HistoryMode = true; @@ -950,18 +1010,21 @@ TEST(BRepGraph_DeduplicateTest, HistoryFindDerived_ContainsCanonicalNode) // For each history record, FindDerived on the original should contain the replacements. // All records are canonicalize records with 1 replacement (no nullify records). int aNbCanonMappings = 0; - for (size_t aRecIdx = 0; aRecIdx < aGraph.History().NbRecords(); ++aRecIdx) + for (size_t aRecIdx = 0; + aRecIdx < aGraph.LayerRegistry().Ensure()->NbRecords(); + ++aRecIdx) { - const BRepGraph_HistoryRecord& aRec = aGraph.History().Record(aRecIdx); - for (NCollection_DataMap>::Iterator + const BRepGraph_LayerHistory::Event& aRec = + aGraph.LayerRegistry().Ensure()->Record(aRecIdx); + for (NCollection_DataMap>::Iterator aMapIter(aRec.Mapping); aMapIter.More(); aMapIter.Next()) { const BRepGraph_NodeId& anOriginal = aMapIter.Key(); - const NCollection_DynamicArray aDerived = - aGraph.History().FindDerived(anOriginal); - EXPECT_EQ(aDerived.Length(), 1); + const NCollection_LinearVector aDerived = + aGraph.LayerRegistry().Ensure()->FindDerived(anOriginal); + EXPECT_EQ(aDerived.Size(), 1); ++aNbCanonMappings; } } @@ -972,20 +1035,23 @@ TEST(BRepGraph_DeduplicateTest, HistoryRecordSequenceNumbers_AreMonotonic) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes29 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes29 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.HistoryMode = true; - (void)BRepGraph_Deduplicate::Perform(aGraph, anOpts); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); bool isFirst = true; size_t aPrevSeq = 0; - for (size_t aRecIdx = 0; aRecIdx < aGraph.History().NbRecords(); ++aRecIdx) + for (size_t aRecIdx = 0; + aRecIdx < aGraph.LayerRegistry().Ensure()->NbRecords(); + ++aRecIdx) { - const BRepGraph_HistoryRecord& aRec = aGraph.History().Record(aRecIdx); + const BRepGraph_LayerHistory::Event& aRec = + aGraph.LayerRegistry().Ensure()->Record(aRecIdx); if (!isFirst) { EXPECT_GT(aRec.SequenceNumber, aPrevSeq); @@ -999,22 +1065,22 @@ TEST(BRepGraph_DeduplicateTest, HistoryOff_NbRecordsUnchanged) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes30 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes30 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Run once with history to get 5 records (1 surface + 4 curve canonicalizes). BRepGraph_Deduplicate::Options anOpts1; anOpts1.HistoryMode = true; - (void)BRepGraph_Deduplicate::Perform(aGraph, anOpts1); - EXPECT_EQ(aGraph.History().NbRecords(), 5); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts1); + EXPECT_EQ(aGraph.LayerRegistry().Ensure()->NbRecords(), 5); // Fresh graph, run with history off - no records should be added. BRepGraph aGraph2; aGraph2.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes31 = - BRepGraph_Builder::Add(aGraph2, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph2.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes31 = + aGraph2.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph2.IsEmpty()); BRepGraph_Deduplicate::Options anOpts2; anOpts2.HistoryMode = false; @@ -1022,7 +1088,7 @@ TEST(BRepGraph_DeduplicateTest, HistoryOff_NbRecordsUnchanged) const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph2, anOpts2); EXPECT_EQ(aRes.NbSurfaceRewrites, 1); EXPECT_EQ(aRes.NbCurveRewrites, 4); - EXPECT_EQ(aGraph2.History().NbRecords(), 0); + EXPECT_EQ(aGraph2.LayerRegistry().Ensure()->NbRecords(), 0); } // --------------------------------------------------------------------------- @@ -1033,11 +1099,11 @@ TEST(BRepGraph_DeduplicateTest, AfterDedup_AllSurfacesValid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes32 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes32 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); for (BRepGraph_FullFaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -1052,17 +1118,17 @@ TEST(BRepGraph_DeduplicateTest, AfterDedup_AllCurve3dsValid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes33 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes33 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); for (BRepGraph_FullEdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); const BRepGraphInc::EdgeDef& anEdgeDef = aGraph.Topo().Edges().Definition(anEdgeId); - if (!anEdgeDef.IsDegenerate) + if (!BRepGraph_Tool::Edge::Degenerated(aGraph, anEdgeId)) { EXPECT_TRUE(anEdgeDef.Curve3DRepId.IsValid()) << "Edge " << anEdgeId.Index << " has null Curve3d after dedup"; @@ -1078,19 +1144,19 @@ TEST(BRepGraph_DeduplicateTest, AfterDedup_AllInlinePCurvesHaveCurve2d) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes34 = - BRepGraph_Builder::Add(aGraph, aCopy.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes34 = + aGraph.Shapes().Add(aCopy.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)addDuplicatePCurvesToAllEdges(aGraph); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = addDuplicatePCurvesToAllEdges(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); for (BRepGraph_FullEdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeId); - for (int aCEIter = 0; aCEIter < aCoEdgeIdxs.Length(); ++aCEIter) + for (size_t aCEIter = 0; aCEIter < aCoEdgeIdxs.Size(); ++aCEIter) { const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(aCEIter)); @@ -1104,11 +1170,11 @@ TEST(BRepGraph_DeduplicateTest, AfterDedup_CanonicalSurfaceGeomNotNull) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes35 = - BRepGraph_Builder::Add(aGraph, makeNCopiedIdenticalFaces(4)); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes35 = + aGraph.Shapes().Add(makeNCopiedIdenticalFaces(4)); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // All face defs should have a non-null surface after dedup. for (BRepGraph_FullFaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) @@ -1124,17 +1190,17 @@ TEST(BRepGraph_DeduplicateTest, AfterDedup_CanonicalCurveGeomNotNull) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes36 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes36 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); for (BRepGraph_FullEdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); const BRepGraphInc::EdgeDef& anEdgeDef = aGraph.Topo().Edges().Definition(anEdgeId); - if (!anEdgeDef.IsDegenerate) + if (!BRepGraph_Tool::Edge::Degenerated(aGraph, anEdgeId)) { EXPECT_TRUE(anEdgeDef.Curve3DRepId.IsValid()) << "Edge " << anEdgeId.Index << " has null Curve3d after dedup"; @@ -1152,17 +1218,15 @@ TEST(BRepGraph_DeduplicateTest, ParallelBuild_SameResultAsSequential) BRepGraph aGraphSeq; aGraphSeq.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes37 = - BRepGraph_Builder::Add(aGraphSeq, - aCompound, - BRepGraph_Builder::Options{{}, true, false, false}); - ASSERT_TRUE(aGraphSeq.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes37 = + aGraphSeq.Shapes().Add(aCompound, BRepGraph::ShapesView::Options{{}, true, false, false}); + ASSERT_FALSE(aGraphSeq.IsEmpty()); BRepGraph aGraphPar; aGraphPar.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes38 = - BRepGraph_Builder::Add(aGraphPar, aCompound, BRepGraph_Builder::Options{{}, true, false, true}); - ASSERT_TRUE(aGraphPar.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes38 = + aGraphPar.Shapes().Add(aCompound, BRepGraph::ShapesView::Options{{}, true, false, true}); + ASSERT_FALSE(aGraphPar.IsEmpty()); const BRepGraph_Deduplicate::Result aResSeq = BRepGraph_Deduplicate::Perform(aGraphSeq); const BRepGraph_Deduplicate::Result aResPar = BRepGraph_Deduplicate::Perform(aGraphPar); @@ -1187,9 +1251,9 @@ TEST(BRepGraph_DeduplicateTest, TenCopies_AllDeduplicatedToOneSurface) const int aNbCopies = 10; BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes39 = - BRepGraph_Builder::Add(aGraph, makeNCopiedIdenticalFaces(aNbCopies)); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes39 = + aGraph.Shapes().Add(makeNCopiedIdenticalFaces(aNbCopies)); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(aGraph.Topo().Faces().Nb(), aNbCopies); @@ -1203,9 +1267,9 @@ TEST(BRepGraph_DeduplicateTest, TwoCopies_CurveCanonicalCountLessThanTotal) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes40 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes40 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // 8 geom curves, 4 canonical. ASSERT_EQ(aGraph.Topo().Edges().Nb(), 8); @@ -1225,9 +1289,9 @@ TEST(BRepGraph_DeduplicateTest, Idempotent_MixedCompound_SurfacesAndCurves) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes41 = - BRepGraph_Builder::Add(aGraph, makeMixedCompound()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes41 = + aGraph.Shapes().Add(makeMixedCompound()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes1 = BRepGraph_Deduplicate::Perform(aGraph); ASSERT_EQ(aRes1.NbSurfaceRewrites, 1); @@ -1248,9 +1312,9 @@ TEST(BRepGraph_DeduplicateTest, Idempotent_TwoIdenticalBoxes) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes42 = - BRepGraph_Builder::Add(aGraph, makeTwoIdenticalBoxes()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes42 = + aGraph.Shapes().Add(makeTwoIdenticalBoxes()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes1 = BRepGraph_Deduplicate::Perform(aGraph); ASSERT_EQ(aRes1.NbSurfaceRewrites, 6); @@ -1275,13 +1339,13 @@ TEST(BRepGraph_DeduplicateTest, DISABLED_PCurveDedup_RewritesReduceUniquePCurveN BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes43 = - BRepGraph_Builder::Add(aGraph, aCopy.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes43 = + aGraph.Shapes().Add(aCopy.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)addDuplicatePCurvesToAllEdges(aGraph); + std::ignore = addDuplicatePCurvesToAllEdges(aGraph); - const int aUniqueBefore = nbUniquePCurveNodes(aGraph); + const uint32_t aUniqueBefore = nbUniquePCurveNodes(aGraph); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); @@ -1297,38 +1361,38 @@ TEST(BRepGraph_DeduplicateTest, RestoresHistoryFlag_WhenHistoryModeOff) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes44 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes44 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - aGraph.History().SetEnabled(true); - ASSERT_TRUE(aGraph.History().IsEnabled()); + aGraph.LayerRegistry().Ensure()->SetEnabled(true); + ASSERT_TRUE(aGraph.LayerRegistry().Ensure()->IsEnabled()); BRepGraph_Deduplicate::Options anOpts; anOpts.HistoryMode = false; - (void)BRepGraph_Deduplicate::Perform(aGraph, anOpts); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); // Should be restored to the original value (true). - EXPECT_TRUE(aGraph.History().IsEnabled()); + EXPECT_TRUE(aGraph.LayerRegistry().Ensure()->IsEnabled()); } TEST(BRepGraph_DeduplicateTest, RestoresHistoryFlag_AnalyzeOnlyPath) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes45 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes45 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - aGraph.History().SetEnabled(true); + aGraph.LayerRegistry().Ensure()->SetEnabled(true); BRepGraph_Deduplicate::Options anOpts; anOpts.AnalyzeOnly = true; anOpts.HistoryMode = false; - (void)BRepGraph_Deduplicate::Perform(aGraph, anOpts); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); // Restored even when exiting through the AnalyzeOnly early-return. - EXPECT_TRUE(aGraph.History().IsEnabled()); + EXPECT_TRUE(aGraph.LayerRegistry().Ensure()->IsEnabled()); } // --------------------------------------------------------------------------- @@ -1339,16 +1403,16 @@ TEST(BRepGraph_DeduplicateTest, GeomCountsUnchanged_AfterDedup) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes46 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes46 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // TwoCopiedFaces: 2 surfaces, 8 curves, 8 PCurves. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); ASSERT_EQ(aGraph.Topo().Edges().Nb(), 8); ASSERT_EQ(nbPCurveEntries(aGraph), 8); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // Dedup rewrites references, but does not remove geometry nodes. EXPECT_EQ(aGraph.Topo().Faces().Nb(), 2); @@ -1360,15 +1424,15 @@ TEST(BRepGraph_DeduplicateTest, DefCountsUnchanged_AfterDedup) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes47 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes47 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // TwoCopiedFaces: 2 face defs, 8 edge defs. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); ASSERT_EQ(aGraph.Topo().Edges().Nb(), 8); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // Dedup never removes or adds defs. EXPECT_EQ(aGraph.Topo().Faces().Nb(), 2); @@ -1383,15 +1447,15 @@ TEST(BRepGraph_DeduplicateTest, PCurveEntryCount_UnchangedAfterDedup) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes48 = - BRepGraph_Builder::Add(aGraph, aCopy.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes48 = + aGraph.Shapes().Add(aCopy.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)addDuplicatePCurvesToAllEdges(aGraph); + std::ignore = addDuplicatePCurvesToAllEdges(aGraph); - const int aPCEntryCount = nbPCurveEntries(aGraph); + const size_t aPCEntryCount = nbPCurveEntries(aGraph); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // Dedup rewrites PCurve node IDs but doesn't add or remove PCurveEntries. EXPECT_EQ(nbPCurveEntries(aGraph), aPCEntryCount); @@ -1421,9 +1485,8 @@ TEST(BRepGraph_DeduplicateTest, TwoCopiedSphereFaces_Deduped) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes49 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes49 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); // Two sphere face copies: 2 faces, 6 edges. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); @@ -1458,9 +1521,8 @@ TEST(BRepGraph_DeduplicateTest, TwoCopiedCylinderFaces_Deduped) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes50 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes50 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); // Two cylinder face copies: 2 faces, 6 edges, 2 surfaces, 6 curves, 12 PCurves. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); @@ -1494,9 +1556,8 @@ TEST(BRepGraph_DeduplicateTest, DifferentSizedCylinders_NotDeduped) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes51 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes51 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); // Two distinct cylinder faces: 2 surfaces, 6 curves. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); @@ -1521,16 +1582,16 @@ TEST(BRepGraph_DeduplicateTest, BackRefs_SurfaceRewrite_UpdatesFaceDefUsers) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes52 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes52 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Before dedup: each face has its own surface handle. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); EXPECT_NE(BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId::Start()).get(), BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId(1)).get()); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // After dedup: both faces share the same canonical surface pointer. EXPECT_EQ(BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId::Start()).get(), @@ -1541,14 +1602,14 @@ TEST(BRepGraph_DeduplicateTest, BackRefs_CurveRewrite_UpdatesEdgeDefUsers) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes53 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes53 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Before dedup: 8 edges, each with its own curve handle. ASSERT_EQ(aGraph.Topo().Edges().Nb(), 8); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // After dedup: edges with identical curves should share the same pointer. // Count distinct curve pointers across all edges. @@ -1570,11 +1631,11 @@ TEST(BRepGraph_DeduplicateTest, FacesOnSurface_AfterDedup_ReturnsCorrectDefs) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes54 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes54 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph); // After dedup, all face defs point to the same canonical surface. ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); @@ -1588,9 +1649,9 @@ TEST(BRepGraph_DeduplicateTest, Nullify_OrphanedSurface_HandleIsNull) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes55 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes55 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); // No orphan nullification since geometry is stored inline on defs. @@ -1609,9 +1670,9 @@ TEST(BRepGraph_DeduplicateTest, Nullify_OrphanedCurve_HandleIsNull) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes56 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes56 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); // No orphan nullification since geometry is stored inline on defs. @@ -1640,9 +1701,9 @@ TEST(BRepGraph_DeduplicateTest, Nullify_OrphanedPCurve_HandleIsNull) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes57 = - BRepGraph_Builder::Add(aGraph, aCopy.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes57 = + aGraph.Shapes().Add(aCopy.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const int aDupCount = addDuplicatePCurvesToAllEdges(aGraph); ASSERT_GT(aDupCount, 0); @@ -1657,9 +1718,9 @@ TEST(BRepGraph_DeduplicateTest, AnalyzeOnly_NoBackRefChangesOrNullification) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes58 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes58 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Snapshot surface/curve pointers before. NCollection_DynamicArray aSurfPtrs; @@ -1721,12 +1782,12 @@ TEST(BRepGraph_DeduplicateTest, RoundTrip_TwoCopiedFaces_FewerSurfaces) // First graph: build and dedup. BRepGraph aGraph1; aGraph1.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes59 = - BRepGraph_Builder::Add(aGraph1, aCompound); - ASSERT_TRUE(aGraph1.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes59 = + aGraph1.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph1.IsEmpty()); ASSERT_EQ(aGraph1.Topo().Faces().Nb(), 2); - (void)BRepGraph_Deduplicate::Perform(aGraph1); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph1); ASSERT_EQ(nbUniqueFaceSurfaceDefs(aGraph1), 1); // Reconstruct each face individually and assemble into a compound. @@ -1744,9 +1805,9 @@ TEST(BRepGraph_DeduplicateTest, RoundTrip_TwoCopiedFaces_FewerSurfaces) // Second graph: build from reconstructed shape. BRepGraph aGraph2; aGraph2.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes60 = - BRepGraph_Builder::Add(aGraph2, aReconstructed); - ASSERT_TRUE(aGraph2.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes60 = + aGraph2.Shapes().Add(aReconstructed); + ASSERT_FALSE(aGraph2.IsEmpty()); // Face defs count stays 2 (topology defs, not geometry nodes). // After round-trip, unique surface pointer count should be 1 @@ -1761,12 +1822,12 @@ TEST(BRepGraph_DeduplicateTest, RoundTrip_TwoCopiedFaces_FewerCurves) BRepGraph aGraph1; aGraph1.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes61 = - BRepGraph_Builder::Add(aGraph1, aCompound); - ASSERT_TRUE(aGraph1.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes61 = + aGraph1.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph1.IsEmpty()); ASSERT_EQ(aGraph1.Topo().Edges().Nb(), 8); - (void)BRepGraph_Deduplicate::Perform(aGraph1); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph1); ASSERT_EQ(nbUniqueEdgeCurveDefs(aGraph1), 4); // Reconstruct each face individually. @@ -1783,9 +1844,9 @@ TEST(BRepGraph_DeduplicateTest, RoundTrip_TwoCopiedFaces_FewerCurves) BRepGraph aGraph2; aGraph2.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes62 = - BRepGraph_Builder::Add(aGraph2, aReconstructed); - ASSERT_TRUE(aGraph2.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes62 = + aGraph2.Shapes().Add(aReconstructed); + ASSERT_FALSE(aGraph2.IsEmpty()); // Edge defs count stays 8 (topology defs, not geometry nodes). // After round-trip, unique curve pointer count should be 4 @@ -1813,9 +1874,8 @@ TEST(BRepGraph_DeduplicateTest, Build_SharedTFace_OneSurfaceNode) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes63 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes63 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); // Both face usages share the same TFace -> same raw surface pointer -> one surface node. EXPECT_EQ(aGraph.Topo().Faces().Nb(), 1); @@ -1827,16 +1887,16 @@ TEST(BRepGraph_DeduplicateTest, RoundTrip_TwoBoxes_GeomReduction) BRepGraph aGraph1; aGraph1.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes64 = - BRepGraph_Builder::Add(aGraph1, aCompound); - ASSERT_TRUE(aGraph1.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes64 = + aGraph1.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph1.IsEmpty()); - const int aSurfsBefore = aGraph1.Topo().Faces().Nb(); - const int aCurvesBefore = aGraph1.Topo().Edges().Nb(); + const uint32_t aSurfsBefore = aGraph1.Topo().Faces().Nb(); + const uint32_t aCurvesBefore = aGraph1.Topo().Edges().Nb(); ASSERT_EQ(aSurfsBefore, 12); ASSERT_EQ(aCurvesBefore, 24); - (void)BRepGraph_Deduplicate::Perform(aGraph1); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph1); // Force reconstruction from deduped graph. BRepGraph_NodeId aRootId(BRepGraph_NodeId::Kind::Compound, 0); @@ -1846,9 +1906,9 @@ TEST(BRepGraph_DeduplicateTest, RoundTrip_TwoBoxes_GeomReduction) // Build second graph. BRepGraph aGraph2; aGraph2.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes65 = - BRepGraph_Builder::Add(aGraph2, aReconstructed); - ASSERT_TRUE(aGraph2.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes65 = + aGraph2.Shapes().Add(aReconstructed); + ASSERT_FALSE(aGraph2.IsEmpty()); // Face/edge def counts stay the same (topology defs, not geometry nodes). EXPECT_EQ(aGraph2.Topo().Faces().Nb(), aSurfsBefore); @@ -1868,11 +1928,11 @@ TEST(BRepGraph_DeduplicateTest, MergeVertices_SharedVerticesReduced) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes66 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes66 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - const int aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; @@ -1894,9 +1954,9 @@ TEST(BRepGraph_DeduplicateTest, MergeEdges_SharedEdgesReduced) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes67 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes67 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; @@ -1909,9 +1969,9 @@ TEST(BRepGraph_DeduplicateTest, MergeWires_IdenticalWiresMerged) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes68 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes68 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; @@ -1925,9 +1985,9 @@ TEST(BRepGraph_DeduplicateTest, MergeFaces_IdenticalFacesMerged) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes69 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes69 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; @@ -1941,9 +2001,9 @@ TEST(BRepGraph_DeduplicateTest, MergeDefsWhenSafe_False_NoMerge) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes70 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes70 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); // Default: MergeEntitiesWhenSafe = false. const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); @@ -1958,11 +2018,11 @@ TEST(BRepGraph_DeduplicateTest, AnalyzeOnly_MergeDefsWhenSafe_CountsOnly) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes71 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes71 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); - const int aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; @@ -1980,9 +2040,9 @@ TEST(BRepGraph_DeduplicateTest, HistoryRecords_MergePhases) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes72 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes72 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; @@ -1994,28 +2054,34 @@ TEST(BRepGraph_DeduplicateTest, HistoryRecords_MergePhases) countHistoryRecordsByOp(aGraph, TCollection_AsciiString("Dedup:MergeVertex")); const int aNbEdgeMerge = countHistoryRecordsByOp(aGraph, TCollection_AsciiString("Dedup:MergeEdge")); + const int aNbWireMerge = + countHistoryRecordsByOp(aGraph, TCollection_AsciiString("Dedup:MergeWire")); + const int aNbFaceMerge = + countHistoryRecordsByOp(aGraph, TCollection_AsciiString("Dedup:MergeFace")); EXPECT_EQ(aNbVertexMerge, aRes.NbMergedVertices); EXPECT_EQ(aNbEdgeMerge, aRes.NbMergedEdges); + EXPECT_EQ(aNbWireMerge, aRes.NbMergedWires); + EXPECT_EQ(aNbFaceMerge, aRes.NbMergedFaces); } TEST(BRepGraph_DeduplicateTest, AfterMerge_Validate_NoIssues) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes73 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes73 = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; - (void)BRepGraph_Deduplicate::Perform(aGraph, anOpts); + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); // After merge + compact, graph should be structurally valid. // Merge alone may leave stale back-references on geometry nodes // that are cleaned up by compaction. - (void)BRepGraph_Compact::Perform(aGraph); + std::ignore = BRepGraph_Compact::Perform(aGraph); const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(aGraph); EXPECT_TRUE(aValResult.IsValid()); @@ -2023,179 +2089,641 @@ TEST(BRepGraph_DeduplicateTest, AfterMerge_Validate_NoIssues) //================================================================================================= -TEST(BRepGraph_DeduplicateTest, VertexMerge_UpdatesInternalEdgeVertexRefs) +TEST(BRepGraph_DeduplicateTest, MergeFaces_SameSurfaceDifferentWires_NotMerged) { - // Regression for Bug A1: Phase 1 vertex merge must update EdgeDef.InternalVertexRefIds. - // Without the fix, BRepGraph_Compact would silently drop the internal vertex ref. + // Create two faces on the same planar surface (Z=0) with different boundary wires. + // They share the same underlying surface pointer after dedup canonicalization, + // but have different wire topologies - they should NOT be merged. + + // Face 1: a rectangle from (0,0) to (10,20) on Z=0 plane. + gp_Pln aPlane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + gp_Pnt aP1(0, 0, 0); + gp_Pnt aP2(10, 0, 0); + gp_Pnt aP3(10, 20, 0); + gp_Pnt aP4(0, 20, 0); + + BRep_Builder aBuilder; + TopoDS_Wire aWire1; + aBuilder.MakeWire(aWire1); + aBuilder.Add(aWire1, BRepBuilderAPI_MakeEdge(aP1, aP2)); + aBuilder.Add(aWire1, BRepBuilderAPI_MakeEdge(aP2, aP3)); + aBuilder.Add(aWire1, BRepBuilderAPI_MakeEdge(aP3, aP4)); + aBuilder.Add(aWire1, BRepBuilderAPI_MakeEdge(aP4, aP1)); + + TopoDS_Face aFace1 = BRepBuilderAPI_MakeFace(aPlane, aWire1); + + // Face 2: a different rectangle on the same Z=0 plane. + gp_Pnt aQ1(30, 0, 0); + gp_Pnt aQ2(40, 0, 0); + gp_Pnt aQ3(40, 10, 0); + gp_Pnt aQ4(30, 10, 0); + + TopoDS_Wire aWire2; + aBuilder.MakeWire(aWire2); + aBuilder.Add(aWire2, BRepBuilderAPI_MakeEdge(aQ1, aQ2)); + aBuilder.Add(aWire2, BRepBuilderAPI_MakeEdge(aQ2, aQ3)); + aBuilder.Add(aWire2, BRepBuilderAPI_MakeEdge(aQ3, aQ4)); + aBuilder.Add(aWire2, BRepBuilderAPI_MakeEdge(aQ4, aQ1)); + + TopoDS_Face aFace2 = BRepBuilderAPI_MakeFace(aPlane, aWire2); + ASSERT_FALSE(aFace1.IsNull()); + ASSERT_FALSE(aFace2.IsNull()); + + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aFace1); + aBuilder.Add(aCompound, aFace2); + BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes74 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); - ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); - ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); - // Add a duplicate vertex at the same position as vertex[0]. - const BRepGraph_VertexId aBaseV = BRepGraph_VertexId::Start(); - const gp_Pnt aSamePosition = BRepGraph_Tool::Vertex::Pnt(aGraph, aBaseV); - const double aTol = BRepGraph_Tool::Vertex::Tolerance(aGraph, aBaseV); - const BRepGraph_VertexId aDupV = aGraph.Editor().Vertices().Add(aSamePosition, aTol); - ASSERT_TRUE(aDupV.IsValid()); + ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); - // Attach the duplicate vertex as an internal vertex of edge[0]. - const BRepGraph_EdgeId aEdge0 = BRepGraph_EdgeId::Start(); - const BRepGraph_VertexRefId aIntRef = - aGraph.Editor().Edges().AddInternalVertex(aEdge0, aDupV, TopAbs_INTERNAL); - ASSERT_TRUE(aIntRef.IsValid()); - ASSERT_EQ(aGraph.Topo().Edges().Definition(aEdge0).InternalVertexRefIds.Length(), 1); - - // Run full vertex (and subsequent entity) merge. - BRepGraph_Deduplicate::Options anOpts; - anOpts.MergeEntitiesWhenSafe = true; - const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); - EXPECT_GE(aRes.NbMergedVertices, 1); - - // The internal edge ref must point to the canonical (non-removed) vertex. - const BRepGraphInc::EdgeDef& anEdgeDef = aGraph.Topo().Edges().Definition(aEdge0); - ASSERT_EQ(anEdgeDef.InternalVertexRefIds.Length(), 1); - const BRepGraph_VertexRefId anUpdRef = anEdgeDef.InternalVertexRefIds.Value(0); - const BRepGraphInc::VertexRef& anRef = aGraph.Refs().Vertices().Entry(anUpdRef); - EXPECT_FALSE(anRef.IsRemoved); - EXPECT_FALSE(aGraph.Topo().Vertices().Definition(anRef.VertexDefId).IsRemoved); - - // After Compact the ref must survive (not be silently dropped). - (void)BRepGraph_Compact::Perform(aGraph); - - bool aFoundInternalRef = false; - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - if (aGraph.Topo().Edges().Definition(anEdgeId).InternalVertexRefIds.Length() > 0) - { - aFoundInternalRef = true; - break; - } - } - EXPECT_TRUE(aFoundInternalRef) << "InternalVertexRef was silently dropped by Compact"; - - const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(aGraph); - EXPECT_TRUE(aValResult.IsValid()); -} - -//================================================================================================= - -TEST(BRepGraph_DeduplicateTest, VertexMerge_UpdatesFaceDirectVertexRefs) -{ - // Regression for Bug A2: Phase 1 vertex merge must update FaceDef.VertexRefIds. - // Without the fix, BRepGraph_Compact would silently drop the direct face vertex ref. - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes75 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); - ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); - ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); - - // Add a duplicate vertex at the same position as vertex[0]. - const BRepGraph_VertexId aBaseV = BRepGraph_VertexId::Start(); - const gp_Pnt aSamePosition = BRepGraph_Tool::Vertex::Pnt(aGraph, aBaseV); - const double aTol = BRepGraph_Tool::Vertex::Tolerance(aGraph, aBaseV); - const BRepGraph_VertexId aDupV = aGraph.Editor().Vertices().Add(aSamePosition, aTol); - ASSERT_TRUE(aDupV.IsValid()); - - // Attach the duplicate vertex directly to face[0]. - const BRepGraph_FaceId aFace0 = BRepGraph_FaceId::Start(); - const BRepGraph_VertexRefId aFaceRef = - aGraph.Editor().Faces().AddVertex(aFace0, aDupV, TopAbs_INTERNAL); - ASSERT_TRUE(aFaceRef.IsValid()); - ASSERT_EQ(aGraph.Topo().Faces().Definition(aFace0).VertexRefIds.Length(), 1); - - // Run full vertex (and subsequent entity) merge. - BRepGraph_Deduplicate::Options anOpts; - anOpts.MergeEntitiesWhenSafe = true; - const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); - EXPECT_GE(aRes.NbMergedVertices, 1); - - // The face direct vertex ref must point to the canonical (non-removed) vertex. - const BRepGraphInc::FaceDef& aFaceDef = aGraph.Topo().Faces().Definition(aFace0); - ASSERT_EQ(aFaceDef.VertexRefIds.Length(), 1); - const BRepGraph_VertexRefId anUpdRef = aFaceDef.VertexRefIds.Value(0); - const BRepGraphInc::VertexRef& anRef = aGraph.Refs().Vertices().Entry(anUpdRef); - EXPECT_FALSE(anRef.IsRemoved); - EXPECT_FALSE(aGraph.Topo().Vertices().Definition(anRef.VertexDefId).IsRemoved); - - // After Compact the ref must survive (not be silently dropped). - (void)BRepGraph_Compact::Perform(aGraph); - - bool aFoundFaceVertexRef = false; - for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) - { - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - if (aGraph.Topo().Faces().Definition(aFaceId).VertexRefIds.Length() > 0) - { - aFoundFaceVertexRef = true; - break; - } - } - EXPECT_TRUE(aFoundFaceVertexRef) << "Face direct VertexRef was silently dropped by Compact"; - - const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(aGraph); - EXPECT_TRUE(aValResult.IsValid()); -} - -//================================================================================================= - -TEST(BRepGraph_DeduplicateTest, WireDedup_PreservesShellAuxChildRefs) -{ - // Regression for Bug A3: Phase 3 wire merge must update ShellDef.AuxChildRefIds. - // Deduplicate redirects AuxChildRef from the removed (old) wire to the canonical wire. - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes76 = - BRepGraph_Builder::Add(aGraph, makeTwoCopiedIdenticalFaces()); - ASSERT_TRUE(aGraph.IsDone()); - - // Two wires are built (one outer wire per copied face). - // The highest-indexed wire belongs to the second face copy and will become the "old" - // (removed) wire when dedup merges duplicate wires bottom-up (canonical = lower index). - const int aNbWiresBefore = aGraph.Topo().Wires().Nb(); - ASSERT_GE(aNbWiresBefore, 2); - const BRepGraph_WireId aWireToAttach = BRepGraph_WireId(aNbWiresBefore - 1); - - // Add a new shell and register the duplicate wire as an auxiliary child. - const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); - ASSERT_TRUE(aShell.IsValid()); - const BRepGraph_ChildRefId anAuxRef = - aGraph.Editor().Shells().AddChild(aShell, aWireToAttach, TopAbs_FORWARD); - ASSERT_TRUE(anAuxRef.IsValid()); - ASSERT_EQ(aGraph.Topo().Shells().Definition(aShell).AuxChildRefIds.Length(), 1); - - // Run full entity merge (vertex -> edge -> wire -> face). BRepGraph_Deduplicate::Options anOpts; anOpts.MergeEntitiesWhenSafe = true; - (void)BRepGraph_Deduplicate::Perform(aGraph, anOpts); - // After merge, the shell's AuxChildRef must point to a non-removed (canonical) wire. - int aNbActiveAuxChildren = 0; - for (BRepGraph_ShellIterator aShellIt(aGraph); aShellIt.More(); aShellIt.Next()) + const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + // Both faces share the same planar surface. + EXPECT_EQ(BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId::Start()).get(), + BRepGraph_Tool::Face::Surface(aGraph, BRepGraph_FaceId(1)).get()); + + // But they should NOT be merged because their wires are different. + // Count active (non-removed) faces. + int aNbActiveFaces = 0; + for (BRepGraph_FullFaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraph_ShellId aShellId = aShellIt.CurrentId(); - const BRepGraphInc::ShellDef& aSh = aGraph.Topo().Shells().Definition(aShellId); - for (int aRefIdx = 0; aRefIdx < aSh.AuxChildRefIds.Length(); ++aRefIdx) + if (!aFaceIt.CurrentId().IsRemoved(aGraph)) { - const BRepGraphInc::ChildRef& aRef = - aGraph.Refs().Children().Entry(aSh.AuxChildRefIds.Value(aRefIdx)); - if (aRef.IsRemoved) + ++aNbActiveFaces; + } + } + EXPECT_EQ(aNbActiveFaces, 2) + << "Faces with same surface but different wires were incorrectly merged"; + EXPECT_EQ(aRes.NbMergedFaces, 0) << "Faces with different wires should not be merged"; +} + +// --------------------------------------------------------------------------- +// Edge-case: Sphere seam edge PCurve survival through Dedup+Compact+Reconstruct +// --------------------------------------------------------------------------- + +TopoDS_Compound makeThreeCopiedCylinderFaces() +{ + BRepPrimAPI_MakeCylinder aCylMaker(5.0, 20.0); + const TopoDS_Shape& aCyl = aCylMaker.Shape(); + + TopExp_Explorer anExp(aCyl, TopAbs_FACE); + if (!anExp.More()) + { + return TopoDS_Compound(); + } + const TopoDS_Shape aFace = anExp.Current(); + + BRepBuilderAPI_Copy aCopy1(aFace, true); + BRepBuilderAPI_Copy aCopy2(aFace, true); + BRepBuilderAPI_Copy aCopy3(aFace, true); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aCopy1.Shape()); + aBuilder.Add(aCompound, aCopy2.Shape()); + aBuilder.Add(aCompound, aCopy3.Shape()); + return aCompound; +} + +TopoDS_Shape makeFullSphere() +{ + return BRepPrimAPI_MakeSphere(10.0).Shape(); +} + +int countValidCoEdgePCurves(const BRepGraph& theGraph) +{ + int aCount = 0; + for (BRepGraph_FullCoEdgeIterator aCEIt(theGraph); aCEIt.More(); aCEIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCEIt.CurrentId(); + if (!aCoEdgeId.IsRemoved(theGraph) + && theGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId.IsValid()) + { + const occ::handle& aPCurve = + BRepGraph_Tool::CoEdge::PCurve(theGraph, aCEIt.CurrentId()); + if (!aPCurve.IsNull()) { - continue; - } - const BRepGraphInc::WireDef& aWireDef = - aGraph.Topo().Wires().Definition(BRepGraph_WireId(aRef.ChildDefId)); - if (!aWireDef.IsRemoved) - { - ++aNbActiveAuxChildren; + ++aCount; } } } - EXPECT_EQ(aNbActiveAuxChildren, 1) << "AuxChildRef points to a removed wire after dedup"; + return aCount; +} + +int countSeamCoEdges(const BRepGraph& theGraph) +{ + int aCount = 0; + for (BRepGraph_FullCoEdgeIterator aCEIt(theGraph); aCEIt.More(); aCEIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCEIt.CurrentId(); + const BRepGraphInc::CoEdgeDef& aCE = theGraph.Topo().CoEdges().Definition(aCoEdgeId); + if (!aCoEdgeId.IsRemoved(theGraph) && aCE.FaceId.IsValid() && aCE.ChildEdgeId.IsValid()) + { + const NCollection_LinearVector& aSiblings = + theGraph.Topo().Edges().CoEdges(aCE.ChildEdgeId); + for (const BRepGraph_CoEdgeId& aOtherId : aSiblings) + { + if (aOtherId == aCoEdgeId) + { + continue; + } + const BRepGraphInc::CoEdgeDef& aOther = theGraph.Topo().CoEdges().Definition(aOtherId); + if (!aOtherId.IsRemoved(theGraph) && aOther.FaceId == aCE.FaceId + && aOther.Orientation != aCE.Orientation) + { + ++aCount; + break; + } + } + } + } + return aCount; +} + +TEST(BRepGraph_DeduplicateTest, Sphere_SeamPCurve_SurvivesDedupCompactReconstruct) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeFullSphere()); + ASSERT_FALSE(aGraph.IsEmpty()); + + // Record PCurve count and seam coedge count before dedup. + const int aPCurvesBefore = countValidCoEdgePCurves(aGraph); + const int aSeamCoEdgesBefore = countSeamCoEdges(aGraph); + ASSERT_EQ(aPCurvesBefore, 4) << "Full sphere must have 4 PCurves"; + ASSERT_EQ(aSeamCoEdgesBefore, 2) << "Full sphere must have 2 seam coedges"; + + // Run full pipeline: dedup (with merge) -> compact. + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + // Verify all PCurves are still non-null after dedup. + for (BRepGraph_FullCoEdgeIterator aCEIt(aGraph); aCEIt.More(); aCEIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCEIt.CurrentId(); + if (!aCoEdgeId.IsRemoved(aGraph) + && aGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId.IsValid()) + { + const occ::handle& aPCurve = BRepGraph_Tool::CoEdge::PCurve(aGraph, aCoEdgeId); + EXPECT_FALSE(aPCurve.IsNull()) + << "CoEdge " << aCoEdgeId.Index << " has null PCurve after dedup"; + } + } + + std::ignore = BRepGraph_Compact::Perform(aGraph); + + // Verify all PCurves survived compact. + const int aPCurvesAfter = countValidCoEdgePCurves(aGraph); + EXPECT_EQ(aPCurvesAfter, aPCurvesBefore) << "PCurve count changed after dedup+compact on sphere"; + + // Verify seam coedges still exist. + const int aSeamCoEdgesAfter = countSeamCoEdges(aGraph); + EXPECT_EQ(aSeamCoEdgesAfter, aSeamCoEdgesBefore) + << "Seam coedge count changed after dedup+compact"; + + // Validate graph structure. + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()) << "Graph invalid after dedup+compact on sphere"; + + // Reconstruct root shape and verify BRep validity. + ASSERT_EQ(aGraph.RootProductIds().Size(), 1); + const TopoDS_Shape aReconstructed = + aGraph.Shapes().Reconstruct(BRepGraph_NodeId(aGraph.RootProductIds().Value(0))); + ASSERT_FALSE(aReconstructed.IsNull()); + BRepCheck_Analyzer anAnalyzer(aReconstructed); + EXPECT_TRUE(anAnalyzer.IsValid()) << "Reconstructed sphere is not valid after dedup+compact"; +} + +TEST(BRepGraph_DeduplicateTest, ThreeCopiedCylinderFaces_PCurvesSurviveFullPipeline) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeThreeCopiedCylinderFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); + + ASSERT_EQ(aGraph.Topo().Faces().Nb(), 3); + const uint32_t aEdgesBefore = aGraph.Topo().Edges().Nb(); + ASSERT_EQ(aEdgesBefore, 9) << "Three cylinder cap faces: 3 copies * 3 edges each = 9"; + ASSERT_EQ(countValidCoEdgePCurves(aGraph), 12) + << "Three cylinder faces: 3 copies * 4 coedges each = 12 PCurves"; + + // Run dedup with entity merge. + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + const BRepGraph_Deduplicate::Result aDedupRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + // All surviving coedges must have non-null PCurves. + for (BRepGraph_FullCoEdgeIterator aCEIt(aGraph); aCEIt.More(); aCEIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCEIt.CurrentId(); + if (!aCoEdgeId.IsRemoved(aGraph) + && aGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId.IsValid()) + { + const occ::handle& aPCurve = BRepGraph_Tool::CoEdge::PCurve(aGraph, aCoEdgeId); + EXPECT_FALSE(aPCurve.IsNull()) + << "CoEdge " << aCoEdgeId.Index << " PCurve is null after dedup"; + } + } + + // Compact. + const BRepGraph_Compact::Result aCompactRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aCompactRes.NbRemovedEdges, 6) + << "3 copies merged to 1 face: 3 edges survive, 6 removed by compact"; + + // All surviving coedges after compact must still have non-null PCurves. + for (BRepGraph_FullCoEdgeIterator aCEIt(aGraph); aCEIt.More(); aCEIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCEIt.CurrentId(); + if (!aCoEdgeId.IsRemoved(aGraph) + && aGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId.IsValid()) + { + const occ::handle& aPCurve = BRepGraph_Tool::CoEdge::PCurve(aGraph, aCoEdgeId); + EXPECT_FALSE(aPCurve.IsNull()) + << "CoEdge " << aCoEdgeId.Index << " PCurve is null after compact"; + } + } + + // Validate graph. + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()) << "Graph invalid after dedup+compact on cylinders"; + + // Reconstruct each face and validate individually. + for (BRepGraph_FullFaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + if (aFaceIt.CurrentId().IsRemoved(aGraph)) + { + continue; + } + const TopoDS_Shape aFace = aGraph.Shapes().Reconstruct(aFaceIt.CurrentId()); + ASSERT_FALSE(aFace.IsNull()); + BRepCheck_Analyzer anFA(aFace); + EXPECT_TRUE(anFA.IsValid()) << "Reconstructed face " << aFaceIt.CurrentId().Index + << " is not valid"; + } +} + +TopoDS_Compound makeThreeCopiedBoxes() +{ + BRepPrimAPI_MakeBox aBoxMaker1(10.0, 20.0, 30.0); + BRepPrimAPI_MakeBox aBoxMaker2(10.0, 20.0, 30.0); + BRepPrimAPI_MakeBox aBoxMaker3(10.0, 20.0, 30.0); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aBoxMaker1.Shape()); + aBuilder.Add(aCompound, aBoxMaker2.Shape()); + aBuilder.Add(aCompound, aBoxMaker3.Shape()); + return aCompound; +} + +TEST(BRepGraph_DeduplicateTest, ThreeCopiedBoxes_DedupMergeCompact_FacesKeepOuterWires) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeThreeCopiedBoxes()); + ASSERT_FALSE(aGraph.IsEmpty()); + + ASSERT_EQ(aGraph.Topo().Faces().Nb(), 18) << "3 boxes * 6 faces each"; + ASSERT_EQ(aGraph.Topo().Solids().Nb(), 3); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + const BRepGraph_Deduplicate::Result aDedupRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + EXPECT_EQ(aDedupRes.NbMergedVertices, 16); + EXPECT_EQ(aDedupRes.NbMergedEdges, 24); + EXPECT_EQ(aDedupRes.NbMergedWires, 12); + EXPECT_EQ(aDedupRes.NbMergedFaces, 12); + + // Compact. + const BRepGraph_Compact::Result aCompactRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aCompactRes.NbRemovedFaces, 12); + EXPECT_EQ(aCompactRes.NbRemovedEdges, 24); + + // Every remaining box face must keep exactly one wire. + for (BRepGraph_FullFaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + if (aFaceId.IsRemoved(aGraph)) + { + continue; + } + int aWireCount = 0; + for (const BRepGraph_WireRefId& aRefId : aGraph.Topo().Faces().Relations(aFaceId).WireRefIds) + { + if (!aGraph.Refs().Gen().IsRemoved(aRefId)) + { + ++aWireCount; + } + } + EXPECT_EQ(aWireCount, 1) << "Face " << aFaceId.Index << " should have exactly 1 wire"; + } + + // Validate graph. + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()) << "Graph invalid after dedup+compact on 3 boxes"; + + // Reconstruct remaining solids and validate. + for (BRepGraph_FullSolidIterator aSolidIt(aGraph); aSolidIt.More(); aSolidIt.Next()) + { + if (aSolidIt.CurrentId().IsRemoved(aGraph)) + { + continue; + } + const TopoDS_Shape aSolid = aGraph.Shapes().Reconstruct(aSolidIt.CurrentId()); + ASSERT_FALSE(aSolid.IsNull()); + BRepCheck_Analyzer anSA(aSolid); + EXPECT_TRUE(anSA.IsValid()) << "Reconstructed solid " << aSolidIt.CurrentId().Index + << " is not valid"; + } +} + +TEST(BRepGraph_DeduplicateTest, SelfLoopEdge_AfterVertexMerge_OrientationCorrect) +{ + // A self-loop edge (start == end vertex) should not be marked as reversed + // during edge merge after vertex deduplication. + + // Build two identical triangles (3 edges each, different vertex instances + // at the same positions). After vertex merge, all vertices at the same + // position are canonicalized. The edges should merge correctly. + BRepPrimAPI_MakeBox aBoxMaker1(10.0, 10.0, 10.0); + BRepPrimAPI_MakeBox aBoxMaker2(10.0, 10.0, 10.0); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aBoxMaker1.Shape()); + aBuilder.Add(aCompound, aBoxMaker2.Shape()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + + ASSERT_EQ(aGraph.Topo().Solids().Nb(), 2); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + // Compact. + std::ignore = BRepGraph_Compact::Perform(aGraph); + + // Validate. + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()) << "Graph invalid after dedup+compact on two boxes"; + + // Reconstruct remaining solids. + for (BRepGraph_FullSolidIterator aSolidIt(aGraph); aSolidIt.More(); aSolidIt.Next()) + { + if (aSolidIt.CurrentId().IsRemoved(aGraph)) + { + continue; + } + const TopoDS_Shape aSolid = aGraph.Shapes().Reconstruct(aSolidIt.CurrentId()); + ASSERT_FALSE(aSolid.IsNull()); + EXPECT_TRUE(aSolid.ShapeType() == TopAbs_SOLID); + } +} + +// --------------------------------------------------------------------------- +// Edge merge tests +// --------------------------------------------------------------------------- + +TEST(BRepGraph_DeduplicateTest, EdgeMerge_ThreeCopiedEdges_GraphValidates) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeThreeCopiedIdenticalEdges()); + ASSERT_FALSE(aGraph.IsEmpty()); + + ASSERT_EQ(aGraph.Topo().Edges().Nb(), 3); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + + const BRepGraph_Deduplicate::Result aDedupRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + EXPECT_EQ(aDedupRes.NbMergedEdges, 2); + + std::ignore = BRepGraph_Compact::Perform(aGraph); + + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()); +} + +// --------------------------------------------------------------------------- +// Face merge tests +// --------------------------------------------------------------------------- + +TEST(BRepGraph_DeduplicateTest, FaceMerge_TwoIdenticalSingularFaces_Merge) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeTwoCopiedIdenticalFaces()); + ASSERT_FALSE(aGraph.IsEmpty()); + + ASSERT_EQ(aGraph.Topo().Faces().Nb(), 2); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + + const BRepGraph_Deduplicate::Result aDedupRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + EXPECT_EQ(aDedupRes.NbMergedFaces, 1); + + std::ignore = BRepGraph_Compact::Perform(aGraph); + + int aNbActiveFaces = 0; + for (BRepGraph_FullFaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + if (!aFaceIt.CurrentId().IsRemoved(aGraph)) + { + ++aNbActiveFaces; + } + } + EXPECT_EQ(aNbActiveFaces, 1); + + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()); +} + +// --------------------------------------------------------------------------- +// Edge-case: edge face counts after merge on face-touching boxes +// --------------------------------------------------------------------------- + +TEST(BRepGraph_DeduplicateTest, DedupMerge_TwoBoxesWithSharedEdge_EdgeFaceCountCorrect) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeTwoFaceTouchingBoxes()); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + uint32_t aNbEdgesWith2Faces = 0; + for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) + { + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + const NCollection_LinearVector& aCoEdgeIdxs = + aGraph.Topo().Edges().CoEdges(anEdgeId); + + NCollection_FlatMap aFaceSet; + for (size_t aCEIdx = 0; aCEIdx < aCoEdgeIdxs.Size(); ++aCEIdx) + { + const BRepGraph_CoEdgeId aCEId = aCoEdgeIdxs.Value(aCEIdx); + const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCEId); + if (!aCEId.IsRemoved(aGraph) && aCE.FaceId.IsValid()) + { + aFaceSet.Add(aCE.FaceId); + } + } + + const uint32_t aNbFaces = static_cast(aFaceSet.Size()); + if (aNbFaces == 2) + { + ++aNbEdgesWith2Faces; + } + } + + // Two face-touching boxes share 4 edges on the common face. + // After merge, edges from both boxes on the shared plane collapse, + // giving those edges 2 faces. Additional edges also have 2 faces + // since each edge within a box is shared by 2 faces. + EXPECT_EQ(aNbEdgesWith2Faces, 16); + + // Compact and validate. + std::ignore = BRepGraph_Compact::Perform(aGraph); + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()); +} + +// --------------------------------------------------------------------------- +// Edge-case: different-sized spheres should not be merged +// --------------------------------------------------------------------------- + +TEST(BRepGraph_DeduplicateTest, DedupMerge_TwoSpheres_DifferentSize_NotMerged) +{ + BRepPrimAPI_MakeSphere aSphereMaker1(5.0); + BRepPrimAPI_MakeSphere aSphereMaker2(10.0); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aSphereMaker1.Shape()); + aBuilder.Add(aCompound, aSphereMaker2.Shape()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + EXPECT_EQ(aRes.NbMergedFaces, 0); + EXPECT_EQ(aRes.NbMergedEdges, 0); + + int aNbActiveFaces = 0; + for (BRepGraph_FullFaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + if (!aFaceIt.CurrentId().IsRemoved(aGraph)) + { + ++aNbActiveFaces; + } + } + EXPECT_EQ(aNbActiveFaces, 2); +} + +// --------------------------------------------------------------------------- +// Edge-case: three identical boxes - explicit merge count assertions +// --------------------------------------------------------------------------- + +TEST(BRepGraph_DeduplicateTest, DedupMerge_ThreeBoxes_AllEdgesMergeCorrectly) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeThreeCopiedBoxes()); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + const BRepGraph_Deduplicate::Result aDedupRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + EXPECT_EQ(aDedupRes.NbMergedVertices, 16); + EXPECT_EQ(aDedupRes.NbMergedEdges, 24); + EXPECT_EQ(aDedupRes.NbMergedWires, 12); + EXPECT_EQ(aDedupRes.NbMergedFaces, 12); + + const BRepGraph_Compact::Result aCompactRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aCompactRes.NbRemovedFaces, 12); + EXPECT_EQ(aCompactRes.NbRemovedEdges, 24); + + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()); + + for (BRepGraph_FullFaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + if (aFaceId.IsRemoved(aGraph)) + { + continue; + } + int aWireCount = 0; + for (const BRepGraph_WireRefId& aRefId : aGraph.Topo().Faces().Relations(aFaceId).WireRefIds) + { + if (!aGraph.Refs().Gen().IsRemoved(aRefId)) + { + ++aWireCount; + } + } + EXPECT_EQ(aWireCount, 1); + } +} + +// --------------------------------------------------------------------------- +// Edge-case: sphere self-loop / degenerate edge orientation after merge +// --------------------------------------------------------------------------- + +TEST(BRepGraph_DeduplicateTest, DedupMerge_SelfLoopEdge_OrientationPreserved) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(makeFullSphere()); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + std::ignore = BRepGraph_Compact::Perform(aGraph); + + const BRepGraph_Validate::Result aVal = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aVal.IsValid()); + + ASSERT_EQ(aGraph.RootProductIds().Size(), 1); + const TopoDS_Shape aReconstructed = + aGraph.Shapes().Reconstruct(BRepGraph_NodeId(aGraph.RootProductIds().Value(0))); + ASSERT_FALSE(aReconstructed.IsNull()); + BRepCheck_Analyzer anAnalyzer(aReconstructed); + EXPECT_TRUE(anAnalyzer.IsValid()); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_DeferredInvalidation_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_DeferredInvalidation_Test.cxx index a9c62f0da8..1db98af83d 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_DeferredInvalidation_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_DeferredInvalidation_Test.cxx @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include #include "BRepGraph_RefTestTools.hxx" -#include #include #include #include @@ -38,9 +38,8 @@ protected: BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBox); - ASSERT_TRUE(myGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = myGraph.Shapes().Add(aBox); + ASSERT_FALSE(myGraph.IsEmpty()); } BRepGraph myGraph; @@ -63,20 +62,20 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_PropagatesUpOnFlush) myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); // During deferred mode: edge is mutated, but parent wire/face are NOT yet. - const NCollection_DynamicArray& aWires = - myGraph.Topo().Edges().Wires(BRepGraph_EdgeId::Start()); - ASSERT_GT(aWires.Length(), 0); - EXPECT_EQ(myGraph.Topo().Wires().Definition(aWires.Value(0)).SubtreeGen, 0u); + BRepGraph_WiresOfEdge aWireIt = myGraph.Topo().Edges().WiresOf(BRepGraph_EdgeId::Start()); + ASSERT_TRUE(aWireIt.More()); + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); + EXPECT_EQ(myGraph.Topo().Wires().Definition(aWireId).SubtreeGen, 0u); myGraph.Editor().EndDeferredInvalidation(); // After flush: wire and face SubtreeGen should be propagated. - EXPECT_GT(myGraph.Topo().Wires().Definition(aWires.Value(0)).SubtreeGen, 0u); + EXPECT_GT(myGraph.Topo().Wires().Definition(aWireId).SubtreeGen, 0u); // Check propagation to face. for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { - if (BRepGraph_TestTools::FaceUsesWire(myGraph, aFaceIt.CurrentId(), aWires.Value(0))) + if (BRepGraph_TestTools::FaceUsesWire(myGraph, aFaceIt.CurrentId(), aWireId)) { EXPECT_GT(aFaceIt.Current().SubtreeGen, 0u); break; @@ -167,7 +166,7 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_ReconstructAfterFlush_Su TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_ParallelMutation_WithExternalSync) { - const int aNbEdges = myGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = myGraph.Topo().Edges().Nb(); ASSERT_GT(aNbEdges, 1); // Deferred mode is NOT internally thread-safe. Parallel callers must @@ -229,10 +228,9 @@ TEST_F(BRepGraph_DeferredInvalidationTest, EndWithoutBegin_IsIdempotent) TEST_F(BRepGraph_DeferredInvalidationTest, DeferredScope_NestedGuards_FlushOnlyOnOuterDestruction) { - const NCollection_DynamicArray& aWires = - myGraph.Topo().Edges().Wires(BRepGraph_EdgeId::Start()); - ASSERT_GT(aWires.Length(), 0); - const BRepGraph_WireId aWireId = aWires.Value(0); + BRepGraph_WiresOfEdge aWireIt = myGraph.Topo().Edges().WiresOf(BRepGraph_EdgeId::Start()); + ASSERT_TRUE(aWireIt.More()); + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); { BRepGraph_DeferredScope anOuterScope(myGraph); @@ -311,19 +309,22 @@ TEST_F(BRepGraph_DeferredInvalidationTest, DeferredMode_OccurrenceMutation_PropagatesSubtreeGenToProduct) { // Build an assembly: root product + child occurrence referencing it. - const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = myGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + const BRepGraph_ProductId aAssemblyId = myGraph.Editor().Products().Add(); + myGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); const BRepGraph_OccurrenceId anOccId = - myGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + myGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); ASSERT_TRUE(anOccId.IsValid()); - // Verify parent product starts clean. - EXPECT_EQ(myGraph.Topo().Products().Definition(aAssemblyId).SubtreeGen, 0u); + // LinkProducts is a structural mutation in immediate mode, so SubtreeGen + // may already be non-zero. Capture the baseline after linking. + const uint32_t aBaselineSubtreeGen = myGraph.Topo().Products().Definition(aAssemblyId).SubtreeGen; + const uint32_t aBaselineOwnGen = myGraph.Topo().Products().Definition(aAssemblyId).OwnGen; // Find the OccurrenceRefId for the occurrence. - const NCollection_DynamicArray& aOccRefs = + const NCollection_LinearVector& aOccRefs = myGraph.Refs().Occurrences().IdsOf(aAssemblyId); - ASSERT_EQ(aOccRefs.Length(), 1); + ASSERT_EQ(aOccRefs.Size(), 1); const BRepGraph_OccurrenceRefId anOccRefId = aOccRefs.Value(0); // Mutate occurrence ref placement in deferred mode. @@ -334,16 +335,79 @@ TEST_F(BRepGraph_DeferredInvalidationTest, myGraph.Editor().Occurrences().SetRefLocalLocation(anOccRefId, TopLoc_Location(aTrsf)); } - // During deferred mode: ref modified. - EXPECT_GT(myGraph.Refs().Occurrences().Entry(anOccRefId).OwnGen, 0u); + // During deferred mode: ref modified - parent product's OwnGen should be bumped. + const BRepGraph_ProductId aParentProductId = + myGraph.Refs().Occurrences().Entry(anOccRefId).ParentProductId; + EXPECT_EQ(myGraph.Topo().Products().Definition(aParentProductId).OwnGen, aBaselineOwnGen + 1); - const uint32_t aSubtreeGenBeforeFlush = - myGraph.Topo().Products().Definition(aAssemblyId).SubtreeGen; + const uint32_t aSubtreeGenBeforeFlush = aBaselineSubtreeGen; myGraph.Editor().EndDeferredInvalidation(); // After flush: parent assembly product should have SubtreeGen >= before flush. EXPECT_GE(myGraph.Topo().Products().Definition(aAssemblyId).SubtreeGen, aSubtreeGenBeforeFlush); - // Parent's OwnGen must remain 0 - its own data didn't change. - EXPECT_EQ(myGraph.Topo().Products().Definition(aAssemblyId).OwnGen, 0u); + // Parent's OwnGen is bumped by exactly 1 because ref mutations now propagate to the parent node. + EXPECT_EQ(myGraph.Topo().Products().Definition(aAssemblyId).OwnGen, aBaselineOwnGen + 1); +} + +TEST_F(BRepGraph_DeferredInvalidationTest, CoEdgeMutation_ImmediatePropagatesToWireAndFace) +{ + const BRepGraph_WireId aWireId = BRepGraph_WireId::Start(); + + // Find the first coedge of the wire and its owning face. + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + + const NCollection_LinearVector& aCoEdgeIds = + myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds; + ASSERT_GT(aCoEdgeIds.Size(), 0); + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIds.Value(0); + ASSERT_TRUE(aCoEdgeId.IsValid()); + + // Record SubtreeGen before mutation. + const uint32_t aWireGenBefore = myGraph.Topo().Wires().Definition(aWireId).SubtreeGen; + const uint32_t aFaceGenBefore = myGraph.Topo().Faces().Definition(aFaceId).SubtreeGen; + + // Mutate CoEdge orientation in immediate mode. + myGraph.Editor().CoEdges().SetOrientation(aCoEdgeId, TopAbs_REVERSED); + + // Verify parent Wire and Face SubtreeGen incremented. + EXPECT_GT(myGraph.Topo().Wires().Definition(aWireId).SubtreeGen, aWireGenBefore) + << "CoEdge mutation did not propagate SubtreeGen to parent Wire in immediate mode"; + EXPECT_GT(myGraph.Topo().Faces().Definition(aFaceId).SubtreeGen, aFaceGenBefore) + << "CoEdge mutation did not propagate SubtreeGen to parent Face in immediate mode"; +} + +TEST_F(BRepGraph_DeferredInvalidationTest, CoEdgeMutation_DeferredPropagatesToWireAndFaceOnFlush) +{ + const BRepGraph_WireId aWireId = BRepGraph_WireId::Start(); + + const NCollection_LinearVector& aCoEdgeIds = + myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds; + ASSERT_GT(aCoEdgeIds.Size(), 0); + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIds.Value(0); + ASSERT_TRUE(aCoEdgeId.IsValid()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + + myGraph.Editor().BeginDeferredInvalidation(); + + // Record SubtreeGen during deferred mode (before flush). + const uint32_t aWireGenBefore = myGraph.Topo().Wires().Definition(aWireId).SubtreeGen; + const uint32_t aFaceGenBefore = myGraph.Topo().Faces().Definition(aFaceId).SubtreeGen; + + myGraph.Editor().CoEdges().SetOrientation(aCoEdgeId, TopAbs_REVERSED); + + // During deferred mode: parent Wire and Face NOT yet propagated. + EXPECT_EQ(myGraph.Topo().Wires().Definition(aWireId).SubtreeGen, aWireGenBefore) + << "Wire SubtreeGen should not change during deferred mode"; + EXPECT_EQ(myGraph.Topo().Faces().Definition(aFaceId).SubtreeGen, aFaceGenBefore) + << "Face SubtreeGen should not change during deferred mode"; + + myGraph.Editor().EndDeferredInvalidation(); + + // After flush: parent Wire and Face SubtreeGen should be incremented. + EXPECT_GT(myGraph.Topo().Wires().Definition(aWireId).SubtreeGen, aWireGenBefore) + << "CoEdge mutation did not propagate SubtreeGen to parent Wire on deferred flush"; + EXPECT_GT(myGraph.Topo().Faces().Definition(aFaceId).SubtreeGen, aFaceGenBefore) + << "CoEdge mutation did not propagate SubtreeGen to parent Face on deferred flush"; } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx index a58bc102bd..37aa955ad4 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_DefsIterator_Test.cxx @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include @@ -38,9 +38,9 @@ namespace { template -static int countIterator(IteratorT theIterator) +static size_t countIterator(IteratorT theIterator) { - int aCount = 0; + size_t aCount = 0; for (; theIterator.More(); theIterator.Next()) { ++aCount; @@ -60,25 +60,6 @@ static TopoDS_Edge makeEdgeWithInternalVertex() return anEdge; } -static TopoDS_Face makeFaceWithDirectVertex() -{ - BRep_Builder aBuilder; - const occ::handle aPlane = new Geom_Plane(gp_Pln()); - TopoDS_Face aFace; - aBuilder.MakeFace(aFace, aPlane, Precision::Confusion()); - - BRepBuilderAPI_MakeEdge aMakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Wire aWire; - aBuilder.MakeWire(aWire); - aBuilder.Add(aWire, aMakeEdge.Edge()); - aBuilder.Add(aFace, aWire); - - TopoDS_Vertex aDirectVertex; - aBuilder.MakeVertex(aDirectVertex, gp_Pnt(5, 5, 0), Precision::Confusion()); - aBuilder.Add(aFace, aDirectVertex.Oriented(TopAbs_INTERNAL)); - return aFace; -} - static TopoDS_Face wrapEdgeInFace(const TopoDS_Edge& theEdge) { BRep_Builder aBuilder; @@ -100,8 +81,8 @@ protected: { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + myGraph.Shapes().Add(aBoxMaker.Shape()); } BRepGraph myGraph; @@ -121,7 +102,11 @@ TEST_F(BRepGraph_DefsIteratorTest, EdgeOfWire_YieldsEdgeDefinitions) { BRepGraph_DefsEdgeOfWire anIt(myGraph, BRepGraph_WireId::Start()); ASSERT_TRUE(anIt.More()); - EXPECT_TRUE(anIt.CurrentId().IsValid(myGraph.Topo().Edges().Nb())); + const BRepGraph_EdgeId anEdgeId = anIt.CurrentId(); + const BRepGraph_CoEdgeId aCoEdgeId = anIt.CurrentRefId(); + EXPECT_TRUE(anEdgeId.IsValid(myGraph.Topo().Edges().Nb())); + EXPECT_TRUE(aCoEdgeId.IsValid(myGraph.Topo().CoEdges().Nb())); + EXPECT_EQ(myGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId, anEdgeId); EXPECT_TRUE(anIt.Current().StartVertexRefId.IsValid()); EXPECT_TRUE(anIt.Current().EndVertexRefId.IsValid()); } @@ -131,31 +116,31 @@ TEST_F(BRepGraph_DefsIteratorTest, CoEdgeOfWire_YieldsCoEdgeDefinitions) BRepGraph_DefsCoEdgeOfWire anIt(myGraph, BRepGraph_WireId::Start()); ASSERT_TRUE(anIt.More()); EXPECT_TRUE(anIt.CurrentId().IsValid(myGraph.Topo().CoEdges().Nb())); - EXPECT_TRUE(anIt.Current().EdgeDefId.IsValid(myGraph.Topo().Edges().Nb())); + EXPECT_EQ(anIt.CurrentRefId(), anIt.CurrentId()); + EXPECT_TRUE(anIt.Current().ChildEdgeId.IsValid(myGraph.Topo().Edges().Nb())); } -TEST(BRepGraph_DefsIteratorTestStandalone, VertexOfEdge_IncludesInternalVertices) +TEST_F(BRepGraph_DefsIteratorTest, VertexOfEdge_ExposesBoundaryVertexRef) +{ + BRepGraph_DefsVertexOfEdge anIt(myGraph, BRepGraph_EdgeId::Start()); + ASSERT_TRUE(anIt.More()); + const BRepGraph_VertexId aVertexId = anIt.CurrentId(); + const BRepGraph_VertexRefId aRefId = anIt.CurrentRefId(); + EXPECT_TRUE(aVertexId.IsValid(myGraph.Topo().Vertices().Nb())); + EXPECT_TRUE(aRefId.IsValid(myGraph.Refs().Vertices().Nb())); + EXPECT_EQ(myGraph.Refs().Vertices().Entry(aRefId).ChildVertexId, aVertexId); +} + +TEST(BRepGraph_DefsIteratorTestStandalone, VertexOfEdge_EnumeratesBoundaryVerticesOnly) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, wrapEdgeInFace(makeEdgeWithInternalVertex())); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(wrapEdgeInFace(makeEdgeWithInternalVertex())); - BRepGraph_EdgeId aEdgeWithInternal; - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - if (anEdgeIt.Current().InternalVertexRefIds.Length() == 1) - { - aEdgeWithInternal = anEdgeIt.CurrentId(); - break; - } - } - - ASSERT_TRUE(aEdgeWithInternal.IsValid()); - - bool aFoundSplitVertex = false; - int aCount = 0; - for (BRepGraph_DefsVertexOfEdge anIt(aGraph, aEdgeWithInternal); anIt.More(); anIt.Next()) + bool aFoundSplitVertex = false; + size_t aCount = 0; + for (BRepGraph_DefsVertexOfEdge anIt(aGraph, BRepGraph_EdgeId::Start()); anIt.More(); anIt.Next()) { ++aCount; const gp_Pnt& aPoint = anIt.Current().Point; @@ -165,24 +150,8 @@ TEST(BRepGraph_DefsIteratorTestStandalone, VertexOfEdge_IncludesInternalVertices } } - EXPECT_EQ(aCount, 3); - EXPECT_TRUE(aFoundSplitVertex); -} - -TEST(BRepGraph_DefsIteratorTestStandalone, VertexOfFace_EnumeratesDirectVertices) -{ - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, makeFaceWithDirectVertex()); - - ASSERT_EQ(countIterator(BRepGraph_DefsWireOfFace(aGraph, BRepGraph_FaceId::Start())), 1); - ASSERT_EQ(countIterator(BRepGraph_DefsVertexOfFace(aGraph, BRepGraph_FaceId::Start())), 1); - - BRepGraph_DefsVertexOfFace anIt(aGraph, BRepGraph_FaceId::Start()); - ASSERT_TRUE(anIt.More()); - EXPECT_NEAR(anIt.Current().Point.X(), 5.0, Precision::Confusion()); - EXPECT_NEAR(anIt.Current().Point.Y(), 5.0, Precision::Confusion()); + EXPECT_EQ(aCount, 2); + EXPECT_FALSE(aFoundSplitVertex); } TEST_F(BRepGraph_DefsIteratorTest, ChildOfCompound_EnumeratesHeterogeneousChildren) @@ -191,10 +160,10 @@ TEST_F(BRepGraph_DefsIteratorTest, ChildOfCompound_EnumeratesHeterogeneousChildr myGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 0.01); ASSERT_TRUE(aLooseVertex.IsValid()); - NCollection_DynamicArray aChildren; + NCollection_LinearVector aChildren; aChildren.Append(BRepGraph_SolidId::Start()); aChildren.Append(aLooseVertex); - const BRepGraph_CompoundId aCompound = myGraph.Editor().Compounds().Add(aChildren); + const BRepGraph_CompoundId aCompound = myGraph.Editor().Compounds().Add(aChildren.ToArray1()); ASSERT_TRUE(aCompound.IsValid()); BRepGraph_DefsChildOfCompound anIt(myGraph, aCompound); @@ -205,6 +174,56 @@ TEST_F(BRepGraph_DefsIteratorTest, ChildOfCompound_EnumeratesHeterogeneousChildr EXPECT_EQ(anIt.CurrentId().NodeKind, BRepGraph_NodeId::Kind::Vertex); } +TEST_F(BRepGraph_DefsIteratorTest, ChildOfCompound_SkipsOutOfRangeChildNode) +{ + const BRepGraph_VertexId aLooseVertex = + myGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 0.01); + ASSERT_TRUE(aLooseVertex.IsValid()); + + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_SolidId::Start()); + aChildren.Append(aLooseVertex); + aChildren.Append(aLooseVertex); + const BRepGraph_CompoundId aCompound = myGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + const NCollection_LinearVector& aChildRefs = + myGraph.Topo().Compounds().Relations(aCompound).ChildRefIds; + ASSERT_EQ(aChildRefs.Size(), 3); + { + BRepGraph_MutGuard aBadRef = + myGraph.Editor().Gen().MutChildRef(aChildRefs.Value(1)); + aBadRef.Internal().ChildNodeId = + BRepGraph_NodeId(BRepGraph_NodeId::Kind::Vertex, myGraph.Topo().Vertices().Nb()); + } + + uint32_t aCount = 0; + for (BRepGraph_DefsChildOfCompound anIt(myGraph, aCompound); anIt.More(); anIt.Next()) + { + EXPECT_TRUE(myGraph.Topo().Gen().IsActive(anIt.CurrentId())); + ++aCount; + } + EXPECT_EQ(aCount, 2u); +} + +TEST_F(BRepGraph_DefsIteratorTest, ChildOfCompound_SkipsRemovedChildNode) +{ + const BRepGraph_VertexId aLooseVertex = + myGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 0.01); + ASSERT_TRUE(aLooseVertex.IsValid()); + + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_SolidId::Start()); + aChildren.Append(aLooseVertex); + const BRepGraph_CompoundId aCompound = myGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + myGraph.Editor().Gen().RemoveNode(aLooseVertex); + + ASSERT_TRUE(BRepGraph_DefsChildOfCompound(myGraph, aCompound).More()); + EXPECT_EQ(countIterator(BRepGraph_DefsChildOfCompound(myGraph, aCompound)), 1); +} + TEST_F(BRepGraph_DefsIteratorTest, SolidOfCompSolid_EnumeratesDirectSolids) { const BRepGraph_SolidId aSolidA = myGraph.Editor().Solids().Add(); @@ -212,10 +231,10 @@ TEST_F(BRepGraph_DefsIteratorTest, SolidOfCompSolid_EnumeratesDirectSolids) ASSERT_TRUE(aSolidA.IsValid()); ASSERT_TRUE(aSolidB.IsValid()); - NCollection_DynamicArray aSolids; + NCollection_LinearVector aSolids; aSolids.Append(aSolidA); aSolids.Append(aSolidB); - const BRepGraph_CompSolidId aCompSolid = myGraph.Editor().CompSolids().Add(aSolids); + const BRepGraph_CompSolidId aCompSolid = myGraph.Editor().CompSolids().Add(aSolids.ToArray1()); ASSERT_TRUE(aCompSolid.IsValid()); EXPECT_EQ(countIterator(BRepGraph_DefsSolidOfCompSolid(myGraph, aCompSolid)), 2); @@ -223,76 +242,26 @@ TEST_F(BRepGraph_DefsIteratorTest, SolidOfCompSolid_EnumeratesDirectSolids) TEST_F(BRepGraph_DefsIteratorTest, OccurrenceOfProduct_EnumeratesDirectOccurrences) { - const BRepGraph_ProductId aPart = - myGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); - const BRepGraph_ProductId anAssembly = myGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPart = myGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + myGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId anAssembly = myGraph.Editor().Products().Add(); + myGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(aPart.IsValid()); ASSERT_TRUE(anAssembly.IsValid()); - EXPECT_TRUE( - myGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location()).IsValid()); - EXPECT_TRUE( - myGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location()).IsValid()); + EXPECT_TRUE(myGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()).IsValid()); + EXPECT_TRUE(myGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()).IsValid()); EXPECT_EQ(countIterator(BRepGraph_DefsOccurrenceOfProduct(myGraph, anAssembly)), 2); } -TEST_F(BRepGraph_DefsIteratorTest, AuxChildrenOfShellAndSolid_EnumerateInjectedChildren) -{ - NCollection_DynamicArray aShellChildren; - aShellChildren.Append(BRepGraph_WireId::Start()); - aShellChildren.Append(BRepGraph_EdgeId::Start()); - const BRepGraph_CompoundId aShellSeed = myGraph.Editor().Compounds().Add(aShellChildren); - ASSERT_TRUE(aShellSeed.IsValid()); - - { - BRepGraph_MutGuard aShell = - myGraph.Editor().Shells().Mut(BRepGraph_ShellId::Start()); - for (const BRepGraph_ChildRefId& aRefId : - myGraph.Topo().Compounds().Definition(aShellSeed).ChildRefIds) - { - aShell.Internal().AuxChildRefIds.Append(aRefId); - } - } - - BRepGraph_DefsChildOfShell aShellIt(myGraph, BRepGraph_ShellId::Start()); - ASSERT_TRUE(aShellIt.More()); - EXPECT_EQ(aShellIt.CurrentId().NodeKind, BRepGraph_NodeId::Kind::Wire); - aShellIt.Next(); - ASSERT_TRUE(aShellIt.More()); - EXPECT_EQ(aShellIt.CurrentId().NodeKind, BRepGraph_NodeId::Kind::Edge); - - NCollection_DynamicArray aSolidChildren; - aSolidChildren.Append(BRepGraph_EdgeId(1)); - aSolidChildren.Append(BRepGraph_VertexId::Start()); - const BRepGraph_CompoundId aSolidSeed = myGraph.Editor().Compounds().Add(aSolidChildren); - ASSERT_TRUE(aSolidSeed.IsValid()); - - { - BRepGraph_MutGuard aSolid = - myGraph.Editor().Solids().Mut(BRepGraph_SolidId::Start()); - for (const BRepGraph_ChildRefId& aRefId : - myGraph.Topo().Compounds().Definition(aSolidSeed).ChildRefIds) - { - aSolid.Internal().AuxChildRefIds.Append(aRefId); - } - } - - BRepGraph_DefsChildOfSolid aSolidIt(myGraph, BRepGraph_SolidId::Start()); - ASSERT_TRUE(aSolidIt.More()); - EXPECT_EQ(aSolidIt.CurrentId().NodeKind, BRepGraph_NodeId::Kind::Edge); - aSolidIt.Next(); - ASSERT_TRUE(aSolidIt.More()); - EXPECT_EQ(aSolidIt.CurrentId().NodeKind, BRepGraph_NodeId::Kind::Vertex); -} - TEST_F(BRepGraph_DefsIteratorTest, RemovedWireRef_IsSkipped) { - const NCollection_DynamicArray& aWireRefs = + const NCollection_LinearVector& aWireRefs = myGraph.Refs().Wires().IdsOf(BRepGraph_FaceId::Start()); - ASSERT_EQ(aWireRefs.Length(), 1); + ASSERT_EQ(aWireRefs.Size(), 1); myGraph.Editor().Gen().RemoveRef(aWireRefs.Value(0)); EXPECT_EQ(countIterator(BRepGraph_DefsWireOfFace(myGraph, BRepGraph_FaceId::Start())), 0); -} \ No newline at end of file +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_EdgeCases_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_EdgeCases_Test.cxx index c902d4f10c..1b19d24c67 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_EdgeCases_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_EdgeCases_Test.cxx @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -28,25 +27,23 @@ // Null / Empty / Invalid input tests // ============================================================ -TEST(BRepGraph_EdgeCasesTest, Build_NullShape_IsDoneFalse) +TEST(BRepGraph_EdgeCasesTest, Build_NullShape_IsEmpty) { BRepGraph aGraph; TopoDS_Shape aNullShape; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aNullShape); - EXPECT_FALSE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aNullShape); + EXPECT_TRUE(aGraph.IsEmpty()); } -TEST(BRepGraph_EdgeCasesTest, Build_EmptyCompound_IsDoneZeroCounts) +TEST(BRepGraph_EdgeCasesTest, Build_EmptyCompound_IsEmptyZeroCounts) { BRepGraph aGraph; BRep_Builder aBuilder; TopoDS_Compound aCompound; aBuilder.MakeCompound(aCompound); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCompound); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aCompound); // Whether IsDone is true or false for an empty compound is implementation-defined; // the key invariant is that all definition counts are zero. @@ -65,9 +62,9 @@ TEST(BRepGraph_EdgeCasesTest, Shape_InvalidNodeId_ReturnsNull) BRepGraph aGraph; BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_NodeId anInvalidId; // default: Index = -1 const TopoDS_Shape aShape = aGraph.Shapes().Shape(anInvalidId); @@ -79,9 +76,9 @@ TEST(BRepGraph_EdgeCasesTest, ReconstructShape_InvalidNodeId_ReturnsNull) BRepGraph aGraph; BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_NodeId anInvalidId; const TopoDS_Shape aShape = aGraph.Shapes().Reconstruct(anInvalidId); @@ -93,9 +90,9 @@ TEST(BRepGraph_EdgeCasesTest, TopoEntity_InvalidNodeId_ReturnsNull) BRepGraph aGraph; BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_NodeId anInvalidId; const BRepGraphInc::BaseDef* aDef = aGraph.Topo().Gen().TopoEntity(anInvalidId); @@ -120,15 +117,13 @@ TEST(BRepGraph_EdgeCasesTest, Build_TwiceOnSameGraph_GenerationIncrements) const TopoDS_Shape aBox = aBoxMaker.Shape(); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const uint32_t aFirstGen = aGraph.UIDs().Generation(); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const uint32_t aSecondGen = aGraph.UIDs().Generation(); EXPECT_GT(aSecondGen, aFirstGen); @@ -143,9 +138,8 @@ TEST(BRepGraph_EdgeCasesTest, Build_TwiceOnSameGraph_OldUIDsInvalidated) // First build: collect total UID counter used. aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const uint32_t aFirstGen = aGraph.UIDs().Generation(); // Record total node count from first build. @@ -154,9 +148,8 @@ TEST(BRepGraph_EdgeCasesTest, Build_TwiceOnSameGraph_OldUIDsInvalidated) // Second build. aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const uint32_t aSecondGen = aGraph.UIDs().Generation(); EXPECT_NE(aFirstGen, aSecondGen); @@ -164,7 +157,6 @@ TEST(BRepGraph_EdgeCasesTest, Build_TwiceOnSameGraph_OldUIDsInvalidated) const BRepGraph_NodeId aSolidId(BRepGraph_NodeId::Kind::Solid, 0); const BRepGraph_UID aUID = aGraph.UIDs().Of(aSolidId); EXPECT_TRUE(aUID.IsValid()); - EXPECT_EQ(aUID.Generation(), aSecondGen); } TEST(BRepGraph_EdgeCasesTest, Build_TwiceOnSameGraph_CountsResetCorrectly) @@ -175,24 +167,22 @@ TEST(BRepGraph_EdgeCasesTest, Build_TwiceOnSameGraph_CountsResetCorrectly) const TopoDS_Shape aBox = aBoxMaker.Shape(); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - const int aSolids1 = aGraph.Topo().Solids().Nb(); - const int aShells1 = aGraph.Topo().Shells().Nb(); - const int aFaces1 = aGraph.Topo().Faces().Nb(); - const int aWires1 = aGraph.Topo().Wires().Nb(); - const int aEdges1 = aGraph.Topo().Edges().Nb(); - const int aVerts1 = aGraph.Topo().Vertices().Nb(); - const int aSurfs1 = aGraph.Topo().Faces().Nb(); - const int aCurves1 = aGraph.Topo().Edges().Nb(); + const uint32_t aSolids1 = aGraph.Topo().Solids().Nb(); + const uint32_t aShells1 = aGraph.Topo().Shells().Nb(); + const uint32_t aFaces1 = aGraph.Topo().Faces().Nb(); + const uint32_t aWires1 = aGraph.Topo().Wires().Nb(); + const uint32_t aEdges1 = aGraph.Topo().Edges().Nb(); + const uint32_t aVerts1 = aGraph.Topo().Vertices().Nb(); + const uint32_t aSurfs1 = aGraph.Topo().Faces().Nb(); + const uint32_t aCurves1 = aGraph.Topo().Edges().Nb(); // Rebuild with same shape. aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Solids().Nb(), aSolids1); EXPECT_EQ(aGraph.Topo().Shells().Nb(), aShells1); @@ -214,9 +204,9 @@ TEST(BRepGraph_EdgeCasesTest, UID_AlwaysEnabled_AfterBuild) BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_NodeId aSolidId(BRepGraph_NodeId::Kind::Solid, 0); const BRepGraph_UID aUID = aGraph.UIDs().Of(aSolidId); @@ -234,15 +224,15 @@ TEST(BRepGraph_EdgeCasesTest, ParallelBuild_Sphere_SameAsSequential) BRepGraph aSeqGraph; aSeqGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aSeqGraph, aSphere, BRepGraph_Builder::Options{{}, true, false, false}); - ASSERT_TRUE(aSeqGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = + aSeqGraph.Shapes().Add(aSphere, BRepGraph::ShapesView::Options{{}, true, false, false}); + ASSERT_FALSE(aSeqGraph.IsEmpty()); BRepGraph aParGraph; aParGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aParGraph, aSphere, BRepGraph_Builder::Options{{}, true, false, true}); - ASSERT_TRUE(aParGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aParGraph.Shapes().Add(aSphere, BRepGraph::ShapesView::Options{{}, true, false, true}); + ASSERT_FALSE(aParGraph.IsEmpty()); EXPECT_EQ(aParGraph.Topo().Solids().Nb(), aSeqGraph.Topo().Solids().Nb()); EXPECT_EQ(aParGraph.Topo().Shells().Nb(), aSeqGraph.Topo().Shells().Nb()); @@ -271,17 +261,15 @@ TEST(BRepGraph_EdgeCasesTest, ParallelBuild_Compound_SameAsSequential) BRepGraph aSeqGraph; aSeqGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aSeqGraph, - aCompound, - BRepGraph_Builder::Options{{}, true, false, false}); - ASSERT_TRUE(aSeqGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aSeqGraph.Shapes().Add(aCompound, BRepGraph::ShapesView::Options{{}, true, false, false}); + ASSERT_FALSE(aSeqGraph.IsEmpty()); BRepGraph aParGraph; aParGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aParGraph, aCompound, BRepGraph_Builder::Options{{}, true, false, true}); - ASSERT_TRUE(aParGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = + aParGraph.Shapes().Add(aCompound, BRepGraph::ShapesView::Options{{}, true, false, true}); + ASSERT_FALSE(aParGraph.IsEmpty()); EXPECT_EQ(aParGraph.Topo().Solids().Nb(), aSeqGraph.Topo().Solids().Nb()); EXPECT_EQ(aParGraph.Topo().Shells().Nb(), aSeqGraph.Topo().Shells().Nb()); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx index ad6f58503f..bfdae1f797 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_EventBus_Test.cxx @@ -12,24 +12,29 @@ // commercial license or contractual agreement. #include +#include #include #include #include #include #include #include -#include +#include #include #include #include +#include +#include + +#include #include // Test layer that records modification events. -class BRepGraph_ModTrackingLayer : public BRepGraph_Layer +class BRepGraph_LayerModTracking : public BRepGraph_Layer { public: - BRepGraph_ModTrackingLayer( + BRepGraph_LayerModTracking( const TCollection_AsciiString& theName, const int theSubscribedKinds, const Standard_GUID& theId = Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10004"), @@ -60,10 +65,9 @@ public: myRefImmediateEvents.Append(theRef); } - void OnRefsModified(const NCollection_DynamicArray& theRefs, - const int /*theModifiedRefKindsMask*/) noexcept override + void OnRefsModified(const NCollection_Array1& theRefs) noexcept override { - myRefBatchEvents = theRefs; + copyArray(theRefs, myRefBatchEvents); ++myRefBatchCallCount; } @@ -72,31 +76,28 @@ public: myImmediateEvents.Append(theNode); } - void OnNodesModified(const NCollection_DynamicArray& theNodes) noexcept override + void OnNodesModified(const NCollection_Array1& theNodes) noexcept override { - myBatchEvents = theNodes; + copyArray(theNodes, myBatchEvents); ++myBatchCallCount; } - void OnNodeRemoved(const BRepGraph_NodeId theNode, - const BRepGraph_NodeId theReplacement) noexcept override + void OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept override { myLastRemovedNode = theNode; - myLastReplacement = theReplacement; ++myRemoveCallCount; } - void OnCompact( - const NCollection_DataMap& theRemapMap) noexcept override + void OnNodeReplaced(const BRepGraph_NodeId theOldNode, + const BRepGraph_NodeId theNewNode) noexcept override { - myLastRemapMap.Clear(); - for (const auto& [aOldNode, aNewNode] : theRemapMap.Items()) - { - myLastRemapMap.Bind(aOldNode, aNewNode); - } - ++myCompactCallCount; + myLastRemovedNode = theOldNode; + myLastReplacement = theNewNode; + ++myReplaceCallCount; } + void CopyTo(const BRepGraph_CopyRemap&) const override {} + void InvalidateAll() noexcept override {} void Clear() noexcept override @@ -105,10 +106,9 @@ public: myBatchEvents.Clear(); myBatchCallCount = 0; myRemoveCallCount = 0; - myCompactCallCount = 0; + myReplaceCallCount = 0; myLastRemovedNode = BRepGraph_NodeId(); myLastReplacement = BRepGraph_NodeId(); - myLastRemapMap.Clear(); myRefRemovedEvents.Clear(); myRefImmediateEvents.Clear(); myRefBatchEvents.Clear(); @@ -153,34 +153,45 @@ public: return aCount; } - NCollection_DynamicArray myImmediateEvents; - NCollection_DynamicArray myBatchEvents; - int myBatchCallCount = 0; - int myRemoveCallCount = 0; - int myCompactCallCount = 0; - BRepGraph_NodeId myLastRemovedNode; - BRepGraph_NodeId myLastReplacement; - NCollection_DataMap myLastRemapMap; + NCollection_LinearVector myImmediateEvents; + NCollection_LinearVector myBatchEvents; + int myBatchCallCount = 0; + int myRemoveCallCount = 0; + int myReplaceCallCount = 0; + BRepGraph_NodeId myLastRemovedNode; + BRepGraph_NodeId myLastReplacement; - NCollection_DynamicArray myRefRemovedEvents; - NCollection_DynamicArray myRefImmediateEvents; - NCollection_DynamicArray myRefBatchEvents; + NCollection_LinearVector myRefRemovedEvents; + NCollection_LinearVector myRefImmediateEvents; + NCollection_LinearVector myRefBatchEvents; int myRefBatchCallCount = 0; int myRefRemoveCallCount = 0; - DEFINE_STANDARD_RTTIEXT(BRepGraph_ModTrackingLayer, BRepGraph_Layer) + DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerModTracking, BRepGraph_Layer) private: + template + static void copyArray(const NCollection_Array1& theSource, + NCollection_LinearVector& theTarget) + { + theTarget.Clear(); + theTarget.Reserve(theSource.Size()); + for (const IdT& anId : theSource) + { + theTarget.Append(anId); + } + } + TCollection_AsciiString myName; int mySubscribedKinds; int mySubscribedRefKinds; Standard_GUID myId; }; -IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_ModTrackingLayer, BRepGraph_Layer) +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerModTracking, BRepGraph_Layer) // Minimal layer with default SubscribedKinds() behavior from base class. -class BRepGraph_DefaultLayer : public BRepGraph_Layer +class BRepGraph_LayerDefault : public BRepGraph_Layer { public: const Standard_GUID& ID() const override @@ -195,20 +206,42 @@ public: return THE_NAME; } - void OnNodeRemoved(const BRepGraph_NodeId, const BRepGraph_NodeId) noexcept override {} + void OnNodeRemoved(const BRepGraph_NodeId) noexcept override {} - void OnCompact(const NCollection_DataMap&) noexcept override - { - } + void CopyTo(const BRepGraph_CopyRemap&) const override {} void InvalidateAll() noexcept override {} void Clear() noexcept override {} - DEFINE_STANDARD_RTTIEXT(BRepGraph_DefaultLayer, BRepGraph_Layer) + DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerDefault, BRepGraph_Layer) }; -IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_DefaultLayer, BRepGraph_Layer) +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerDefault, BRepGraph_Layer) + +class BRepGraph_LayerGraphProbe : public BRepGraph_LayerDefault +{ +public: + const Standard_GUID& ID() const override + { + static const Standard_GUID THE_ID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10008"); + return THE_ID; + } + + const TCollection_AsciiString& Name() const override + { + static const TCollection_AsciiString THE_NAME("GraphProbeLayer"); + return THE_NAME; + } + + [[nodiscard]] bool IsGraphAttached() const noexcept { return IsAttached(); } + + [[nodiscard]] int NbFacesFromGraph() const { return Graph().Topo().Faces().Nb(); } + + DEFINE_STANDARD_RTTIEXT(BRepGraph_LayerGraphProbe, BRepGraph_LayerDefault) +}; + +IMPLEMENT_STANDARD_RTTIEXT(BRepGraph_LayerGraphProbe, BRepGraph_LayerDefault) class BRepGraph_EventBusTest : public testing::Test { @@ -218,9 +251,8 @@ protected: BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBox); - ASSERT_TRUE(myGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = myGraph.Shapes().Add(aBox); + ASSERT_FALSE(myGraph.IsEmpty()); } BRepGraph myGraph; @@ -244,8 +276,8 @@ TEST_F(BRepGraph_EventBusTest, ImmediateMode_SingleEdge) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Face) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Shell) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Solid); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("Tracker", aAllKinds); + occ::handle aLayer = + new BRepGraph_LayerModTracking("Tracker", aAllKinds); myGraph.LayerRegistry().RegisterLayer(aLayer); { @@ -257,7 +289,7 @@ TEST_F(BRepGraph_EventBusTest, ImmediateMode_SingleEdge) // Edge(0) should have an immediate event. EXPECT_TRUE(aLayer->HasImmediateEventFor(BRepGraph_EdgeId::Start())); // At least one event total (edge + propagated parents). - EXPECT_GT(aLayer->myImmediateEvents.Length(), 0); + EXPECT_GT(aLayer->myImmediateEvents.Size(), 0); } TEST_F(BRepGraph_EventBusTest, ImmediateMode_UpwardPropagation) @@ -267,8 +299,8 @@ TEST_F(BRepGraph_EventBusTest, ImmediateMode_UpwardPropagation) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Face) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Shell) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Solid); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("Tracker", aAllKinds); + occ::handle aLayer = + new BRepGraph_LayerModTracking("Tracker", aAllKinds); myGraph.LayerRegistry().RegisterLayer(aLayer); { @@ -292,8 +324,8 @@ TEST_F(BRepGraph_EventBusTest, ImmediateMode_UpwardPropagation) TEST_F(BRepGraph_EventBusTest, ImmediateMode_KindFilter) { // Subscribe only to Face. - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("FaceOnly", + occ::handle aLayer = + new BRepGraph_LayerModTracking("FaceOnly", BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Face)); myGraph.LayerRegistry().RegisterLayer(aLayer); @@ -318,8 +350,8 @@ TEST_F(BRepGraph_EventBusTest, DeferredMode_BatchDispatch) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Face) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Shell) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Solid); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("Tracker", aAllKinds); + occ::handle aLayer = + new BRepGraph_LayerModTracking("Tracker", aAllKinds); myGraph.LayerRegistry().RegisterLayer(aLayer); myGraph.Editor().BeginDeferredInvalidation(); @@ -331,7 +363,7 @@ TEST_F(BRepGraph_EventBusTest, DeferredMode_BatchDispatch) // OnNodesModified called exactly once. EXPECT_EQ(aLayer->myBatchCallCount, 1); // Batch contains at least the 3 edges + propagated parents. - EXPECT_GE(aLayer->myBatchEvents.Length(), 3); + EXPECT_GE(aLayer->myBatchEvents.Size(), 3); EXPECT_TRUE(aLayer->HasBatchEventFor(BRepGraph_EdgeId::Start())); EXPECT_TRUE(aLayer->HasBatchEventFor(BRepGraph_EdgeId(1))); EXPECT_TRUE(aLayer->HasBatchEventFor(BRepGraph_EdgeId(2))); @@ -340,28 +372,52 @@ TEST_F(BRepGraph_EventBusTest, DeferredMode_BatchDispatch) TEST_F(BRepGraph_EventBusTest, DeferredMode_NoImmediateDispatch) { const int aEdgeBit = BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Edge); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("Tracker", aEdgeBit); + occ::handle aLayer = + new BRepGraph_LayerModTracking("Tracker", aEdgeBit); myGraph.LayerRegistry().RegisterLayer(aLayer); myGraph.Editor().BeginDeferredInvalidation(); myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.5); // During deferred mode: OnNodeModified must NOT be called. - EXPECT_EQ(aLayer->myImmediateEvents.Length(), 0); + EXPECT_EQ(aLayer->myImmediateEvents.Size(), 0); myGraph.Editor().EndDeferredInvalidation(); // Immediate events still empty - only batch was dispatched. - EXPECT_EQ(aLayer->myImmediateEvents.Length(), 0); + EXPECT_EQ(aLayer->myImmediateEvents.Size(), 0); EXPECT_EQ(aLayer->myBatchCallCount, 1); } +TEST_F(BRepGraph_EventBusTest, DeferredMode_RefOnlyMutation_DispatchesRefBatch) +{ + const int aVertexRefBit = BRepGraph_Layer::RefKindBit(BRepGraph_RefId::Kind::Vertex); + occ::handle aLayer = + new BRepGraph_LayerModTracking("RefTracker", + 0, + Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10008"), + aVertexRefBit); + myGraph.LayerRegistry().RegisterLayer(aLayer); + + const BRepGraph_VertexRefId aVertexRef = + myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).StartVertexRefId; + ASSERT_TRUE(aVertexRef.IsValid()); + + myGraph.Editor().BeginDeferredInvalidation(); + myGraph.Editor().Vertices().SetRefOrientation(aVertexRef, TopAbs_REVERSED); + myGraph.Editor().EndDeferredInvalidation(); + + EXPECT_EQ(aLayer->myBatchCallCount, 0); + EXPECT_EQ(aLayer->myRefBatchCallCount, 1); + ASSERT_EQ(aLayer->myRefBatchEvents.Size(), 1); + EXPECT_EQ(aLayer->myRefBatchEvents.Value(0), BRepGraph_RefId(aVertexRef)); +} + TEST_F(BRepGraph_EventBusTest, UnregisterLayer_FlagUpdate) { const int aEdgeBit = BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Edge); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("Tracker", aEdgeBit); + occ::handle aLayer = + new BRepGraph_LayerModTracking("Tracker", aEdgeBit); myGraph.LayerRegistry().RegisterLayer(aLayer); // Mutate - should dispatch. @@ -370,7 +426,7 @@ TEST_F(BRepGraph_EventBusTest, UnregisterLayer_FlagUpdate) myGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); myGraph.Editor().Edges().SetTolerance(aMut, 0.5); } - EXPECT_GT(aLayer->myImmediateEvents.Length(), 0); + EXPECT_GT(aLayer->myImmediateEvents.Size(), 0); // Unregister and clear. myGraph.LayerRegistry().UnregisterLayer(aLayer->ID()); @@ -382,77 +438,73 @@ TEST_F(BRepGraph_EventBusTest, UnregisterLayer_FlagUpdate) myGraph.Editor().Edges().Mut(BRepGraph_EdgeId(1)); myGraph.Editor().Edges().SetTolerance(aMut, 0.6); } - EXPECT_EQ(aLayer->myImmediateEvents.Length(), 0); + EXPECT_EQ(aLayer->myImmediateEvents.Size(), 0); +} + +TEST_F(BRepGraph_EventBusTest, DetachedLayerGraph_FailsClearly) +{ + occ::handle aLayer = new BRepGraph_LayerGraphProbe; + myGraph.LayerRegistry().RegisterLayer(aLayer); + + EXPECT_TRUE(aLayer->IsGraphAttached()); + EXPECT_EQ(aLayer->NbFacesFromGraph(), myGraph.Topo().Faces().Nb()); + + myGraph.LayerRegistry().UnregisterLayer(aLayer->ID()); + + EXPECT_FALSE(aLayer->IsGraphAttached()); + EXPECT_THROW(std::ignore = aLayer->NbFacesFromGraph(), Standard_ProgramError); } TEST_F(BRepGraph_EventBusTest, FindLayer_ByGuid_ReturnsRegisteredLayer) { const int aEdgeBit = BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Edge); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("Tracker", aEdgeBit); + occ::handle aLayer = + new BRepGraph_LayerModTracking("Tracker", aEdgeBit); myGraph.LayerRegistry().RegisterLayer(aLayer); EXPECT_EQ(myGraph.LayerRegistry().FindLayer(aLayer->ID()), aLayer); - EXPECT_GE(myGraph.LayerRegistry().FindSlot(aLayer->ID()), 0); + uint32_t aSlot = 0; + EXPECT_TRUE(myGraph.LayerRegistry().FindSlot(aLayer->ID(), aSlot)); } -TEST_F(BRepGraph_EventBusTest, OnNodeRemoved_DispatchesReplacement) +TEST_F(BRepGraph_EventBusTest, OnNodeReplaced_DispatchesReplacement) { - occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); + occ::handle aLayer = new BRepGraph_LayerModTracking("Tracker", 0); myGraph.LayerRegistry().RegisterLayer(aLayer); const BRepGraph_NodeId anOldEdge = BRepGraph_EdgeId::Start(); const BRepGraph_NodeId aNewEdge = BRepGraph_EdgeId(1); - myGraph.Editor().Gen().RemoveNode(anOldEdge, aNewEdge); + myGraph.Editor().Gen().ReplaceNode(anOldEdge, aNewEdge); - EXPECT_EQ(aLayer->myRemoveCallCount, 1); + EXPECT_EQ(aLayer->myRemoveCallCount, 0); + EXPECT_EQ(aLayer->myReplaceCallCount, 1); EXPECT_EQ(aLayer->myLastRemovedNode, anOldEdge); EXPECT_EQ(aLayer->myLastReplacement, aNewEdge); } -TEST_F(BRepGraph_EventBusTest, OnNodeRemoved_DispatchesInvalidReplacementForPureDeletion) +TEST_F(BRepGraph_EventBusTest, OnNodeRemoved_DispatchesPureDeletion) { - occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); + occ::handle aLayer = new BRepGraph_LayerModTracking("Tracker", 0); myGraph.LayerRegistry().RegisterLayer(aLayer); const BRepGraph_NodeId aFaceId = BRepGraph_FaceId::Start(); myGraph.Editor().Gen().RemoveNode(aFaceId); EXPECT_EQ(aLayer->myRemoveCallCount, 1); + EXPECT_EQ(aLayer->myReplaceCallCount, 0); EXPECT_EQ(aLayer->myLastRemovedNode, aFaceId); EXPECT_FALSE(aLayer->myLastReplacement.IsValid()); } -TEST_F(BRepGraph_EventBusTest, OnCompact_DispatchesRemapToRegisteredLayers) -{ - occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); - myGraph.LayerRegistry().RegisterLayer(aLayer); - - myGraph.Editor().Gen().RemoveNode(BRepGraph_FaceId::Start()); - const int aNbFacesBefore = myGraph.Topo().Faces().Nb(); - - const BRepGraph_Compact::Result aResult = BRepGraph_Compact::Perform(myGraph); - (void)aResult; - - EXPECT_EQ(aLayer->myCompactCallCount, 1); - EXPECT_EQ(myGraph.Topo().Faces().Nb(), aNbFacesBefore - 1); - - const BRepGraph_NodeId* aNewFaceId = aLayer->myLastRemapMap.Seek(BRepGraph_FaceId(1)); - ASSERT_NE(aNewFaceId, nullptr); - EXPECT_EQ(aNewFaceId->NodeKind, BRepGraph_NodeId::Kind::Face); - EXPECT_EQ(aNewFaceId->Index, 0); - EXPECT_EQ(aLayer->myLastRemapMap.Seek(BRepGraph_FaceId::Start()), nullptr); -} - TEST_F(BRepGraph_EventBusTest, MultipleSubscribers) { - occ::handle aEdgeLayer = - new BRepGraph_ModTrackingLayer("EdgeTracker", + occ::handle aEdgeLayer = + new BRepGraph_LayerModTracking("EdgeTracker", BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Edge), Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10005")); - occ::handle aFaceLayer = - new BRepGraph_ModTrackingLayer("FaceTracker", + occ::handle aFaceLayer = + new BRepGraph_LayerModTracking("FaceTracker", BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Face), Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10006")); myGraph.LayerRegistry().RegisterLayer(aEdgeLayer); @@ -475,7 +527,7 @@ TEST_F(BRepGraph_EventBusTest, MultipleSubscribers) TEST_F(BRepGraph_EventBusTest, DefaultSubscribedKinds_Zero) { - occ::handle aDefaultLayer = new BRepGraph_DefaultLayer; + occ::handle aDefaultLayer = new BRepGraph_LayerDefault; myGraph.LayerRegistry().RegisterLayer(aDefaultLayer); // SubscribedKinds() == 0 in BRepGraph_Layer base default implementation. @@ -494,8 +546,8 @@ TEST_F(BRepGraph_EventBusTest, DefaultSubscribedKinds_Zero) TEST_F(BRepGraph_EventBusTest, DeferredScope_DispatchesOnDestruction) { const int aEdgeBit = BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Edge); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("Tracker", aEdgeBit); + occ::handle aLayer = + new BRepGraph_LayerModTracking("Tracker", aEdgeBit); myGraph.LayerRegistry().RegisterLayer(aLayer); { @@ -514,8 +566,8 @@ TEST_F(BRepGraph_EventBusTest, DeferredScope_DispatchesOnDestruction) TEST_F(BRepGraph_EventBusTest, DeferredMode_NoModifications_NoDispatch) { const int aEdgeBit = BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Edge); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("Tracker", aEdgeBit); + occ::handle aLayer = + new BRepGraph_LayerModTracking("Tracker", aEdgeBit); myGraph.LayerRegistry().RegisterLayer(aLayer); myGraph.Editor().BeginDeferredInvalidation(); @@ -523,7 +575,7 @@ TEST_F(BRepGraph_EventBusTest, DeferredMode_NoModifications_NoDispatch) myGraph.Editor().EndDeferredInvalidation(); EXPECT_EQ(aLayer->myBatchCallCount, 0); - EXPECT_EQ(aLayer->myBatchEvents.Length(), 0); + EXPECT_EQ(aLayer->myBatchEvents.Size(), 0); } TEST_F(BRepGraph_EventBusTest, KindBit_Helpers) @@ -560,35 +612,34 @@ TEST_F(BRepGraph_EventBusTest, RefKindBit_Helpers) EXPECT_EQ(BRepGraph_Layer::RefKindBit(Kind::Shell), 1 << static_cast(Kind::Shell)); EXPECT_EQ(BRepGraph_Layer::RefKindBit(Kind::Face), 1 << static_cast(Kind::Face)); EXPECT_EQ(BRepGraph_Layer::RefKindBit(Kind::Wire), 1 << static_cast(Kind::Wire)); - EXPECT_EQ(BRepGraph_Layer::RefKindBit(Kind::CoEdge), 1 << static_cast(Kind::CoEdge)); EXPECT_EQ(BRepGraph_Layer::RefKindBit(Kind::Vertex), 1 << static_cast(Kind::Vertex)); EXPECT_EQ(BRepGraph_Layer::RefKindBit(Kind::Solid), 1 << static_cast(Kind::Solid)); EXPECT_EQ(BRepGraph_Layer::RefKindBit(Kind::Child), 1 << static_cast(Kind::Child)); EXPECT_EQ(BRepGraph_Layer::RefKindBit(Kind::Occurrence), 1 << static_cast(Kind::Occurrence)); - // 8 distinct bits. + // 7 distinct bits. const int aAll = BRepGraph_Layer::RefKindBit(Kind::Shell) | BRepGraph_Layer::RefKindBit(Kind::Face) - | BRepGraph_Layer::RefKindBit(Kind::Wire) | BRepGraph_Layer::RefKindBit(Kind::CoEdge) - | BRepGraph_Layer::RefKindBit(Kind::Vertex) | BRepGraph_Layer::RefKindBit(Kind::Solid) - | BRepGraph_Layer::RefKindBit(Kind::Child) | BRepGraph_Layer::RefKindBit(Kind::Occurrence); + | BRepGraph_Layer::RefKindBit(Kind::Wire) | BRepGraph_Layer::RefKindBit(Kind::Vertex) + | BRepGraph_Layer::RefKindBit(Kind::Solid) | BRepGraph_Layer::RefKindBit(Kind::Child) + | BRepGraph_Layer::RefKindBit(Kind::Occurrence); int aBitCount = 0; for (int v = aAll; v != 0; v >>= 1) { aBitCount += (v & 1); } - EXPECT_EQ(aBitCount, 8); + EXPECT_EQ(aBitCount, 7); } TEST_F(BRepGraph_EventBusTest, DefaultSubscribedRefKinds_Zero) { - occ::handle aDefaultLayer = new BRepGraph_DefaultLayer; + occ::handle aDefaultLayer = new BRepGraph_LayerDefault; EXPECT_EQ(aDefaultLayer->SubscribedRefKinds(), 0); } TEST_F(BRepGraph_EventBusTest, OnRefRemoved_DispatchedToAllLayers) { - occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); + occ::handle aLayer = new BRepGraph_LayerModTracking("Tracker", 0); myGraph.LayerRegistry().RegisterLayer(aLayer); // A box has FaceRef entries in its shell. Remove one. @@ -597,25 +648,25 @@ TEST_F(BRepGraph_EventBusTest, OnRefRemoved_DispatchedToAllLayers) // OnRefRemoved must be dispatched regardless of SubscribedRefKinds. EXPECT_EQ(aLayer->myRefRemoveCallCount, 1); - EXPECT_EQ(aLayer->myRefRemovedEvents.Length(), 1); + EXPECT_EQ(aLayer->myRefRemovedEvents.Size(), 1); EXPECT_EQ(aLayer->myRefRemovedEvents.Value(0), BRepGraph_RefId(aFaceRef)); } TEST_F(BRepGraph_EventBusTest, OnRefRemoved_MultipleRefs) { - occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); + occ::handle aLayer = new BRepGraph_LayerModTracking("Tracker", 0); myGraph.LayerRegistry().RegisterLayer(aLayer); ASSERT_TRUE(myGraph.Editor().Gen().RemoveRef(BRepGraph_FaceRefId::Start())); ASSERT_TRUE(myGraph.Editor().Gen().RemoveRef(BRepGraph_FaceRefId(1))); EXPECT_EQ(aLayer->myRefRemoveCallCount, 2); - EXPECT_EQ(aLayer->myRefRemovedEvents.Length(), 2); + EXPECT_EQ(aLayer->myRefRemovedEvents.Size(), 2); } TEST_F(BRepGraph_EventBusTest, OnRefRemoved_AlreadyRemoved_NotDispatched) { - occ::handle aLayer = new BRepGraph_ModTrackingLayer("Tracker", 0); + occ::handle aLayer = new BRepGraph_LayerModTracking("Tracker", 0); myGraph.LayerRegistry().RegisterLayer(aLayer); ASSERT_TRUE(myGraph.Editor().Gen().RemoveRef(BRepGraph_FaceRefId::Start())); @@ -631,8 +682,8 @@ TEST_F(BRepGraph_EventBusTest, OverlappingSubscription_EdgeAndFace) // Layer subscribes to both Edge and Face - should receive events for both kinds. const int aEdgeFace = BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Edge) | BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Face); - occ::handle aLayer = - new BRepGraph_ModTrackingLayer("EdgeFace", aEdgeFace); + occ::handle aLayer = + new BRepGraph_LayerModTracking("EdgeFace", aEdgeFace); myGraph.LayerRegistry().RegisterLayer(aLayer); { @@ -649,14 +700,15 @@ TEST_F(BRepGraph_EventBusTest, OverlappingSubscription_EdgeAndFace) TEST_F(BRepGraph_EventBusTest, LayerIterator_TraditionalLoop) { - occ::handle aLayer1 = - new BRepGraph_ModTrackingLayer("L1", 0, Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10030")); - occ::handle aLayer2 = - new BRepGraph_ModTrackingLayer("L2", 0, Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10031")); + const uint32_t aBaseLayerCount = myGraph.LayerRegistry().NbLayers(); + occ::handle aLayer1 = + new BRepGraph_LayerModTracking("L1", 0, Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10030")); + occ::handle aLayer2 = + new BRepGraph_LayerModTracking("L2", 0, Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10031")); myGraph.LayerRegistry().RegisterLayer(aLayer1); myGraph.LayerRegistry().RegisterLayer(aLayer2); - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_LayerIterator anIt(myGraph.LayerRegistry()); anIt.More(); anIt.Next()) { EXPECT_FALSE(anIt.Value().IsNull()); @@ -664,21 +716,22 @@ TEST_F(BRepGraph_EventBusTest, LayerIterator_TraditionalLoop) EXPECT_EQ(myGraph.LayerRegistry().Layer(anIt.Slot()), anIt.Value()); ++aCount; } - EXPECT_EQ(aCount, 2); + EXPECT_EQ(aCount, aBaseLayerCount + 2); } TEST_F(BRepGraph_EventBusTest, LayerIterator_RangeFor) { - occ::handle aLayer1 = - new BRepGraph_ModTrackingLayer("L1", 0, Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10030")); - occ::handle aLayer2 = - new BRepGraph_ModTrackingLayer("L2", 0, Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10031")); + const uint32_t aBaseLayerCount = myGraph.LayerRegistry().NbLayers(); + occ::handle aLayer1 = + new BRepGraph_LayerModTracking("L1", 0, Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10030")); + occ::handle aLayer2 = + new BRepGraph_LayerModTracking("L2", 0, Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10031")); myGraph.LayerRegistry().RegisterLayer(aLayer1); myGraph.LayerRegistry().RegisterLayer(aLayer2); - int aCount = 0; - bool hasL1 = false; - bool hasL2 = false; + uint32_t aCount = 0; + bool hasL1 = false; + bool hasL2 = false; for (const occ::handle& aLayer : BRepGraph_LayerIterator(myGraph.LayerRegistry())) { @@ -692,19 +745,20 @@ TEST_F(BRepGraph_EventBusTest, LayerIterator_RangeFor) } ++aCount; } - EXPECT_EQ(aCount, 2); + EXPECT_EQ(aCount, aBaseLayerCount + 2); EXPECT_TRUE(hasL1); EXPECT_TRUE(hasL2); } -TEST_F(BRepGraph_EventBusTest, LayerIterator_RangeFor_Empty) +TEST_F(BRepGraph_EventBusTest, LayerIterator_RangeFor_DefaultLayers) { - int aCount = 0; + EXPECT_GE(myGraph.LayerRegistry().NbLayers(), 1u); + uint32_t aCount = 0; for (const occ::handle& aLayer : BRepGraph_LayerIterator(myGraph.LayerRegistry())) { - (void)aLayer; + std::ignore = aLayer; ++aCount; } - EXPECT_EQ(aCount, 0); + EXPECT_EQ(aCount, myGraph.LayerRegistry().NbLayers()); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Fuzz_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Fuzz_Test.cxx index b38ee1877f..64c74e02f4 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Fuzz_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Fuzz_Test.cxx @@ -26,7 +26,7 @@ // Extend SEEDS with a new constant to add coverage. #include -#include +#include #include #include #include @@ -52,7 +52,7 @@ enum class MutationKind }; // Split and RemoveSomeEdge are exercised in isolation below; mixing them into -// the general fuzz stream uncovers real reverse-index inconsistencies that +// the general fuzz stream uncovers real relation-table inconsistencies that // belong to a separate follow-up (tracked as Phase 5.10/5.11). struct FuzzOutcome @@ -69,9 +69,9 @@ bool applyOne(BRepGraph& theGraph, std::mt19937& theRng) std::uniform_int_distribution aKindDist(0, static_cast(MutationKind::Count) - 1); const MutationKind aKind = static_cast(aKindDist(theRng)); - const int aNbEdges = theGraph.Topo().Edges().Nb(); - const int aNbVertices = theGraph.Topo().Vertices().Nb(); - const int aNbFaces = theGraph.Topo().Faces().Nb(); + const uint32_t aNbEdges = theGraph.Topo().Edges().Nb(); + const uint32_t aNbVertices = theGraph.Topo().Vertices().Nb(); + const uint32_t aNbFaces = theGraph.Topo().Faces().Nb(); auto pickActiveEdge = [&](BRepGraph_EdgeId& theOut) -> bool { if (aNbEdges <= 0) @@ -82,7 +82,7 @@ bool applyOne(BRepGraph& theGraph, std::mt19937& theRng) for (int aTry = 0; aTry < 8; ++aTry) { const BRepGraph_EdgeId anId(aDist(theRng)); - if (!theGraph.Topo().Edges().Definition(anId).IsRemoved) + if (!anId.IsRemoved(theGraph)) { theOut = anId; return true; @@ -100,7 +100,7 @@ bool applyOne(BRepGraph& theGraph, std::mt19937& theRng) for (int aTry = 0; aTry < 8; ++aTry) { const BRepGraph_VertexId anId(aDist(theRng)); - if (!theGraph.Topo().Vertices().Definition(anId).IsRemoved) + if (!anId.IsRemoved(theGraph)) { theOut = anId; return true; @@ -142,7 +142,7 @@ bool applyOne(BRepGraph& theGraph, std::mt19937& theRng) } std::uniform_int_distribution aDist(0, aNbFaces - 1); BRepGraph_FaceId aFaceId(aDist(theRng)); - if (theGraph.Topo().Faces().Definition(aFaceId).IsRemoved) + if (aFaceId.IsRemoved(theGraph)) { return false; } @@ -170,8 +170,7 @@ FuzzOutcome runFuzz(BRepGraph& theGraph, const uint32_t theSeed, const int theNb EXPECT_TRUE(aResult.IsValid()) << "Fuzz iteration " << aIt << " (seed=" << theSeed << ") left the graph invalid. " << "First issue: " - << (aResult.Issues.Length() > 0 ? aResult.Issues.First().Description.ToCString() - : "(none)"); + << (aResult.Issues.Size() > 0 ? aResult.Issues.First().Description.ToCString() : "(none)"); } else { @@ -193,9 +192,9 @@ TEST_P(BRepGraph_FuzzSeedTest, BoxSeed_RandomMutations_RemainValid) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()) << "Seed graph must be clean before fuzzing"; @@ -210,9 +209,9 @@ TEST_P(BRepGraph_FuzzSeedTest, CylinderSeed_RandomMutations_RemainValid) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeCylinder(5.0, 15.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(BRepPrimAPI_MakeCylinder(5.0, 15.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); const FuzzOutcome aOut = runFuzz(aGraph, aSeed, 40); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx index b2ca3d99a4..29e7f015af 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Geometry_Test.cxx @@ -14,33 +14,45 @@ #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 #include +#include +#include +#include +#include #include #include #include #include #include +#include + #include namespace @@ -86,6 +98,28 @@ static NCollection_DataMap faceCountsByComponent(const BRepGraph& theG return aCounts; } +static NCollection_LinearVector collectSameDomainFaces( + const BRepGraph& theGraph, + const BRepGraph_FaceId theFace) +{ + NCollection_LinearVector aFaces; + const occ::handle& aSurface = BRepGraph_Tool::Face::Surface(theGraph, theFace); + if (aSurface.IsNull()) + { + return aFaces; + } + + for (BRepGraph_FaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId anOtherFace = aFaceIt.CurrentId(); + if (anOtherFace != theFace && BRepGraph_Tool::Face::Surface(theGraph, anOtherFace) == aSurface) + { + aFaces.Append(anOtherFace); + } + } + return aFaces; +} + } // namespace // ============================================================ @@ -96,9 +130,9 @@ TEST(BRepGraph_GeometryTest, Sphere_AllFaces_SameSurface) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeSphere(15.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeSphere(15.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // All face defs of a sphere share the same surface handle. ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); @@ -122,9 +156,9 @@ TEST(BRepGraph_GeometryTest, Sphere_AllFacesShareSurface) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeSphere(15.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(BRepPrimAPI_MakeSphere(15.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // All faces of a sphere share the same surface pointer. ASSERT_TRUE(BRepGraph_Tool::Face::HasSurface(aGraph, BRepGraph_FaceId::Start())); @@ -148,9 +182,9 @@ TEST(BRepGraph_GeometryTest, Box_Curve3d_ValidForAll12Edges) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) @@ -165,9 +199,9 @@ TEST(BRepGraph_GeometryTest, Box_AllEdgesHaveCurve3d) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { @@ -177,50 +211,50 @@ TEST(BRepGraph_GeometryTest, Box_AllEdgesHaveCurve3d) } } -TEST(BRepGraph_GeometryTest, Box_FindPCurve_AllEdgeFacePairs_Valid) +TEST(BRepGraph_GeometryTest, Box_FindPCurveCoEdgeId_AllEdgeFacePairs_Valid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aPCurveCount = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeId); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) { const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeId); - const BRepGraphInc::CoEdgeDef* aPCurveEntry = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, BRepGraph_FaceId(aCE.FaceDefId)); - EXPECT_NE(aPCurveEntry, nullptr); + const BRepGraph_CoEdgeId aPCurveId = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, anEdgeId, BRepGraph_FaceId(aCE.FaceId)); + EXPECT_TRUE(aPCurveId.IsValid()); ++aPCurveCount; } } EXPECT_GT(aPCurveCount, 0); } -TEST(BRepGraph_GeometryTest, CoEdge_FaceDefIdValid) +TEST(BRepGraph_GeometryTest, CoEdge_FaceIdValid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); - for (int j = 0; j < aCoEdgeIdxs.Length(); ++j) + for (size_t j = 0; j < aCoEdgeIdxs.Size(); ++j) { const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(j)); - EXPECT_TRUE(aCE.FaceDefId.IsValid()) - << "Edge " << anEdgeIt.CurrentId().Index << " CoEdge " << j << " has invalid FaceDefId"; - EXPECT_EQ(BRepGraph_NodeId(aCE.FaceDefId).NodeKind, BRepGraph_NodeId::Kind::Face); + EXPECT_TRUE(aCE.FaceId.IsValid()) + << "Edge " << anEdgeIt.CurrentId().Index << " CoEdge " << j << " has invalid FaceId"; + EXPECT_EQ(BRepGraph_NodeId(aCE.FaceId).NodeKind, BRepGraph_NodeId::Kind::Face); } } } @@ -229,20 +263,21 @@ TEST(BRepGraph_GeometryTest, CoEdge_ParamRange_NonZero) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCoEdgeCount = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); - for (int j = 0; j < aCoEdgeIdxs.Length(); ++j) + for (size_t j = 0; j < aCoEdgeIdxs.Size(); ++j) { - const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(j)); - const double aRange = aCE.ParamLast - aCE.ParamFirst; - EXPECT_GT(std::abs(aRange), Precision::PConfusion()) + const std::pair aRange = + BRepGraph_Tool::CoEdge::Range(aGraph, aCoEdgeIdxs.Value(j)); + const double aRangeVal = aRange.second - aRange.first; + EXPECT_GT(std::abs(aRangeVal), Precision::PConfusion()) << "Edge " << anEdgeIt.CurrentId().Index << " CoEdge " << j << " has zero parameter range"; ++aCoEdgeCount; } @@ -250,37 +285,45 @@ TEST(BRepGraph_GeometryTest, CoEdge_ParamRange_NonZero) EXPECT_GT(aCoEdgeCount, 0); } -TEST(BRepGraph_GeometryTest, Edge_Continuity_Valid) +TEST(BRepGraph_GeometryTest, SetPCurveTwoArgPreservesExistingRange) { - const TopoDS_Shape aShape = BRepPrimAPI_MakeCylinder(10.0, 20.0).Shape(); - BRepGraph aGraph; - aGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerRegularity()); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - // Cylinder has at least the seam edge with C^k > C0 in BRepGraph_LayerRegularity. - bool hasNonC0Continuity = false; + BRepGraph_CoEdgeId aCoEdgeId; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - if (BRepGraph_Tool::Edge::MaxContinuity(aGraph, anEdgeIt.CurrentId()) > GeomAbs_C0) + const NCollection_LinearVector& aCoEdges = + aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); + if (!aCoEdges.IsEmpty()) { - hasNonC0Continuity = true; + aCoEdgeId = aCoEdges.First(); break; } } - EXPECT_TRUE(hasNonC0Continuity); + ASSERT_TRUE(aCoEdgeId.IsValid()); + const std::pair aRangeBefore = BRepGraph_Tool::CoEdge::Range(aGraph, aCoEdgeId); + + occ::handle aReplacement = + new Geom2d_Line(gp_Pnt2d(100.0, 200.0), gp_Dir2d(1.0, 0.0)); + aGraph.Editor().CoEdges().SetPCurve(aCoEdgeId, aReplacement); + + const std::pair aRangeAfter = BRepGraph_Tool::CoEdge::Range(aGraph, aCoEdgeId); + EXPECT_NEAR(aRangeAfter.first, aRangeBefore.first, Precision::PConfusion()); + EXPECT_NEAR(aRangeAfter.second, aRangeBefore.second, Precision::PConfusion()); + EXPECT_EQ(BRepGraph_Tool::CoEdge::PCurve(aGraph, aCoEdgeId).get(), aReplacement.get()); } TEST(BRepGraph_GeometryTest, FaceDef_Surface_IsNotNull) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -293,9 +336,9 @@ TEST(BRepGraph_GeometryTest, EdgeDef_Curve3d_IsNotNull) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { @@ -308,21 +351,48 @@ TEST(BRepGraph_GeometryTest, SameDomainFaces_SimpleBox_Empty) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // For a simple box each face has a unique surface, so SameDomainFaces is empty. for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraph_FaceId aFaceDefId = aFaceIt.CurrentId(); - const NCollection_DynamicArray aSameDomain = - aGraph.Topo().Faces().SameDomain(aFaceDefId, aGraph.Allocator()); - EXPECT_EQ(aSameDomain.Length(), 0) - << "Face def " << aFaceDefId.Index << " has unexpected same-domain faces"; + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + const NCollection_LinearVector aSameDomain = + collectSameDomainFaces(aGraph, aFaceId); + EXPECT_EQ(aSameDomain.Size(), 0) + << "Face def " << aFaceId.Index << " has unexpected same-domain faces"; } } +TEST(BRepGraph_GeometryTest, SameDomainFaces_SharedSurfaceAcrossOwnedUses) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const occ::handle aSurface = new Geom_Plane(gp_Pln()); + NCollection_LinearVector anInnerWires; + const BRepGraph_FaceId aFaceA = + aGraph.Editor().Faces().Add(aSurface, BRepGraph_WireId(), anInnerWires.ToArray1(), 1.e-7); + const BRepGraph_FaceId aFaceB = + aGraph.Editor().Faces().Add(aSurface, BRepGraph_WireId(), anInnerWires.ToArray1(), 1.e-7); + ASSERT_TRUE(aFaceA.IsValid()); + ASSERT_TRUE(aFaceB.IsValid()); + ASSERT_NE(aGraph.Topo().Faces().Definition(aFaceA).SurfaceRepId, + aGraph.Topo().Faces().Definition(aFaceB).SurfaceRepId); + + const NCollection_LinearVector aSameAsA = + collectSameDomainFaces(aGraph, aFaceA); + ASSERT_EQ(aSameAsA.Size(), 1u); + EXPECT_EQ(aSameAsA.Value(0), aFaceB); + + const NCollection_LinearVector aSameAsB = + collectSameDomainFaces(aGraph, aFaceB); + ASSERT_EQ(aSameAsB.Size(), 1u); + EXPECT_EQ(aSameAsB.Value(0), aFaceA); +} + TEST(BRepGraph_GeometryTest, CompoundWithMovedChild_SharedSolidDef) { const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape(); @@ -339,29 +409,28 @@ TEST(BRepGraph_GeometryTest, CompoundWithMovedChild_SharedSolidDef) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); - // Moved() preserves TShape - one solid definition, two compound ChildRefs. - EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1); + // Moved() preserves TShape, but locations are baked into topology definitions. + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 2); // Verify the graph was built successfully. - EXPECT_TRUE(aGraph.IsDone()); + EXPECT_FALSE(aGraph.IsEmpty()); } TEST(BRepGraph_GeometryTest, FaceDef_Triangulation_NullForAnalyticNoCrash) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Analytical box faces should have null triangulation (no mesh computed). // Simply verify access does not crash. for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { - const bool hasActiveTri = BRepGraph_Tool::Face::HasTriangulation(aGraph, aFaceIt.CurrentId()); + const bool hasActiveTri = aGraph.Mesh().Effective().Faces().Has(aFaceIt.CurrentId()); EXPECT_FALSE(hasActiveTri) << "Face " << aFaceIt.CurrentId().Index << " unexpectedly has a triangulation"; } @@ -375,14 +444,14 @@ TEST(BRepGraph_GeometryTest, SolidDef_CountMatchesNb) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_SolidIterator aSolidIt(aGraph); aSolidIt.More(); aSolidIt.Next()) { - (void)aSolidIt.Current(); + std::ignore = aSolidIt.Current(); ++aCount; } EXPECT_EQ(aCount, aGraph.Topo().Solids().Nb()); @@ -392,14 +461,14 @@ TEST(BRepGraph_GeometryTest, ShellDef_CountMatchesNb) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_ShellIterator aShellIt(aGraph); aShellIt.More(); aShellIt.Next()) { - (void)aShellIt.Current(); + std::ignore = aShellIt.Current(); ++aCount; } EXPECT_EQ(aCount, aGraph.Topo().Shells().Nb()); @@ -409,14 +478,14 @@ TEST(BRepGraph_GeometryTest, FaceDef_CountMatchesNb) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { - (void)aFaceIt.Current(); + std::ignore = aFaceIt.Current(); ++aCount; } EXPECT_EQ(aCount, aGraph.Topo().Faces().Nb()); @@ -426,14 +495,14 @@ TEST(BRepGraph_GeometryTest, WireDef_CountMatchesNb) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_WireIterator aWireIt(aGraph); aWireIt.More(); aWireIt.Next()) { - (void)aWireIt.Current(); + std::ignore = aWireIt.Current(); ++aCount; } EXPECT_EQ(aCount, aGraph.Topo().Wires().Nb()); @@ -443,14 +512,14 @@ TEST(BRepGraph_GeometryTest, EdgeDef_CountMatchesNb) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - (void)anEdgeIt.Current(); + std::ignore = anEdgeIt.Current(); ++aCount; } EXPECT_EQ(aCount, aGraph.Topo().Edges().Nb()); @@ -460,14 +529,14 @@ TEST(BRepGraph_GeometryTest, VertexDef_CountMatchesNb) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes19 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes19 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_VertexIterator aVertexIt(aGraph); aVertexIt.More(); aVertexIt.Next()) { - (void)aVertexIt.Current(); + std::ignore = aVertexIt.Current(); ++aCount; } EXPECT_EQ(aCount, aGraph.Topo().Vertices().Nb()); @@ -477,14 +546,14 @@ TEST(BRepGraph_GeometryTest, FaceDef_CountViaIterator_MatchesNb) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes20 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes20 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { - (void)aFaceIt.Current(); + std::ignore = aFaceIt.Current(); ++aCount; } EXPECT_EQ(aCount, aGraph.Topo().Faces().Nb()); @@ -494,9 +563,9 @@ TEST(BRepGraph_GeometryTest, FaceDef_AllSurfacesNonNull) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes21 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes21 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) @@ -511,9 +580,9 @@ TEST(BRepGraph_GeometryTest, EdgeDef_AllCurves3dNonNull) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes22 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes22 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) @@ -531,14 +600,14 @@ TEST(BRepGraph_GeometryTest, AllCoEdgesHaveCurve2d) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes23 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes23 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); int aCount = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) { @@ -553,17 +622,17 @@ TEST(BRepGraph_GeometryTest, CoEdgePCurveAdaptor_FallsBackOnPlaneWhenStoredPCurv { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes24 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes24 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_CoEdgeId aCoEdgeId; BRepGraph_EdgeId anEdgeId; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdges = + const NCollection_LinearVector& aCoEdges = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); - if (aCoEdges.Length() == 0) + if (aCoEdges.Size() == 0) { continue; } @@ -596,9 +665,9 @@ TEST(BRepGraph_GeometryTest, ConnectedComponents_SingleBox_OneComponent) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes25 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes25 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const NCollection_DataMap aFaceCounts = faceCountsByComponent(aGraph); ASSERT_EQ(aFaceCounts.Extent(), 1); @@ -624,9 +693,8 @@ TEST(BRepGraph_GeometryTest, ConnectedComponents_TwoBoxCompound_TwoComponents) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes26 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes26 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); const NCollection_DataMap aFaceCounts = faceCountsByComponent(aGraph); EXPECT_EQ(aFaceCounts.Extent(), 2); @@ -646,9 +714,8 @@ TEST(BRepGraph_GeometryTest, ConnectedComponents_TwoBoxCompound_FacesGroupedPerR BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes27 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes27 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); const NCollection_DataMap aFaceCounts = faceCountsByComponent(aGraph); ASSERT_EQ(aFaceCounts.Extent(), 2); @@ -672,9 +739,8 @@ TEST(BRepGraph_GeometryTest, ConnectedComponents_TwoBoxCompound_CoverAllFaces) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes28 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes28 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); const NCollection_DataMap aFaceCounts = faceCountsByComponent(aGraph); int aTotalFaces = 0; @@ -694,9 +760,9 @@ TEST(BRepGraph_GeometryTest, Box_EdgeDef_SameParameter_IsSet) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes29 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes29 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); // Box edges are well-formed; SameParameter should be true for all. @@ -711,9 +777,9 @@ TEST(BRepGraph_GeometryTest, Box_EdgeDef_SameRange_IsSet) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes30 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes30 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) @@ -723,6 +789,44 @@ TEST(BRepGraph_GeometryTest, Box_EdgeDef_SameRange_IsSet) } } +TEST(BRepGraph_GeometryTest, DerivedStateCache_LazyAndFreshAfterPCurveRangeMutation) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes31 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + EXPECT_TRUE(aGraph.CacheRegistry().Find().IsNull()); + + BRepGraph_EdgeId anEdgeId; + BRepGraph_CoEdgeId aCoEdgeId; + for (BRepGraph_CoEdgeIterator aCoEdgeIt(aGraph); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCandidateCoEdge = aCoEdgeIt.CurrentId(); + const BRepGraph_EdgeId aCandidateEdge = + aGraph.Topo().CoEdges().Definition(aCandidateCoEdge).ChildEdgeId; + if (aCandidateEdge.IsValid(aGraph.Topo().Edges().Nb()) && !aCandidateEdge.IsRemoved(aGraph) + && !BRepGraph_Tool::Edge::Curve(aGraph, aCandidateEdge).IsNull() + && !BRepGraph_Tool::CoEdge::PCurve(aGraph, aCandidateCoEdge).IsNull()) + { + anEdgeId = aCandidateEdge; + aCoEdgeId = aCandidateCoEdge; + break; + } + } + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + + ASSERT_TRUE(BRepGraph_Tool::Edge::SameRange(aGraph, anEdgeId)); + EXPECT_FALSE(aGraph.CacheRegistry().Find().IsNull()); + + const std::pair anEdgeRange = BRepGraph_Tool::Edge::Range(aGraph, anEdgeId); + aGraph.Editor().CoEdges().SetParamRange(aCoEdgeId, anEdgeRange.first + 1.0, anEdgeRange.second); + + EXPECT_FALSE(BRepGraph_Tool::Edge::SameRange(aGraph, anEdgeId)); + EXPECT_FALSE(BRepGraph_Tool::Edge::SameParameter(aGraph, anEdgeId)); +} + // ============================================================ // Seam edge PCurve validation tests // ============================================================ @@ -732,15 +836,15 @@ TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_HasTwoCoEdges) // A cylinder has a seam edge on its lateral face. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes31 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeCylinder(5.0, 20.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes32 = + aGraph.Shapes().Add(BRepPrimAPI_MakeCylinder(5.0, 20.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Find a seam edge via the connectivity-derived BRepGraph_Tool::CoEdge::SeamPair query. bool aFoundSeam = false; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More() && !aFoundSeam; anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) { @@ -751,7 +855,7 @@ TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_HasTwoCoEdges) const BRepGraphInc::CoEdgeDef& aPair = aGraph.Topo().CoEdges().Definition(aPairId); EXPECT_NE(aCE.Orientation, aPair.Orientation) << "Seam coedges should have opposite orientations"; - EXPECT_EQ(aCE.FaceDefId, aPair.FaceDefId) << "Seam coedges should share the same face"; + EXPECT_EQ(aCE.FaceId, aPair.FaceId) << "Seam coedges should share the same face"; aFoundSeam = true; break; } @@ -760,19 +864,20 @@ TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_HasTwoCoEdges) EXPECT_TRUE(aFoundSeam) << "No seam edge found in cylinder"; } -TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_FindPCurve_WithOrientation) +TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_FindPCurveCoEdgeId_WithOrientation) { - // Verify FindPCurve(edge, face, orientation) returns different entries for FORWARD vs REVERSED. + // Verify FindPCurveCoEdgeId(edge, face, orientation) returns different entries for FORWARD vs + // REVERSED. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes32 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeCylinder(5.0, 20.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes32 = + aGraph.Shapes().Add(BRepPrimAPI_MakeCylinder(5.0, 20.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeId); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) @@ -784,14 +889,15 @@ TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_FindPCurve_WithOrientation) } // This is a seam edge - test oriented overload. - const BRepGraph_FaceId aFaceId(aCE.FaceDefId.Index); - const BRepGraphInc::CoEdgeDef* aPC_Fwd = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, aFaceId, TopAbs_FORWARD); - const BRepGraphInc::CoEdgeDef* aPC_Rev = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, aFaceId, TopAbs_REVERSED); + const BRepGraph_FaceId aFaceId(aCE.FaceId.Index); + const BRepGraph_CoEdgeId aPC_Fwd = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, anEdgeId, aFaceId, TopAbs_FORWARD); + const BRepGraph_CoEdgeId aPC_Rev = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, anEdgeId, aFaceId, TopAbs_REVERSED); - EXPECT_NE(aPC_Fwd, nullptr) << "FindPCurve FORWARD returned null for seam edge"; - EXPECT_NE(aPC_Rev, nullptr) << "FindPCurve REVERSED returned null for seam edge"; + EXPECT_TRUE(aPC_Fwd.IsValid()) << "FindPCurveCoEdgeId FORWARD returned invalid for seam edge"; + EXPECT_TRUE(aPC_Rev.IsValid()) + << "FindPCurveCoEdgeId REVERSED returned invalid for seam edge"; EXPECT_NE(aPC_Fwd, aPC_Rev) << "FORWARD and REVERSED PCurves should be different entries"; return; // one seam is enough } @@ -799,48 +905,42 @@ TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_FindPCurve_WithOrientation) GTEST_SKIP() << "No seam edge found; test inconclusive"; } -TEST(BRepGraph_GeometryTest, Box_FindPCurve_MatchesToolOverload) +TEST(BRepGraph_GeometryTest, Box_FindPCurveCoEdgeId_MatchesCoEdge) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes33 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes33 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeId); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) { const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeId); - const BRepGraphInc::CoEdgeDef* aFromDefs = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, aCE.FaceDefId, aCE.Orientation); - const BRepGraphInc::CoEdgeDef* aFromTool = - BRepGraph_Tool::Edge::FindPCurve(aGraph, - anEdgeId, - BRepGraph_FaceId(aCE.FaceDefId), - aCE.Orientation); + const BRepGraph_CoEdgeId aFoundId = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, anEdgeId, aCE.FaceId, aCE.Orientation); - EXPECT_EQ(aFromDefs, aFromTool); - EXPECT_NE(aFromDefs, nullptr); + EXPECT_EQ(aFoundId, aCoEdgeId); } } } -TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_FindPCurve_DistinguishesOrientation) +TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_FindPCurveCoEdgeId_DistinguishesOrientation) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes34 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeCylinder(5.0, 20.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes34 = + aGraph.Shapes().Add(BRepPrimAPI_MakeCylinder(5.0, 20.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) @@ -852,16 +952,16 @@ TEST(BRepGraph_GeometryTest, Cylinder_SeamEdge_FindPCurve_DistinguishesOrientati } // Seam edge: same face, two orientations. - const BRepGraph_FaceId aFaceId = aCE.FaceDefId; + const BRepGraph_FaceId aFaceId = aCE.FaceId; const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const BRepGraphInc::CoEdgeDef* aPCFwd = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, aFaceId, TopAbs_FORWARD); - const BRepGraphInc::CoEdgeDef* aPCRev = - BRepGraph_Tool::Edge::FindPCurve(aGraph, anEdgeId, aFaceId, TopAbs_REVERSED); + const BRepGraph_CoEdgeId aPCFwd = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, anEdgeId, aFaceId, TopAbs_FORWARD); + const BRepGraph_CoEdgeId aPCRev = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, anEdgeId, aFaceId, TopAbs_REVERSED); - EXPECT_NE(aPCFwd, nullptr); - EXPECT_NE(aPCRev, nullptr); + EXPECT_TRUE(aPCFwd.IsValid()); + EXPECT_TRUE(aPCRev.IsValid()); EXPECT_NE(aPCFwd, aPCRev); return; } @@ -877,13 +977,13 @@ TEST(BRepGraph_GeometryTest, Box_RepCounts_MatchTopology) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes35 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes35 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_GT(aGraph.Topo().Geometry().NbSurfaces(), 0); - EXPECT_GT(aGraph.Topo().Geometry().NbCurves3D(), 0); - EXPECT_GT(aGraph.Topo().Geometry().NbCurves2D(), 0); + EXPECT_GT(aGraph.Topo().Geometry().NbFaceSurfaces(), 0); + EXPECT_GT(aGraph.Topo().Geometry().NbEdgeCurves3D(), 0); + EXPECT_GT(aGraph.Topo().Geometry().NbCoEdgeCurves2D(), 0); // Every face has a valid surface. for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) @@ -920,9 +1020,9 @@ TEST(BRepGraph_GeometryTest, Sphere_SurfaceDedup_SharedHandle) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes36 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeSphere(15.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes36 = + aGraph.Shapes().Add(BRepPrimAPI_MakeSphere(15.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // All faces of a sphere share the same TShape -> same entity -> same surface. if (aGraph.Topo().Faces().Nb() > 1) @@ -941,9 +1041,9 @@ TEST(BRepGraph_GeometryTest, Cylinder_TriangulationReps_Populated) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes37 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeCylinder(5.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes37 = + aGraph.Shapes().Add(BRepPrimAPI_MakeCylinder(5.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -952,9 +1052,9 @@ TEST(BRepGraph_GeometryTest, Cylinder_TriangulationReps_Populated) { EXPECT_TRUE(aFace.TriangulationRepId.IsValid()); // Verify active triangulation is non-null via BRepGraph_Tool. - if (BRepGraph_Tool::Face::HasTriangulation(aGraph, aFaceIt.CurrentId())) + if (aGraph.Mesh().Effective().Faces().Has(aFaceIt.CurrentId())) { - EXPECT_FALSE(BRepGraph_Tool::Face::Triangulation(aGraph, aFaceIt.CurrentId()).IsNull()); + EXPECT_FALSE(aGraph.Mesh().Effective().Faces().Triangulation(aFaceIt.CurrentId()).IsNull()); } } } @@ -962,41 +1062,37 @@ TEST(BRepGraph_GeometryTest, Cylinder_TriangulationReps_Populated) TEST(BRepGraph_GeometryTest, RepId_FactoryMethods) { - const BRepGraph_SurfaceRepId aSurfId(42); - EXPECT_EQ(BRepGraph_RepId(aSurfId).RepKind, BRepGraph_RepId::Kind::Surface); + const BRepGraph_FaceSurfaceRepId aSurfId(42); + EXPECT_EQ(BRepGraph_RepId(aSurfId).RepKind, BRepGraph_RepId::Kind::FaceSurface); EXPECT_EQ(aSurfId.Index, 42u); EXPECT_TRUE(aSurfId.IsValid()); - const BRepGraph_Curve3DRepId aCurve3DId(7); - EXPECT_EQ(BRepGraph_RepId(aCurve3DId).RepKind, BRepGraph_RepId::Kind::Curve3D); + const BRepGraph_EdgeCurve3DRepId aCurve3DId(7); + EXPECT_EQ(BRepGraph_RepId(aCurve3DId).RepKind, BRepGraph_RepId::Kind::EdgeCurve3D); EXPECT_EQ(aCurve3DId.Index, 7u); const BRepGraph_RepId aDefaultId; EXPECT_FALSE(aDefaultId.IsValid()); - EXPECT_EQ(aSurfId, BRepGraph_SurfaceRepId(42)); + EXPECT_EQ(aSurfId, BRepGraph_FaceSurfaceRepId(42)); EXPECT_NE(BRepGraph_RepId(aSurfId), BRepGraph_RepId(aCurve3DId)); } TEST(BRepGraph_GeometryTest, RepId_UntypedArithmetic_PreservesKindAndIndex) { - BRepGraph_RepId aRepId(BRepGraph_RepId::Kind::Curve3D, 5); + const BRepGraph_RepId aBase(BRepGraph_RepId::Kind::EdgeCurve3D, 5); + EXPECT_EQ(aBase.RepKind, BRepGraph_RepId::Kind::EdgeCurve3D); + EXPECT_EQ(aBase.Index, 5u); - const BRepGraph_RepId aPrev = aRepId++; - EXPECT_EQ(aPrev.RepKind, BRepGraph_RepId::Kind::Curve3D); - EXPECT_EQ(aPrev.Index, 5); - EXPECT_EQ(aRepId.Index, 6); + const BRepGraph_RepId aSame(BRepGraph_RepId::Kind::EdgeCurve3D, 5); + EXPECT_EQ(aBase, aSame); - ++aRepId; - EXPECT_EQ(aRepId.Index, 7); + const BRepGraph_RepId aDifferent(BRepGraph_RepId::Kind::EdgeCurve3D, 9); + EXPECT_NE(aBase, aDifferent); + EXPECT_LT(aBase, aDifferent); - const BRepGraph_RepId anAdvanced = aRepId + 2; - EXPECT_EQ(anAdvanced.RepKind, BRepGraph_RepId::Kind::Curve3D); - EXPECT_EQ(anAdvanced.Index, 9); - - const BRepGraph_RepId aRetreated = anAdvanced - 4; - EXPECT_EQ(aRetreated.RepKind, BRepGraph_RepId::Kind::Curve3D); - EXPECT_EQ(aRetreated.Index, 5); + const BRepGraph_RepId aOtherKind(BRepGraph_RepId::Kind::FaceSurface, 5); + EXPECT_NE(aBase, aOtherKind); } TEST(BRepGraph_GeometryTest, Compound_TwoBoxes_SurfaceDedup) @@ -1012,13 +1108,12 @@ TEST(BRepGraph_GeometryTest, Compound_TwoBoxes_SurfaceDedup) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes38 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes38 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 12); - EXPECT_GT(aGraph.Topo().Geometry().NbSurfaces(), 0); - EXPECT_LE(aGraph.Topo().Geometry().NbSurfaces(), 12); + EXPECT_GT(aGraph.Topo().Geometry().NbFaceSurfaces(), 0); + EXPECT_LE(aGraph.Topo().Geometry().NbFaceSurfaces(), 12); for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -1038,9 +1133,9 @@ TEST(BRepGraph_GeometryTest, Box_Polygon2DRep_MatchesInline) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes39 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes39 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Every coedge with a Polygon2DRepId has a valid polygon rep. for (BRepGraph_CoEdgeIterator aCoEdgeIt(aGraph); aCoEdgeIt.More(); aCoEdgeIt.Next()) @@ -1049,7 +1144,7 @@ TEST(BRepGraph_GeometryTest, Box_Polygon2DRep_MatchesInline) if (aCoEdge.Polygon2DRepId.IsValid()) { const occ::handle& aPoly = - BRepGraph_Tool::CoEdge::PolygonOnSurface(aGraph, aCoEdgeIt.CurrentId()); + aGraph.Mesh().Persistent().CoEdges().PolygonOnSurface(aCoEdgeIt.CurrentId()); EXPECT_FALSE(aPoly.IsNull()) << "CoEdge " << aCoEdgeIt.CurrentId().Index << " has Polygon2DRepId but null polygon"; } @@ -1060,3 +1155,148 @@ TEST(BRepGraph_GeometryTest, Box_Polygon2DRep_MatchesInline) } } } + +TEST(BRepGraph_GeometryTest, ClearUseOwnersMarksRecordsRemovedAndSetReusesSlots) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes40 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + const BRepGraph_FaceSurfaceRepId aSurfaceRepId = + aGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId; + ASSERT_TRUE(aSurfaceRepId.IsValid()); + const occ::handle aSurface = BRepGraph_Tool::Face::Surface(aGraph, aFaceId); + ASSERT_FALSE(aSurface.IsNull()); + + BRepGraph_EdgeId anEdgeId; + for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) + { + if (anEdgeIt.Current().Curve3DRepId.IsValid()) + { + anEdgeId = anEdgeIt.CurrentId(); + break; + } + } + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + const BRepGraph_EdgeCurve3DRepId aCurveRepId = + aGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId; + const occ::handle aCurve3d = BRepGraph_Tool::Edge::Curve(aGraph, anEdgeId); + ASSERT_FALSE(aCurve3d.IsNull()); + + BRepGraph_CoEdgeId aCoEdgeId; + for (BRepGraph_CoEdgeIterator aCoEdgeIt(aGraph); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + if (aCoEdgeIt.Current().Curve2DRepId.IsValid()) + { + aCoEdgeId = aCoEdgeIt.CurrentId(); + break; + } + } + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + const BRepGraph_CoEdgeCurve2DRepId aPCurveRepId = + aGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId; + const occ::handle aPCurve = BRepGraph_Tool::CoEdge::PCurve(aGraph, aCoEdgeId); + ASSERT_FALSE(aPCurve.IsNull()); + + const uint32_t aNbActiveSurfaces = aGraph.Topo().Geometry().NbActiveFaceSurfaces(); + ASSERT_GT(aNbActiveSurfaces, 0u); + aGraph.Editor().Faces().ClearSurface(aFaceId); + EXPECT_EQ(aGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId, aSurfaceRepId); + EXPECT_FALSE(BRepGraph_Tool::Face::HasSurface(aGraph, aFaceId)); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveFaceSurfaces(), aNbActiveSurfaces - 1u); + aGraph.Editor().Faces().SetSurface(aFaceId, aSurface); + EXPECT_EQ(aGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId, aSurfaceRepId); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveFaceSurfaces(), aNbActiveSurfaces); + aGraph.Editor().Faces().ClearSurface(aFaceId); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveFaceSurfaces(), aNbActiveSurfaces - 1u); + + const uint32_t aNbActiveCurves3D = aGraph.Topo().Geometry().NbActiveEdgeCurves3D(); + ASSERT_GT(aNbActiveCurves3D, 0u); + aGraph.Editor().Edges().ClearCurve(anEdgeId); + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId, aCurveRepId); + EXPECT_FALSE(BRepGraph_Tool::Edge::HasCurve(aGraph, anEdgeId)); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveEdgeCurves3D(), aNbActiveCurves3D - 1u); + aGraph.Editor().Edges().SetCurve(anEdgeId, aCurve3d, 0.0, 1.0); + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId, aCurveRepId); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveEdgeCurves3D(), aNbActiveCurves3D); + aGraph.Editor().Edges().ClearCurve(anEdgeId); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveEdgeCurves3D(), aNbActiveCurves3D - 1u); + + const uint32_t aNbActiveCurves2D = aGraph.Topo().Geometry().NbActiveCoEdgeCurves2D(); + ASSERT_GT(aNbActiveCurves2D, 0u); + aGraph.Editor().CoEdges().ClearPCurve(aCoEdgeId); + EXPECT_EQ(aGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId, aPCurveRepId); + EXPECT_FALSE(BRepGraph_Tool::CoEdge::HasPCurve(aGraph, aCoEdgeId)); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveCoEdgeCurves2D(), aNbActiveCurves2D - 1u); + aGraph.Editor().CoEdges().SetPCurve(aCoEdgeId, aPCurve, 0.0, 1.0); + EXPECT_EQ(aGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId, aPCurveRepId); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveCoEdgeCurves2D(), aNbActiveCurves2D); + aGraph.Editor().CoEdges().ClearPCurve(aCoEdgeId); + EXPECT_EQ(aGraph.Topo().Geometry().NbActiveCoEdgeCurves2D(), aNbActiveCurves2D - 1u); + + const occ::handle aTriangulation = new Poly_Triangulation(3, 1, false); + const occ::handle aPolygon3D = new Poly_Polygon3D(2, false); + const occ::handle aPolygon2D = new Poly_Polygon2D(2); + const occ::handle aPolygonOnTri = + new Poly_PolygonOnTriangulation(2, false); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTriangulation); + aGraph.Editor().Edges().SetPersistentPolygon3D(anEdgeId, aPolygon3D); + aGraph.Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, aPolygon2D); + aGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, aPolygonOnTri); + const BRepGraph_FaceTriangulationRepId aTriangulationRepId = + aGraph.Topo().Faces().Definition(aFaceId).TriangulationRepId; + const BRepGraph_EdgePolygon3DRepId aPolygon3DRepId = + aGraph.Topo().Edges().Definition(anEdgeId).Polygon3DRepId; + const BRepGraph_CoEdgePolygon2DRepId aPolygon2DRepId = + aGraph.Topo().CoEdges().Definition(aCoEdgeId).Polygon2DRepId; + const BRepGraph_CoEdgePolygonOnTriRepId aPolygonOnTriRepId = + aGraph.Topo().CoEdges().Definition(aCoEdgeId).PolygonOnTriRepId; + + const uint32_t aNbActiveTriangulations = aGraph.Mesh().Poly().NbActiveTriangulations(); + ASSERT_GT(aNbActiveTriangulations, 0u); + aGraph.Editor().Faces().ClearPersistentTriangulation(aFaceId); + EXPECT_EQ(aGraph.Topo().Faces().Definition(aFaceId).TriangulationRepId, aTriangulationRepId); + EXPECT_FALSE(aGraph.Mesh().Persistent().Faces().Has(aFaceId)); + EXPECT_EQ(aGraph.Mesh().Poly().NbActiveTriangulations(), aNbActiveTriangulations - 1u); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTriangulation); + EXPECT_EQ(aGraph.Topo().Faces().Definition(aFaceId).TriangulationRepId, aTriangulationRepId); + EXPECT_EQ(aGraph.Mesh().Poly().NbActiveTriangulations(), aNbActiveTriangulations); + aGraph.Editor().Faces().ClearPersistentTriangulation(aFaceId); + + const uint32_t aNbActivePolygons3D = aGraph.Mesh().Poly().NbActivePolygons3D(); + ASSERT_GT(aNbActivePolygons3D, 0u); + aGraph.Editor().Edges().ClearPersistentPolygon3D(anEdgeId); + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdgeId).Polygon3DRepId, aPolygon3DRepId); + EXPECT_FALSE(aGraph.Mesh().Persistent().Edges().Has(anEdgeId)); + EXPECT_EQ(aGraph.Mesh().Poly().NbActivePolygons3D(), aNbActivePolygons3D - 1u); + aGraph.Editor().Edges().SetPersistentPolygon3D(anEdgeId, aPolygon3D); + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdgeId).Polygon3DRepId, aPolygon3DRepId); + EXPECT_EQ(aGraph.Mesh().Poly().NbActivePolygons3D(), aNbActivePolygons3D); + aGraph.Editor().Edges().ClearPersistentPolygon3D(anEdgeId); + + const uint32_t aNbActivePolygons2D = aGraph.Mesh().Poly().NbActivePolygons2D(); + ASSERT_GT(aNbActivePolygons2D, 0u); + aGraph.Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, occ::handle()); + EXPECT_EQ(aGraph.Topo().CoEdges().Definition(aCoEdgeId).Polygon2DRepId, aPolygon2DRepId); + EXPECT_FALSE(aGraph.Mesh().Persistent().CoEdges().Has(aCoEdgeId)); + EXPECT_EQ(aGraph.Mesh().Poly().NbActivePolygons2D(), aNbActivePolygons2D - 1u); + aGraph.Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, aPolygon2D); + EXPECT_EQ(aGraph.Topo().CoEdges().Definition(aCoEdgeId).Polygon2DRepId, aPolygon2DRepId); + EXPECT_EQ(aGraph.Mesh().Poly().NbActivePolygons2D(), aNbActivePolygons2D); + aGraph.Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, occ::handle()); + + const uint32_t aNbActivePolygonsOnTri = aGraph.Mesh().Poly().NbActivePolygonsOnTri(); + ASSERT_GT(aNbActivePolygonsOnTri, 0u); + aGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, + occ::handle()); + EXPECT_EQ(aGraph.Topo().CoEdges().Definition(aCoEdgeId).PolygonOnTriRepId, aPolygonOnTriRepId); + EXPECT_FALSE(aGraph.Mesh().Persistent().CoEdges().HasPolygonOnTriangulation(aCoEdgeId)); + EXPECT_EQ(aGraph.Mesh().Poly().NbActivePolygonsOnTri(), aNbActivePolygonsOnTri - 1u); + aGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, aPolygonOnTri); + EXPECT_EQ(aGraph.Topo().CoEdges().Definition(aCoEdgeId).PolygonOnTriRepId, aPolygonOnTriRepId); + EXPECT_EQ(aGraph.Mesh().Poly().NbActivePolygonsOnTri(), aNbActivePolygonsOnTri); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_History_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_History_Test.cxx deleted file mode 100644 index a5f8f07858..0000000000 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_History_Test.cxx +++ /dev/null @@ -1,560 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include -#include "BRepGraph_RefTestTools.hxx" -#include -#include -#include -#include -#include -#include - -#include - -class BRepGraph_HistoryTest : public testing::Test -{ -protected: - void SetUp() override - { - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); - } - - BRepGraph myGraph; -}; - -// ============================================================ -// History chain tests -// ============================================================ - -TEST_F(BRepGraph_HistoryTest, FindOriginal_ChainABC_ReturnsA) -{ - // Build a chain: edge0 -> edge1 -> edge2 via two ApplyModification calls. - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - BRepGraph_NodeId anEdge1; - BRepGraph_NodeId anEdge2; - - myGraph.Editor().Gen().ApplyModification( - anEdge0, - [&](BRepGraph& theGraph, - BRepGraph_NodeId /*theTarget*/) -> NCollection_DynamicArray { - // Simulate producing a new edge node at index NbEdgeDefs. - anEdge1 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); - NCollection_DynamicArray aResult; - aResult.Append(anEdge1); - return aResult; - }, - "Step1"); - - myGraph.Editor().Gen().ApplyModification( - anEdge1, - [&](BRepGraph& theGraph, - BRepGraph_NodeId /*theTarget*/) -> NCollection_DynamicArray { - anEdge2 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); - NCollection_DynamicArray aResult; - aResult.Append(anEdge2); - return aResult; - }, - "Step2"); - - const BRepGraph_NodeId anOriginal = myGraph.History().FindOriginal(anEdge2); - EXPECT_TRUE(anOriginal.IsValid()); - EXPECT_EQ(anOriginal, anEdge0); -} - -TEST_F(BRepGraph_HistoryTest, FindDerived_ChainABC_ContainsBAndC) -{ - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - BRepGraph_NodeId anEdge1; - BRepGraph_NodeId anEdge2; - - myGraph.Editor().Gen().ApplyModification( - anEdge0, - [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_DynamicArray { - anEdge1 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); - NCollection_DynamicArray aResult; - aResult.Append(anEdge1); - return aResult; - }, - "Step1"); - - myGraph.Editor().Gen().ApplyModification( - anEdge1, - [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_DynamicArray { - anEdge2 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); - NCollection_DynamicArray aResult; - aResult.Append(anEdge2); - return aResult; - }, - "Step2"); - - const NCollection_DynamicArray aDerived = - myGraph.History().FindDerived(anEdge0); - bool hasEdge1 = false; - bool hasEdge2 = false; - for (const BRepGraph_NodeId& aDerivedId : aDerived) - { - if (aDerivedId == anEdge1) - { - hasEdge1 = true; - } - if (aDerivedId == anEdge2) - { - hasEdge2 = true; - } - } - EXPECT_TRUE(hasEdge1); - EXPECT_TRUE(hasEdge2); -} - -TEST_F(BRepGraph_HistoryTest, FindOriginal_UnmodifiedNode_ReturnsSelf) -{ - // FindOriginal returns the node itself when it is not derived from anything. - const BRepGraph_NodeId aFace(BRepGraph_NodeId::Kind::Face, 0); - const BRepGraph_NodeId anOriginal = myGraph.History().FindOriginal(aFace); - EXPECT_TRUE(anOriginal.IsValid()); - EXPECT_EQ(anOriginal, aFace); -} - -TEST_F(BRepGraph_HistoryTest, FindDerived_UnmodifiedNode_ReturnsEmpty) -{ - const BRepGraph_NodeId aFace(BRepGraph_NodeId::Kind::Face, 0); - const NCollection_DynamicArray aDerived = myGraph.History().FindDerived(aFace); - EXPECT_EQ(aDerived.Length(), 0); -} - -TEST_F(BRepGraph_HistoryTest, Disabled_RecordHistory_NoRecordStored) -{ - const size_t aNbBefore = myGraph.History().NbRecords(); - myGraph.History().SetEnabled(false); - - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - NCollection_DynamicArray aReplacements; - aReplacements.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); - myGraph.History().Record("Disabled", anEdge0, aReplacements); - - EXPECT_EQ(myGraph.History().NbRecords(), aNbBefore); -} - -TEST_F(BRepGraph_HistoryTest, Disabled_ApplyModification_ModifierStillRuns) -{ - myGraph.History().SetEnabled(false); - bool aModifierRan = false; - - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - myGraph.Editor().Gen().ApplyModification( - anEdge0, - [&](BRepGraph&, BRepGraph_NodeId) -> NCollection_DynamicArray { - aModifierRan = true; - return NCollection_DynamicArray(); - }, - "DisabledOp"); - - EXPECT_TRUE(aModifierRan); -} - -TEST_F(BRepGraph_HistoryTest, ReEnabled_RecordsAfterReEnable) -{ - myGraph.History().SetEnabled(false); - EXPECT_FALSE(myGraph.History().IsEnabled()); - - myGraph.History().SetEnabled(true); - EXPECT_TRUE(myGraph.History().IsEnabled()); - - const size_t aNbBefore = myGraph.History().NbRecords(); - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - NCollection_DynamicArray aReplacements; - aReplacements.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); - myGraph.History().Record("ReEnabled", anEdge0, aReplacements); - - EXPECT_EQ(myGraph.History().NbRecords(), aNbBefore + 1); -} - -TEST_F(BRepGraph_HistoryTest, ApplyModification_EmptyReplacements) -{ - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - const size_t aNbBefore = myGraph.History().NbRecords(); - - myGraph.Editor().Gen().ApplyModification( - anEdge0, - [](BRepGraph&, BRepGraph_NodeId) -> NCollection_DynamicArray { - return NCollection_DynamicArray(); - }, - "Delete"); - - EXPECT_EQ(myGraph.History().NbRecords(), aNbBefore + 1); - const BRepGraph_HistoryRecord& aRec = myGraph.History().Record(myGraph.History().NbRecords() - 1); - EXPECT_TRUE(aRec.OperationName.IsEqual("Delete")); -} - -TEST_F(BRepGraph_HistoryTest, ApplyModification_MultipleReplacements) -{ - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - BRepGraph_NodeId aNew1; - BRepGraph_NodeId aNew2; - - myGraph.Editor().Gen().ApplyModification( - anEdge0, - [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_DynamicArray { - const int aBase = theGraph.Topo().Edges().Nb(); - aNew1 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, aBase); - aNew2 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, aBase + 1); - NCollection_DynamicArray aResult; - aResult.Append(aNew1); - aResult.Append(aNew2); - return aResult; - }, - "Split"); - - const NCollection_DynamicArray aDerived = - myGraph.History().FindDerived(anEdge0); - bool hasNew1 = false; - bool hasNew2 = false; - for (const BRepGraph_NodeId& aDerivedId : aDerived) - { - if (aDerivedId == aNew1) - { - hasNew1 = true; - } - if (aDerivedId == aNew2) - { - hasNew2 = true; - } - } - EXPECT_TRUE(hasNew1); - EXPECT_TRUE(hasNew2); -} - -TEST_F(BRepGraph_HistoryTest, RecordHistory_EmptyReplacements_Stored) -{ - const size_t aNbBefore = myGraph.History().NbRecords(); - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - NCollection_DynamicArray anEmpty; - myGraph.History().Record("Erase", anEdge0, anEmpty); - - EXPECT_EQ(myGraph.History().NbRecords(), aNbBefore + 1); -} - -TEST_F(BRepGraph_HistoryTest, HistoryRecord_SequenceNumber_Monotonic) -{ - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - const BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); - const BRepGraph_NodeId anEdge2(BRepGraph_NodeId::Kind::Edge, 2); - - NCollection_DynamicArray aRepl; - aRepl.Append(anEdge1); - myGraph.History().Record("Op1", anEdge0, aRepl); - - NCollection_DynamicArray aRepl2; - aRepl2.Append(anEdge2); - myGraph.History().Record("Op2", anEdge1, aRepl2); - - const size_t aNb = myGraph.History().NbRecords(); - ASSERT_GE(aNb, 2); - const BRepGraph_HistoryRecord& aRec1 = myGraph.History().Record(aNb - 2); - const BRepGraph_HistoryRecord& aRec2 = myGraph.History().Record(aNb - 1); - EXPECT_LT(aRec1.SequenceNumber, aRec2.SequenceNumber); -} - -TEST_F(BRepGraph_HistoryTest, HistoryRecord_OperationName_Stored) -{ - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - NCollection_DynamicArray aRepl; - aRepl.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); - myGraph.History().Record("MyCustomOp", anEdge0, aRepl); - - const BRepGraph_HistoryRecord& aRec = myGraph.History().Record(myGraph.History().NbRecords() - 1); - EXPECT_TRUE(aRec.OperationName.IsEqual("MyCustomOp")); -} - -TEST_F(BRepGraph_HistoryTest, NbHistoryRecords_AfterMultipleOps_Correct) -{ - const size_t aNbBefore = myGraph.History().NbRecords(); - - const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - NCollection_DynamicArray aRepl; - aRepl.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); - - myGraph.History().Record("A", anEdge0, aRepl); - myGraph.History().Record("B", anEdge0, aRepl); - myGraph.History().Record("C", anEdge0, aRepl); - - EXPECT_EQ(myGraph.History().NbRecords(), aNbBefore + 3); -} - -TEST_F(BRepGraph_HistoryTest, FindOriginal_TwoApply_TransitiveTrace) -{ - const BRepGraph_NodeId aVtx0(BRepGraph_NodeId::Kind::Vertex, 0); - BRepGraph_NodeId aVtx1; - BRepGraph_NodeId aVtx2; - - myGraph.Editor().Gen().ApplyModification( - aVtx0, - [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_DynamicArray { - aVtx1 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Vertex, theGraph.Topo().Vertices().Nb()); - NCollection_DynamicArray aResult; - aResult.Append(aVtx1); - return aResult; - }, - "Move1"); - - myGraph.Editor().Gen().ApplyModification( - aVtx1, - [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_DynamicArray { - aVtx2 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Vertex, theGraph.Topo().Vertices().Nb()); - NCollection_DynamicArray aResult; - aResult.Append(aVtx2); - return aResult; - }, - "Move2"); - - // FindOriginal from the end of a 2-step chain should reach the root. - const BRepGraph_NodeId anOriginal = myGraph.History().FindOriginal(aVtx2); - EXPECT_TRUE(anOriginal.IsValid()); - EXPECT_EQ(anOriginal, aVtx0); - - // Intermediate node should also trace back to root. - const BRepGraph_NodeId anOriginal1 = myGraph.History().FindOriginal(aVtx1); - EXPECT_TRUE(anOriginal1.IsValid()); - EXPECT_EQ(anOriginal1, aVtx0); -} - -TEST_F(BRepGraph_HistoryTest, ApplyModification_WhenModifierThrows_DoesNotRecordHistory) -{ - const size_t aNbRecordsBefore = myGraph.History().NbRecords(); - - const BRepGraph_NodeId anEdge(BRepGraph_NodeId::Kind::Edge, 0); - -#if !defined(No_Exception) - EXPECT_THROW(myGraph.Editor().Gen().ApplyModification( - anEdge, - [](BRepGraph&, BRepGraph_NodeId) -> NCollection_DynamicArray { - throw Standard_Failure("Synthetic failure"); - }, - "ThrowingModification"), - Standard_Failure); -#endif - - EXPECT_EQ(myGraph.History().NbRecords(), aNbRecordsBefore); -} - -TEST_F(BRepGraph_HistoryTest, SplitEdge_RewritesAllContainingWires) -{ - ASSERT_GT(myGraph.Topo().Edges().Nb(), 0); - - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraphInc::EdgeDef& anEdgeDef = myGraph.Topo().Edges().Definition(anEdgeId); - - const double aSplitParam = 0.5 * (anEdgeDef.ParamFirst + anEdgeDef.ParamLast); - - const int aNbVerticesBefore = myGraph.Topo().Vertices().Nb(); - const gp_Pnt aSplitPoint(1.0, 2.0, 3.0); - const BRepGraph_VertexId aSplitVertex = myGraph.Editor().Vertices().Add(aSplitPoint, 1.0e-7); - - ASSERT_TRUE(aSplitVertex.IsValid()); - EXPECT_EQ(myGraph.Topo().Vertices().Nb(), aNbVerticesBefore + 1); - - const NCollection_DynamicArray& aWireIndices = - myGraph.Topo().Edges().Wires(anEdgeId); - ASSERT_GT(aWireIndices.Length(), 0); - - const uint32_t aNbEdgesBefore = myGraph.Topo().Edges().Nb(); - const uint32_t aNbActiveEdgesBefore = myGraph.Topo().Edges().NbActive(); - - BRepGraph_EdgeId aSubA; - BRepGraph_EdgeId aSubB; - - myGraph.Editor().Edges().Split(anEdgeId, aSplitVertex, aSplitParam, aSubA, aSubB); - - ASSERT_TRUE(aSubA.IsValid()); - ASSERT_TRUE(aSubB.IsValid()); - EXPECT_EQ(myGraph.Topo().Edges().Nb(), aNbEdgesBefore + 2); - EXPECT_EQ(myGraph.Topo().Edges().NbActive(), aNbActiveEdgesBefore + 1); - - for (const BRepGraph_WireId& aWireId : aWireIndices) - { - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, aWireId); - - bool hasOld = false; - bool hasSubA = false; - bool hasSubB = false; - int aSubAOrd = -1; - int aSubBOrd = -1; - - for (int anIdx = 0; anIdx < aCoEdgeRefs.Length(); ++anIdx) - { - const BRepGraphInc::CoEdgeRef& aCR = myGraph.Refs().CoEdges().Entry(aCoEdgeRefs.Value(anIdx)); - const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aCR.CoEdgeDefId); - const BRepGraph_NodeId anId(aCoEdge.EdgeDefId); - if (anId == anEdgeId) - { - hasOld = true; - } - else if (anId == aSubA) - { - hasSubA = true; - aSubAOrd = anIdx; - } - else if (anId == aSubB) - { - hasSubB = true; - aSubBOrd = anIdx; - } - } - - EXPECT_FALSE(hasOld); - EXPECT_TRUE(hasSubA); - EXPECT_TRUE(hasSubB); - EXPECT_GE(aSubAOrd, 0); - EXPECT_GE(aSubBOrd, 0); - EXPECT_EQ(aSubBOrd, aSubAOrd + 1); - } -} - -TEST_F(BRepGraph_HistoryTest, SplitEdge_IgnoresRemovedCoEdgeRefEntries) -{ - ASSERT_GT(myGraph.Topo().Edges().Nb(), 0); - - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraphInc::EdgeDef& anEdgeDef = myGraph.Topo().Edges().Definition(anEdgeId); - const double aSplitParam = 0.5 * (anEdgeDef.ParamFirst + anEdgeDef.ParamLast); - - const NCollection_DynamicArray& aWireIndices = - myGraph.Topo().Edges().Wires(anEdgeId); - ASSERT_GT(aWireIndices.Length(), 1); - - const BRepGraph_WireId aWireId = aWireIndices.Value(0); - const NCollection_DynamicArray aWireRefsBefore = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, aWireId); - ASSERT_GT(aWireRefsBefore.Length(), 0); - - BRepGraph_CoEdgeRefId aRefToRemove; - int aRemovedOrd = -1; - for (int aRefOrd = 0; aRefOrd < aWireRefsBefore.Length(); ++aRefOrd) - { - const BRepGraph_CoEdgeRefId aRefId = aWireRefsBefore.Value(aRefOrd); - const BRepGraphInc::CoEdgeRef& aRef = myGraph.Refs().CoEdges().Entry(aRefId); - const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aRef.CoEdgeDefId); - if (aCoEdge.EdgeDefId == anEdgeId) - { - aRefToRemove = aRefId; - aRemovedOrd = aRefOrd; - break; - } - } - ASSERT_TRUE(aRefToRemove.IsValid(myGraph.Refs().CoEdges().Nb())); - ASSERT_GE(aRemovedOrd, 0); - - { - BRepGraph_MutGuard aMut = - myGraph.Editor().CoEdges().MutRef(aRefToRemove); - myGraph.Editor().Gen().RemoveRef(aRefToRemove); - } - ASSERT_TRUE(myGraph.Refs().CoEdges().Entry(aRefToRemove).IsRemoved); - const BRepGraph_CoEdgeId aRemovedCoEdgeId = - myGraph.Refs().CoEdges().Entry(aRefToRemove).CoEdgeDefId; - - const int aRemovedWireNbActiveBefore = - BRepGraph_TestTools::CountCoEdgeRefsOfWire(myGraph, aWireId); - - const BRepGraph_VertexId aSplitVertex = - myGraph.Editor().Vertices().Add(gp_Pnt(4.0, 5.0, 6.0), 1.0e-7); - ASSERT_TRUE(aSplitVertex.IsValid()); - - BRepGraph_EdgeId aSubA; - BRepGraph_EdgeId aSubB; - myGraph.Editor().Edges().Split(anEdgeId, aSplitVertex, aSplitParam, aSubA, aSubB); - ASSERT_TRUE(aSubA.IsValid()); - ASSERT_TRUE(aSubB.IsValid()); - - EXPECT_TRUE(myGraph.Refs().CoEdges().Entry(aRefToRemove).IsRemoved); - EXPECT_EQ(BRepGraph_TestTools::CountCoEdgeRefsOfWire(myGraph, aWireId), - aRemovedWireNbActiveBefore); - const BRepGraphInc::CoEdgeDef& aRemovedCoEdgeAfter = - myGraph.Topo().CoEdges().Definition(aRemovedCoEdgeId); - EXPECT_EQ(aRemovedCoEdgeAfter.EdgeDefId, anEdgeId); - - bool hasSubA = false; - bool hasSubB = false; - for (BRepGraph_CoEdgeRefId aRefId = BRepGraph_CoEdgeRefId::Start(); - aRefId.IsValid(myGraph.Refs().CoEdges().Nb()); - ++aRefId) - { - const BRepGraphInc::CoEdgeRef& aRef = myGraph.Refs().CoEdges().Entry(aRefId); - if (aRef.IsRemoved || !aRef.CoEdgeDefId.IsValid(myGraph.Topo().CoEdges().Nb())) - { - continue; - } - const BRepGraph_NodeId anId(myGraph.Topo().CoEdges().Definition(aRef.CoEdgeDefId).EdgeDefId); - if (anId == aSubA) - { - hasSubA = true; - } - if (anId == aSubB) - { - hasSubB = true; - } - } - EXPECT_TRUE(hasSubA); - EXPECT_TRUE(hasSubB); -} - -TEST_F(BRepGraph_HistoryTest, ApplyModification_SplitEdge_RecordsBothDerivedNodes) -{ - ASSERT_GT(myGraph.Topo().Edges().Nb(), 0); - - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraphInc::EdgeDef& anEdgeDef = myGraph.Topo().Edges().Definition(anEdgeId); - const double aSplitParam = 0.5 * (anEdgeDef.ParamFirst + anEdgeDef.ParamLast); - - const BRepGraph_VertexId aSplitVertex = - myGraph.Editor().Vertices().Add(gp_Pnt(4.0, 5.0, 6.0), 1.0e-7); - ASSERT_TRUE(aSplitVertex.IsValid()); - - const size_t aNbRecordsBefore = myGraph.History().NbRecords(); - - myGraph.Editor().Gen().ApplyModification( - anEdgeId, - [&](BRepGraph& theGraph, - BRepGraph_NodeId theTarget) -> NCollection_DynamicArray { - BRepGraph_EdgeId aSubA; - BRepGraph_EdgeId aSubB; - theGraph.Editor().Edges().Split(BRepGraph_EdgeId::FromNodeId(theTarget), - aSplitVertex, - aSplitParam, - aSubA, - aSubB); - - NCollection_DynamicArray aResult; - aResult.Append(aSubA); - aResult.Append(aSubB); - return aResult; - }, - "Split"); - - EXPECT_EQ(myGraph.History().NbRecords(), aNbRecordsBefore + 1); - - const NCollection_DynamicArray aDerived = - myGraph.History().FindDerived(anEdgeId); - EXPECT_GE(aDerived.Length(), 2); -} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ItemId_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ItemId_Test.cxx new file mode 100644 index 0000000000..b7b2fee23d --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ItemId_Test.cxx @@ -0,0 +1,64 @@ +// 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 + +#include + +TEST(BRepGraph_ItemIdTest, Default_IsInvalid) +{ + const BRepGraph_ItemId anItem; + + EXPECT_FALSE(anItem.IsValid()); + EXPECT_FALSE(anItem.IsNode()); + EXPECT_FALSE(anItem.IsReference()); +} + +TEST(BRepGraph_ItemIdTest, NodeRoundTrip) +{ + const BRepGraph_NodeId aNode(BRepGraph_NodeId::Kind::Face, 3u); + const BRepGraph_ItemId anItem(aNode); + + EXPECT_TRUE(anItem.IsValid()); + EXPECT_TRUE(anItem.IsNode()); + EXPECT_FALSE(anItem.IsReference()); + EXPECT_EQ(anItem.ItemDomain(), BRepGraph_ItemId::Domain::Node); + EXPECT_EQ(anItem.NodeKind(), BRepGraph_NodeId::Kind::Face); + EXPECT_EQ(anItem.NodeId(), aNode); + EXPECT_FALSE(anItem.RefId().IsValid()); +} + +TEST(BRepGraph_ItemIdTest, ReferenceRoundTrip) +{ + const BRepGraph_RefId aRef(BRepGraph_RefId::Kind::Wire, 4u); + const BRepGraph_ItemId anItem(aRef); + + EXPECT_TRUE(anItem.IsValid()); + EXPECT_FALSE(anItem.IsNode()); + EXPECT_TRUE(anItem.IsReference()); + EXPECT_EQ(anItem.ItemDomain(), BRepGraph_ItemId::Domain::Reference); + EXPECT_EQ(anItem.RefKind(), BRepGraph_RefId::Kind::Wire); + EXPECT_EQ(anItem.RefId(), aRef); + EXPECT_FALSE(anItem.NodeId().IsValid()); +} + +TEST(BRepGraph_ItemIdTest, InvalidSourceKindProducesInvalidItem) +{ + constexpr uint32_t THE_RESERVED_KIND = 9u; + const BRepGraph_NodeId aNode(static_cast(THE_RESERVED_KIND), 0u); + const BRepGraph_ItemId anItem(aNode); + + EXPECT_FALSE(aNode.IsValid()); + EXPECT_FALSE(anItem.IsValid()); + EXPECT_EQ(anItem.ItemDomain(), BRepGraph_ItemId::Domain::None); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Iterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Iterator_Test.cxx index f1e708cadd..76a48c6652 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Iterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Iterator_Test.cxx @@ -15,12 +15,14 @@ #include #include #include -#include +#include #include #include #include +#include + #include class BRepGraph_IteratorTest : public testing::Test @@ -30,8 +32,8 @@ protected: { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + myGraph.Shapes().Add(aBoxMaker.Shape()); } BRepGraph myGraph; @@ -102,11 +104,11 @@ TEST_F(BRepGraph_IteratorTest, RootProductIterator_MatchesStoredRoots) int aCount = 0; for (BRepGraph_RootProductIterator anIt(myGraph); anIt.More(); anIt.Next()) { - ASSERT_LT(aCount, myGraph.RootProductIds().Length()); + ASSERT_LT(aCount, myGraph.RootProductIds().Size()); EXPECT_EQ(anIt.Current(), myGraph.RootProductIds().Value(aCount)); ++aCount; } - EXPECT_EQ(aCount, myGraph.RootProductIds().Length()); + EXPECT_EQ(aCount, myGraph.RootProductIds().Size()); } TEST_F(BRepGraph_IteratorTest, CurrentId_ReturnsValidTypedIds) @@ -130,7 +132,7 @@ TEST_F(BRepGraph_IteratorTest, Current_ReturnsDefinition) TEST_F(BRepGraph_IteratorTest, RemovedFace_SkippedByDefaultIterator) { - const int aNbBefore = myGraph.Topo().Faces().Nb(); + const uint32_t aNbBefore = myGraph.Topo().Faces().Nb(); myGraph.Editor().Gen().RemoveNode(BRepGraph_FaceId::Start()); int aCount = 0; @@ -143,7 +145,7 @@ TEST_F(BRepGraph_IteratorTest, RemovedFace_SkippedByDefaultIterator) TEST_F(BRepGraph_IteratorTest, FullTraverse_IncludesRemovedFace) { - const int aNbBefore = myGraph.Topo().Faces().Nb(); + const uint32_t aNbBefore = myGraph.Topo().Faces().Nb(); myGraph.Editor().Gen().RemoveNode(BRepGraph_FaceId::Start()); int aCount = 0; @@ -159,7 +161,7 @@ TEST_F(BRepGraph_IteratorTest, RangeFor_WorksCorrectly) int aCount = 0; for (const BRepGraphInc::FaceDef& aFace : BRepGraph_FaceIterator(myGraph)) { - (void)aFace; + std::ignore = aFace; ++aCount; } EXPECT_EQ(aCount, 6); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_LayerHistory_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_LayerHistory_Test.cxx new file mode 100644 index 0000000000..c11fc1b524 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_LayerHistory_Test.cxx @@ -0,0 +1,1106 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BRepGraph_RefTestTools.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class BRepGraph_LayerHistoryTest : public testing::Test +{ +protected: + void SetUp() override + { + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + myGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + myGraph.Shapes().Add(aBoxMaker.Shape()); + } + + BRepGraph myGraph; +}; + +static NCollection_LinearVector collectWiresOfEdge(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge) +{ + NCollection_LinearVector aWires; + for (BRepGraph_WiresOfEdge aWireIt = theGraph.Topo().Edges().WiresOf(theEdge); aWireIt.More(); + aWireIt.Next()) + { + aWires.Append(aWireIt.CurrentId()); + } + return aWires; +} + +// ============================================================ +// History chain tests +// ============================================================ + +TEST_F(BRepGraph_LayerHistoryTest, FindOriginal_ChainABC_ReturnsA) +{ + // Build a chain: edge0 -> edge1 -> edge2 via two ApplyModification calls. + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + BRepGraph_NodeId anEdge1; + BRepGraph_NodeId anEdge2; + + myGraph.Editor().Gen().ApplyModification( + anEdge0, + [&](BRepGraph& theGraph, + BRepGraph_NodeId /*theTarget*/) -> NCollection_LinearVector { + // Simulate producing a new edge node at index NbEdgeDefs. + anEdge1 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); + NCollection_LinearVector aResult; + aResult.Append(anEdge1); + return aResult; + }, + "Step1"); + + myGraph.Editor().Gen().ApplyModification( + anEdge1, + [&](BRepGraph& theGraph, + BRepGraph_NodeId /*theTarget*/) -> NCollection_LinearVector { + anEdge2 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); + NCollection_LinearVector aResult; + aResult.Append(anEdge2); + return aResult; + }, + "Step2"); + + const BRepGraph_NodeId anOriginal = + myGraph.LayerRegistry().Ensure()->FindOriginal(anEdge2); + EXPECT_TRUE(anOriginal.IsValid()); + EXPECT_EQ(anOriginal, anEdge0); +} + +TEST_F(BRepGraph_LayerHistoryTest, FindDerived_ChainABC_ContainsBAndC) +{ + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + BRepGraph_NodeId anEdge1; + BRepGraph_NodeId anEdge2; + + myGraph.Editor().Gen().ApplyModification( + anEdge0, + [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_LinearVector { + anEdge1 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); + NCollection_LinearVector aResult; + aResult.Append(anEdge1); + return aResult; + }, + "Step1"); + + myGraph.Editor().Gen().ApplyModification( + anEdge1, + [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_LinearVector { + anEdge2 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); + NCollection_LinearVector aResult; + aResult.Append(anEdge2); + return aResult; + }, + "Step2"); + + const NCollection_LinearVector aDerived = + myGraph.LayerRegistry().Ensure()->FindDerived(anEdge0); + bool hasEdge1 = false; + bool hasEdge2 = false; + for (const BRepGraph_NodeId& aDerivedId : aDerived) + { + if (aDerivedId == anEdge1) + { + hasEdge1 = true; + } + if (aDerivedId == anEdge2) + { + hasEdge2 = true; + } + } + EXPECT_TRUE(hasEdge1); + EXPECT_TRUE(hasEdge2); +} + +TEST_F(BRepGraph_LayerHistoryTest, FindOriginal_UnmodifiedNode_ReturnsSelf) +{ + // FindOriginal returns the node itself when it is not derived from anything. + const BRepGraph_NodeId aFace(BRepGraph_NodeId::Kind::Face, 0); + const BRepGraph_NodeId anOriginal = + myGraph.LayerRegistry().Ensure()->FindOriginal(aFace); + EXPECT_TRUE(anOriginal.IsValid()); + EXPECT_EQ(anOriginal, aFace); +} + +TEST_F(BRepGraph_LayerHistoryTest, FindDerived_UnmodifiedNode_ReturnsEmpty) +{ + const BRepGraph_NodeId aFace(BRepGraph_NodeId::Kind::Face, 0); + const NCollection_LinearVector aDerived = + myGraph.LayerRegistry().Ensure()->FindDerived(aFace); + EXPECT_EQ(aDerived.Size(), size_t(0)); +} + +TEST_F(BRepGraph_LayerHistoryTest, Disabled_RecordHistory_NoRecordStored) +{ + const size_t aNbBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + myGraph.LayerRegistry().Ensure()->SetEnabled(false); + + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + NCollection_LinearVector aReplacements; + aReplacements.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); + myGraph.LayerRegistry().Ensure()->Record("Disabled", + anEdge0, + aReplacements.ToArray1()); + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore); +} + +TEST_F(BRepGraph_LayerHistoryTest, Disabled_ApplyModification_ModifierStillRuns) +{ + myGraph.LayerRegistry().Ensure()->SetEnabled(false); + bool aModifierRan = false; + + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + myGraph.Editor().Gen().ApplyModification( + anEdge0, + [&](BRepGraph&, BRepGraph_NodeId) -> NCollection_LinearVector { + aModifierRan = true; + return NCollection_LinearVector(); + }, + "DisabledOp"); + + EXPECT_TRUE(aModifierRan); +} + +TEST_F(BRepGraph_LayerHistoryTest, ReEnabled_RecordsAfterReEnable) +{ + myGraph.LayerRegistry().Ensure()->SetEnabled(false); + EXPECT_FALSE(myGraph.LayerRegistry().Ensure()->IsEnabled()); + + myGraph.LayerRegistry().Ensure()->SetEnabled(true); + EXPECT_TRUE(myGraph.LayerRegistry().Ensure()->IsEnabled()); + + const size_t aNbBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + NCollection_LinearVector aReplacements; + aReplacements.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); + myGraph.LayerRegistry().Ensure()->Record("ReEnabled", + anEdge0, + aReplacements.ToArray1()); + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore + 1); +} + +TEST_F(BRepGraph_LayerHistoryTest, ApplyModification_EmptyReplacements) +{ + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + const size_t aNbBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + + myGraph.Editor().Gen().ApplyModification( + anEdge0, + [](BRepGraph&, BRepGraph_NodeId) -> NCollection_LinearVector { + return NCollection_LinearVector(); + }, + "Delete"); + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore + 1); + const BRepGraph_LayerHistory::Event& aRec = + myGraph.LayerRegistry().Ensure()->Record( + myGraph.LayerRegistry().Ensure()->NbRecords() - 1); + EXPECT_TRUE(aRec.OperationName.IsEqual("Delete")); +} + +TEST_F(BRepGraph_LayerHistoryTest, ApplyModification_MultipleReplacements) +{ + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + BRepGraph_NodeId aNew1; + BRepGraph_NodeId aNew2; + + myGraph.Editor().Gen().ApplyModification( + anEdge0, + [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_LinearVector { + const uint32_t aBase = theGraph.Topo().Edges().Nb(); + aNew1 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, aBase); + aNew2 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, aBase + 1); + NCollection_LinearVector aResult; + aResult.Append(aNew1); + aResult.Append(aNew2); + return aResult; + }, + "Split"); + + const NCollection_LinearVector aDerived = + myGraph.LayerRegistry().Ensure()->FindDerived(anEdge0); + bool hasNew1 = false; + bool hasNew2 = false; + for (const BRepGraph_NodeId& aDerivedId : aDerived) + { + if (aDerivedId == aNew1) + { + hasNew1 = true; + } + if (aDerivedId == aNew2) + { + hasNew2 = true; + } + } + EXPECT_TRUE(hasNew1); + EXPECT_TRUE(hasNew2); +} + +TEST_F(BRepGraph_LayerHistoryTest, RecordHistory_EmptyReplacements_Stored) +{ + const size_t aNbBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + NCollection_LinearVector anEmpty; + myGraph.LayerRegistry().Ensure()->Record("Erase", + anEdge0, + anEmpty.ToArray1()); + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore + 1); +} + +TEST_F(BRepGraph_LayerHistoryTest, HistoryRecord_SequenceNumber_Monotonic) +{ + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + const BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); + const BRepGraph_NodeId anEdge2(BRepGraph_NodeId::Kind::Edge, 2); + + NCollection_LinearVector aRepl; + aRepl.Append(anEdge1); + myGraph.LayerRegistry().Ensure()->Record("Op1", + anEdge0, + aRepl.ToArray1()); + + NCollection_LinearVector aRepl2; + aRepl2.Append(anEdge2); + myGraph.LayerRegistry().Ensure()->Record("Op2", + anEdge1, + aRepl2.ToArray1()); + + const size_t aNb = myGraph.LayerRegistry().Ensure()->NbRecords(); + ASSERT_GE(aNb, 2); + const BRepGraph_LayerHistory::Event& aRec1 = + myGraph.LayerRegistry().Ensure()->Record(aNb - 2); + const BRepGraph_LayerHistory::Event& aRec2 = + myGraph.LayerRegistry().Ensure()->Record(aNb - 1); + EXPECT_LT(aRec1.SequenceNumber, aRec2.SequenceNumber); +} + +TEST_F(BRepGraph_LayerHistoryTest, HistoryRecord_OperationName_Stored) +{ + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + NCollection_LinearVector aRepl; + aRepl.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); + myGraph.LayerRegistry().Ensure()->Record("MyCustomOp", + anEdge0, + aRepl.ToArray1()); + + const BRepGraph_LayerHistory::Event& aRec = + myGraph.LayerRegistry().Ensure()->Record( + myGraph.LayerRegistry().Ensure()->NbRecords() - 1); + EXPECT_TRUE(aRec.OperationName.IsEqual("MyCustomOp")); +} + +TEST_F(BRepGraph_LayerHistoryTest, NbHistoryRecords_AfterMultipleOps_Correct) +{ + const size_t aNbBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + + const BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + NCollection_LinearVector aRepl; + aRepl.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); + + myGraph.LayerRegistry().Ensure()->Record("A", anEdge0, aRepl.ToArray1()); + myGraph.LayerRegistry().Ensure()->Record("B", anEdge0, aRepl.ToArray1()); + myGraph.LayerRegistry().Ensure()->Record("C", anEdge0, aRepl.ToArray1()); + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore + 3); +} + +TEST_F(BRepGraph_LayerHistoryTest, FindOriginal_TwoApply_TransitiveTrace) +{ + const BRepGraph_NodeId aVtx0(BRepGraph_NodeId::Kind::Vertex, 0); + BRepGraph_NodeId aVtx1; + BRepGraph_NodeId aVtx2; + + myGraph.Editor().Gen().ApplyModification( + aVtx0, + [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_LinearVector { + aVtx1 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Vertex, theGraph.Topo().Vertices().Nb()); + NCollection_LinearVector aResult; + aResult.Append(aVtx1); + return aResult; + }, + "Move1"); + + myGraph.Editor().Gen().ApplyModification( + aVtx1, + [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_LinearVector { + aVtx2 = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Vertex, theGraph.Topo().Vertices().Nb()); + NCollection_LinearVector aResult; + aResult.Append(aVtx2); + return aResult; + }, + "Move2"); + + // FindOriginal from the end of a 2-step chain should reach the root. + const BRepGraph_NodeId anOriginal = + myGraph.LayerRegistry().Ensure()->FindOriginal(aVtx2); + EXPECT_TRUE(anOriginal.IsValid()); + EXPECT_EQ(anOriginal, aVtx0); + + // Intermediate node should also trace back to root. + const BRepGraph_NodeId anOriginal1 = + myGraph.LayerRegistry().Ensure()->FindOriginal(aVtx1); + EXPECT_TRUE(anOriginal1.IsValid()); + EXPECT_EQ(anOriginal1, aVtx0); +} + +TEST_F(BRepGraph_LayerHistoryTest, ApplyModification_WhenModifierThrows_DoesNotRecordHistory) +{ + const size_t aNbRecordsBefore = + myGraph.LayerRegistry().Ensure()->NbRecords(); + + const BRepGraph_NodeId anEdge(BRepGraph_NodeId::Kind::Edge, 0); + +#if !defined(No_Exception) + EXPECT_THROW(myGraph.Editor().Gen().ApplyModification( + anEdge, + [](BRepGraph&, BRepGraph_NodeId) -> NCollection_LinearVector { + throw Standard_Failure("Synthetic failure"); + }, + "ThrowingModification"), + Standard_Failure); +#endif + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), + aNbRecordsBefore); +} + +TEST_F(BRepGraph_LayerHistoryTest, SplitEdge_RewritesAllContainingWires) +{ + ASSERT_GT(myGraph.Topo().Edges().Nb(), 0u); + + const BRepGraph_EdgeId anEdgeId(0); + const std::pair anEdgeRange = BRepGraph_Tool::Edge::Range(myGraph, anEdgeId); + + const double aSplitParam = 0.5 * (anEdgeRange.first + anEdgeRange.second); + + const uint32_t aNbVerticesBefore = myGraph.Topo().Vertices().Nb(); + const gp_Pnt aSplitPoint(1.0, 2.0, 3.0); + const BRepGraph_VertexId aSplitVertex = myGraph.Editor().Vertices().Add(aSplitPoint, 1.0e-7); + + ASSERT_TRUE(aSplitVertex.IsValid()); + EXPECT_EQ(myGraph.Topo().Vertices().Nb(), aNbVerticesBefore + 1); + + const NCollection_LinearVector aWireIndices = + collectWiresOfEdge(myGraph, anEdgeId); + ASSERT_GT(aWireIndices.Size(), 0u); + + const uint32_t aNbEdgesBefore = myGraph.Topo().Edges().Nb(); + const uint32_t aNbActiveEdgesBefore = myGraph.Topo().Edges().NbActive(); + + BRepGraph_EdgeId aSubA; + BRepGraph_EdgeId aSubB; + + myGraph.Editor().Edges().Split(anEdgeId, aSplitVertex, aSplitParam, aSubA, aSubB); + + ASSERT_TRUE(aSubA.IsValid()); + ASSERT_TRUE(aSubB.IsValid()); + EXPECT_EQ(myGraph.Topo().Edges().Nb(), aNbEdgesBefore + 2); + EXPECT_EQ(myGraph.Topo().Edges().NbActive(), aNbActiveEdgesBefore + 1); + + for (const BRepGraph_WireId& aWireId : aWireIndices) + { + const NCollection_LinearVector& aCoEdgeIds = + myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds; + + bool hasOld = false; + bool hasSubA = false; + bool hasSubB = false; + constexpr size_t THE_NOT_FOUND = std::numeric_limits::max(); + size_t aSubAOrd = THE_NOT_FOUND; + size_t aSubBOrd = THE_NOT_FOUND; + + for (size_t anIdx = 0; anIdx < aCoEdgeIds.Size(); ++anIdx) + { + const BRepGraphInc::CoEdgeDef& aCoEdge = + myGraph.Topo().CoEdges().Definition(aCoEdgeIds.Value(anIdx)); + const BRepGraph_NodeId anId(aCoEdge.ChildEdgeId); + if (anId == anEdgeId) + { + hasOld = true; + } + else if (anId == aSubA) + { + hasSubA = true; + aSubAOrd = anIdx; + } + else if (anId == aSubB) + { + hasSubB = true; + aSubBOrd = anIdx; + } + } + + EXPECT_FALSE(hasOld); + EXPECT_TRUE(hasSubA); + EXPECT_TRUE(hasSubB); + EXPECT_NE(aSubAOrd, THE_NOT_FOUND); + EXPECT_NE(aSubBOrd, THE_NOT_FOUND); + const size_t aDistance = (aSubAOrd < aSubBOrd) ? (aSubBOrd - aSubAOrd) : (aSubAOrd - aSubBOrd); + EXPECT_TRUE(aDistance == 1 || aDistance + 1 == aCoEdgeIds.Size()); + } +} + +TEST_F(BRepGraph_LayerHistoryTest, SplitEdge_IgnoresRemovedCoEdgeEntries) +{ + ASSERT_GT(myGraph.Topo().Edges().Nb(), 0u); + + const BRepGraph_EdgeId anEdgeId(0); + const std::pair anEdgeRange = BRepGraph_Tool::Edge::Range(myGraph, anEdgeId); + const double aSplitParam = 0.5 * (anEdgeRange.first + anEdgeRange.second); + + const NCollection_LinearVector aWireIndices = + collectWiresOfEdge(myGraph, anEdgeId); + ASSERT_GT(aWireIndices.Size(), 1); + + const BRepGraph_WireId aWireId = aWireIndices.Value(0); + const NCollection_LinearVector& aWireCoEdgesBefore = + myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds; + ASSERT_GT(aWireCoEdgesBefore.Size(), 0u); + + constexpr size_t THE_NOT_FOUND = std::numeric_limits::max(); + BRepGraph_CoEdgeId aCoEdgeToRemove; + size_t aRemovedOrd = THE_NOT_FOUND; + for (size_t aRefOrd = 0; aRefOrd < aWireCoEdgesBefore.Size(); ++aRefOrd) + { + const BRepGraph_CoEdgeId aCoEdgeId = aWireCoEdgesBefore.Value(aRefOrd); + const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aCoEdgeId); + if (aCoEdge.ChildEdgeId == anEdgeId) + { + aCoEdgeToRemove = aCoEdgeId; + aRemovedOrd = aRefOrd; + break; + } + } + ASSERT_TRUE(aCoEdgeToRemove.IsValid(myGraph.Topo().CoEdges().Nb())); + ASSERT_NE(aRemovedOrd, THE_NOT_FOUND); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aCoEdgeToRemove)); + ASSERT_TRUE(aCoEdgeToRemove.IsRemoved(myGraph)); + const BRepGraph_CoEdgeId aRemovedCoEdgeId = aCoEdgeToRemove; + + const size_t aRemovedWireNbActiveBefore = + myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds.Size(); + + const BRepGraph_VertexId aSplitVertex = + myGraph.Editor().Vertices().Add(gp_Pnt(4.0, 5.0, 6.0), 1.0e-7); + ASSERT_TRUE(aSplitVertex.IsValid()); + + BRepGraph_EdgeId aSubA; + BRepGraph_EdgeId aSubB; + myGraph.Editor().Edges().Split(anEdgeId, aSplitVertex, aSplitParam, aSubA, aSubB); + ASSERT_TRUE(aSubA.IsValid()); + ASSERT_TRUE(aSubB.IsValid()); + + EXPECT_TRUE(aCoEdgeToRemove.IsRemoved(myGraph)); + EXPECT_EQ(myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds.Size(), aRemovedWireNbActiveBefore); + const BRepGraphInc::CoEdgeDef& aRemovedCoEdgeAfter = + myGraph.Topo().CoEdges().Definition(aRemovedCoEdgeId); + EXPECT_EQ(aRemovedCoEdgeAfter.ChildEdgeId, anEdgeId); + + bool hasSubA = false; + bool hasSubB = false; + for (BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_CoEdgeId::Start(); + aCoEdgeId.IsValid(myGraph.Topo().CoEdges().Nb()); + ++aCoEdgeId) + { + const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aCoEdgeId); + if (aCoEdgeId.IsRemoved(myGraph)) + { + continue; + } + const BRepGraph_NodeId anId(aCoEdge.ChildEdgeId); + if (anId == aSubA) + { + hasSubA = true; + } + if (anId == aSubB) + { + hasSubB = true; + } + } + EXPECT_TRUE(hasSubA); + EXPECT_TRUE(hasSubB); +} + +TEST_F(BRepGraph_LayerHistoryTest, ApplyModification_SplitEdge_RecordsBothDerivedNodes) +{ + ASSERT_GT(myGraph.Topo().Edges().Nb(), 0u); + + const BRepGraph_EdgeId anEdgeId(0); + const std::pair anEdgeRange = BRepGraph_Tool::Edge::Range(myGraph, anEdgeId); + const double aSplitParam = 0.5 * (anEdgeRange.first + anEdgeRange.second); + + const BRepGraph_VertexId aSplitVertex = + myGraph.Editor().Vertices().Add(gp_Pnt(4.0, 5.0, 6.0), 1.0e-7); + ASSERT_TRUE(aSplitVertex.IsValid()); + + const size_t aNbRecordsBefore = + myGraph.LayerRegistry().Ensure()->NbRecords(); + + myGraph.Editor().Gen().ApplyModification( + anEdgeId, + [&](BRepGraph& theGraph, + BRepGraph_NodeId theTarget) -> NCollection_LinearVector { + BRepGraph_EdgeId aSubA; + BRepGraph_EdgeId aSubB; + theGraph.Editor().Edges().Split(BRepGraph_EdgeId::FromNodeId(theTarget), + aSplitVertex, + aSplitParam, + aSubA, + aSubB); + + NCollection_LinearVector aResult; + aResult.Append(aSubA); + aResult.Append(aSubB); + return aResult; + }, + "Split"); + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), + aNbRecordsBefore + 1); + + const NCollection_LinearVector aDerived = + myGraph.LayerRegistry().Ensure()->FindDerived(anEdgeId); + EXPECT_GE(aDerived.Size(), 2); +} + +TEST_F(BRepGraph_LayerHistoryTest, FindModified_PerKindLookup_ReturnsOnlyModified) +{ + const BRepGraph_NodeId anOrig(BRepGraph_NodeId::Kind::Edge, 0); + BRepGraph_NodeId aRepl; + + myGraph.Editor().Gen().ApplyModification( + anOrig, + [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_LinearVector { + aRepl = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); + NCollection_LinearVector aResult; + aResult.Append(aRepl); + return aResult; + }, + "Modify"); + + const NCollection_LinearVector* aMod = + myGraph.LayerRegistry().Ensure()->FindModified(anOrig); + ASSERT_NE(aMod, nullptr); + EXPECT_EQ(aMod->Size(), 1); + EXPECT_EQ(aMod->Value(0), aRepl); + + const NCollection_LinearVector* aGen = + myGraph.LayerRegistry().Ensure()->FindGenerated(anOrig); + EXPECT_EQ(aGen, nullptr); +} + +TEST_F(BRepGraph_LayerHistoryTest, FindGenerated_PerKindLookup_ReturnsOnlyGenerated) +{ + const BRepGraph_NodeId anOrig(BRepGraph_NodeId::Kind::Edge, 0); + const BRepGraph_NodeId anRepl(BRepGraph_NodeId::Kind::Edge, 1); + + NCollection_LinearVector aReplVec; + aReplVec.Append(anRepl); + + myGraph.LayerRegistry().Ensure()->Record( + "Generate", + anOrig, + aReplVec.ToArray1(), + BRepGraph_LayerHistory::Kind::Generated); + + const NCollection_LinearVector* aGen = + myGraph.LayerRegistry().Ensure()->FindGenerated(anOrig); + ASSERT_NE(aGen, nullptr); + EXPECT_EQ(aGen->Size(), 1); + EXPECT_EQ(aGen->Value(0), anRepl); + + const NCollection_LinearVector* aMod = + myGraph.LayerRegistry().Ensure()->FindModified(anOrig); + EXPECT_EQ(aMod, nullptr); +} + +TEST_F(BRepGraph_LayerHistoryTest, RecordDeleted_MarksNodesAsDeleted) +{ + const BRepGraph_NodeId anEdgeA(BRepGraph_NodeId::Kind::Edge, 0); + const BRepGraph_NodeId anEdgeB(BRepGraph_NodeId::Kind::Edge, 1); + + NCollection_LinearVector aDeleted; + aDeleted.Append(anEdgeA); + aDeleted.Append(anEdgeB); + + const size_t aNbBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + myGraph.LayerRegistry().Ensure()->RecordDeleted("DeleteEdges", + aDeleted.ToArray1()); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore + 1); + + EXPECT_TRUE(myGraph.LayerRegistry().Ensure()->IsDeleted(anEdgeA)); + EXPECT_TRUE(myGraph.LayerRegistry().Ensure()->IsDeleted(anEdgeB)); + + const NCollection_FlatMap& aDeletedSet = + myGraph.LayerRegistry().Ensure()->DeletedNodes(); + EXPECT_TRUE(aDeletedSet.Contains(anEdgeA)); + EXPECT_TRUE(aDeletedSet.Contains(anEdgeB)); +} + +TEST_F(BRepGraph_LayerHistoryTest, RecordDeleted_EmptyList_IsNoop) +{ + const size_t aNbBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + NCollection_LinearVector aEmpty; + myGraph.LayerRegistry().Ensure()->RecordDeleted("Noop", + aEmpty.ToArray1()); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore); +} + +TEST_F(BRepGraph_LayerHistoryTest, Record_EmptyReplacements_AutoDeleted) +{ + const BRepGraph_NodeId anOrig(BRepGraph_NodeId::Kind::Edge, 0); + NCollection_LinearVector aEmpty; + + myGraph.LayerRegistry().Ensure()->Record( + "Consumed", + anOrig, + aEmpty.ToArray1(), + BRepGraph_LayerHistory::Kind::Generated); + + EXPECT_TRUE(myGraph.LayerRegistry().Ensure()->IsDeleted(anOrig)); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), 1); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->Record(size_t(0)).RecordKind, + BRepGraph_LayerHistory::Kind::Deleted); +} + +TEST_F(BRepGraph_LayerHistoryTest, FindDerived_ModifiedAndGenerated_CollectsBoth) +{ + const BRepGraph_NodeId anOrig(BRepGraph_NodeId::Kind::Edge, 0); + BRepGraph_NodeId aModRepl; + BRepGraph_NodeId aGenRepl; + + myGraph.Editor().Gen().ApplyModification( + anOrig, + [&](BRepGraph& theGraph, BRepGraph_NodeId) -> NCollection_LinearVector { + aModRepl = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, theGraph.Topo().Edges().Nb()); + NCollection_LinearVector aResult; + aResult.Append(aModRepl); + return aResult; + }, + "FirstModified"); + + // Record a Generated event on the Modified result. + aGenRepl = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 2); + NCollection_LinearVector aGenRepVec; + aGenRepVec.Append(aGenRepl); + myGraph.LayerRegistry().Ensure()->Record( + "ThenGenerated", + aModRepl, + aGenRepVec.ToArray1(), + BRepGraph_LayerHistory::Kind::Generated); + + const NCollection_LinearVector aAll = + myGraph.LayerRegistry().Ensure()->FindDerived(anOrig); + EXPECT_GE(aAll.Size(), 1); + bool aFoundGen = false; + for (const BRepGraph_NodeId& aNode : aAll) + { + if (aNode == aGenRepl) + { + aFoundGen = true; + } + } + EXPECT_TRUE(aFoundGen); + // Verify the Generated record is in the per-kind map. + EXPECT_NE(myGraph.LayerRegistry().Ensure()->FindGenerated(aModRepl), + nullptr); + EXPECT_NE(myGraph.LayerRegistry().Ensure()->FindModified(anOrig), + nullptr); +} + +TEST_F(BRepGraph_LayerHistoryTest, RecordBatch_ExplicitGeneratedKind_StoredCorrectly) +{ + const BRepGraph_NodeId anOrig(BRepGraph_NodeId::Kind::Edge, 0); + const BRepGraph_NodeId anRepl(BRepGraph_NodeId::Kind::Edge, 1); + + NCollection_LinearVector aOriginals; + NCollection_LinearVector aReplacements; + aOriginals.Append(anOrig); + aReplacements.Append(anRepl); + + myGraph.LayerRegistry().Ensure()->RecordBatch( + "BatchGen", + aOriginals.ToArray1(), + aReplacements.ToArray1(), + TCollection_AsciiString(), + BRepGraph_LayerHistory::Kind::Generated); + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), 1); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->Record(size_t(0)).RecordKind, + BRepGraph_LayerHistory::Kind::Generated); + EXPECT_NE(myGraph.LayerRegistry().Ensure()->FindGenerated(anOrig), + nullptr); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->FindModified(anOrig), + nullptr); +} + +TEST_F(BRepGraph_LayerHistoryTest, Record_ExplicitModifiedKind_DefaultMatches) +{ + NCollection_LinearVector aRepls; + aRepls.Append(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 1)); + + const size_t aNbBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + myGraph.LayerRegistry().Ensure()->Record( + "ExplicitMod", + BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 0), + aRepls.ToArray1(), + BRepGraph_LayerHistory::Kind::Modified); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbBefore + 1); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->Record(aNbBefore).RecordKind, + BRepGraph_LayerHistory::Kind::Modified); +} + +TEST_F(BRepGraph_LayerHistoryTest, IsDeleted_UnrelatedNode_ReturnsFalse) +{ + const BRepGraph_NodeId aNode(BRepGraph_NodeId::Kind::Edge, 0); + EXPECT_FALSE(myGraph.LayerRegistry().Ensure()->IsDeleted(aNode)); +} + +TEST_F(BRepGraph_LayerHistoryTest, RecordReplaced_MapsImageAndMarksDeleted) +{ + const BRepGraph_NodeId anOrig(BRepGraph_NodeId::Kind::Face, 0); + const BRepGraph_NodeId aReplacement(BRepGraph_NodeId::Kind::Face, 1); + + myGraph.LayerRegistry().Ensure()->RecordReplaced("ReplaceFace", + anOrig, + aReplacement); + + ASSERT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), size_t(1)); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->Record(size_t(0)).RecordKind, + BRepGraph_LayerHistory::Kind::Replaced); + EXPECT_TRUE(myGraph.LayerRegistry().Ensure()->IsDeleted(anOrig)); + + const NCollection_LinearVector* aModified = + myGraph.LayerRegistry().Ensure()->FindModified(anOrig); + ASSERT_NE(aModified, nullptr); + ASSERT_EQ(aModified->Size(), size_t(1)); + EXPECT_EQ(aModified->Value(0), aReplacement); + + const NCollection_LinearVector* anOrigins = + myGraph.LayerRegistry().Ensure()->FindOriginals(aReplacement); + ASSERT_NE(anOrigins, nullptr); + ASSERT_EQ(anOrigins->Size(), size_t(1)); + EXPECT_EQ(anOrigins->Value(0), anOrig); +} + +TEST_F(BRepGraph_LayerHistoryTest, StructuralReplacement_DoesNotEmitSemanticHistory) +{ + ASSERT_GT(myGraph.Topo().Faces().Nb(), 1); + myGraph.LayerRegistry().Ensure()->Clear(); + + const BRepGraph_FaceId aRemoved(1); + const BRepGraph_FaceId aReplacement(0); + myGraph.Editor().Gen().ReplaceNode(aRemoved, aReplacement); + + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), size_t(0)); + EXPECT_FALSE(myGraph.LayerRegistry().Ensure()->IsDeleted(aRemoved)); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->FindModified(aRemoved), + nullptr); +} + +TEST_F(BRepGraph_LayerHistoryTest, FindOriginals_DerivedWithTwoParents_ReturnsBoth) +{ + const BRepGraph_NodeId anOrigA(BRepGraph_NodeId::Kind::Face, 0); + const BRepGraph_NodeId anOrigB(BRepGraph_NodeId::Kind::Face, 1); + const BRepGraph_NodeId aDerived(BRepGraph_NodeId::Kind::Edge, 0); + + NCollection_LinearVector aFirst; + aFirst.Append(aDerived); + myGraph.LayerRegistry().Ensure()->Record("FirstParent", + anOrigA, + aFirst.ToArray1()); + + NCollection_LinearVector aSecond; + aSecond.Append(aDerived); + myGraph.LayerRegistry().Ensure()->Record("SecondParent", + anOrigB, + aSecond.ToArray1()); + + const NCollection_LinearVector* anOrigins = + myGraph.LayerRegistry().Ensure()->FindOriginals(aDerived); + ASSERT_NE(anOrigins, nullptr); + EXPECT_EQ(anOrigins->Size(), size_t(2)); + + bool aHasA = false; + bool aHasB = false; + for (const BRepGraph_NodeId& anOrigin : *anOrigins) + { + if (anOrigin == anOrigA) + { + aHasA = true; + } + if (anOrigin == anOrigB) + { + aHasB = true; + } + } + EXPECT_TRUE(aHasA); + EXPECT_TRUE(aHasB); +} + +TEST_F(BRepGraph_LayerHistoryTest, RecordUid_Modified_AppendsAuditRecord) +{ + const BRepGraph_UID anOrig(BRepGraph_NodeId::Kind::Face, 10); + const BRepGraph_UID aRepl(BRepGraph_NodeId::Kind::Face, 11); + + NCollection_LinearVector aReplacements; + aReplacements.Append(aRepl); + + BRepGraph_LayerHistory aHist; + aHist.RecordUid("UidMod", + anOrig, + aReplacements.ToArray1(), + BRepGraph_LayerHistory::Kind::Modified); + + ASSERT_EQ(aHist.NbRecords(), size_t(1)); + const BRepGraph_LayerHistory::Event& aRecord = aHist.Record(size_t(0)); + EXPECT_TRUE(aRecord.OperationName.IsEqual("UidMod")); + EXPECT_EQ(aRecord.RecordKind, BRepGraph_LayerHistory::Kind::Modified); + ASSERT_TRUE(aRecord.UidMapping.IsBound(anOrig)); + ASSERT_EQ(aRecord.UidMapping.Find(anOrig).Size(), size_t(1)); + EXPECT_EQ(aRecord.UidMapping.Find(anOrig).Value(0), aRepl); + EXPECT_TRUE(aRecord.Mapping.IsEmpty()); + EXPECT_TRUE(aHist.HasKnownInput(anOrig)); + + const NCollection_LinearVector* aMod = aHist.FindModified(anOrig); + ASSERT_NE(aMod, nullptr); + ASSERT_EQ(aMod->Size(), size_t(1)); + EXPECT_EQ(aMod->Value(0), aRepl); +} + +TEST_F(BRepGraph_LayerHistoryTest, RecordDeletedUid_AppendsAuditRecord) +{ + const BRepGraph_UID anOrigA(BRepGraph_NodeId::Kind::Edge, 20); + const BRepGraph_UID anOrigB(BRepGraph_NodeId::Kind::Edge, 21); + + NCollection_LinearVector aDeleted; + aDeleted.Append(anOrigA); + aDeleted.Append(anOrigB); + + BRepGraph_LayerHistory aHist; + aHist.RecordDeletedUid("UidDelete", aDeleted.ToArray1()); + + ASSERT_EQ(aHist.NbRecords(), size_t(1)); + const BRepGraph_LayerHistory::Event& aRecord = aHist.Record(size_t(0)); + EXPECT_TRUE(aRecord.OperationName.IsEqual("UidDelete")); + EXPECT_EQ(aRecord.RecordKind, BRepGraph_LayerHistory::Kind::Deleted); + ASSERT_TRUE(aRecord.UidMapping.IsBound(anOrigA)); + ASSERT_TRUE(aRecord.UidMapping.IsBound(anOrigB)); + EXPECT_TRUE(aRecord.UidMapping.Find(anOrigA).IsEmpty()); + EXPECT_TRUE(aRecord.UidMapping.Find(anOrigB).IsEmpty()); + EXPECT_TRUE(aHist.IsDeleted(anOrigA)); + EXPECT_TRUE(aHist.IsDeleted(anOrigB)); + EXPECT_TRUE(aHist.HasKnownInput(anOrigA)); + EXPECT_TRUE(aHist.HasKnownInput(anOrigB)); +} + +// ============================================================ +// Absorb (BRepTools_History -> BRepGraph_LayerHistory bridge) tests +// ============================================================ + +namespace +{ +TopoDS_Shape MakeVertexShape(const double theX, const double theY, const double theZ) +{ + BRepBuilderAPI_MakeVertex aMaker(gp_Pnt(theX, theY, theZ)); + return aMaker.Shape(); +} +} // namespace + +TEST_F(BRepGraph_LayerHistoryTest, Absorb_NullSource_NoOp) +{ + BRepGraph_LayerHistory aHist; + NCollection_DataMap anInputs; + NCollection_DataMap anOutputs; + const occ::handle aNullSrc; + aHist.Absorb(anInputs, anOutputs, aNullSrc, "NullSrc"); + EXPECT_EQ(aHist.NbRecords(), size_t(0)); +} + +TEST_F(BRepGraph_LayerHistoryTest, Absorb_EmptyInputs_NoOp) +{ + BRepGraph_LayerHistory aHist; + NCollection_DataMap anInputs; + NCollection_DataMap anOutputs; + const occ::handle aSrc = new BRepTools_History(); + aHist.Absorb(anInputs, anOutputs, aSrc, "EmptyInputs"); + EXPECT_EQ(aHist.NbRecords(), size_t(0)); +} + +TEST_F(BRepGraph_LayerHistoryTest, Absorb_ModifiedOnly_EmitsModifiedRecord) +{ + const TopoDS_Shape anInShape = MakeVertexShape(0, 0, false); + const TopoDS_Shape anOutShape = MakeVertexShape(1, 0, false); + const BRepGraph_NodeId anInNode(BRepGraph_NodeId::Kind::Vertex, 100); + const BRepGraph_NodeId anOutNode(BRepGraph_NodeId::Kind::Vertex, 101); + + occ::handle aSrc = new BRepTools_History(); + aSrc->AddModified(anInShape, anOutShape); + + NCollection_DataMap anInputs; + anInputs.Bind(anInShape, anInNode); + NCollection_DataMap anOutputs; + anOutputs.Bind(anOutShape, anOutNode); + + BRepGraph_LayerHistory aHist; + aHist.Absorb(anInputs, anOutputs, aSrc, "Mod"); + + ASSERT_EQ(aHist.NbRecords(), size_t(1)); + EXPECT_EQ(aHist.Record(size_t(0)).RecordKind, BRepGraph_LayerHistory::Kind::Modified); + + const NCollection_LinearVector* aMod = aHist.FindModified(anInNode); + ASSERT_NE(aMod, nullptr); + ASSERT_EQ(aMod->Size(), size_t(1)); + EXPECT_EQ(aMod->Value(0), anOutNode); + EXPECT_EQ(aHist.FindGenerated(anInNode), nullptr); + EXPECT_FALSE(aHist.IsDeleted(anInNode)); +} + +TEST_F(BRepGraph_LayerHistoryTest, Absorb_GeneratedOnly_EmitsGeneratedRecord) +{ + const TopoDS_Shape anInShape = MakeVertexShape(0, 0, false); + const TopoDS_Shape anOutShape = MakeVertexShape(2, 0, false); + const BRepGraph_NodeId anInNode(BRepGraph_NodeId::Kind::Vertex, 200); + const BRepGraph_NodeId anOutNode(BRepGraph_NodeId::Kind::Vertex, 201); + + occ::handle aSrc = new BRepTools_History(); + aSrc->AddGenerated(anInShape, anOutShape); + + NCollection_DataMap anInputs; + anInputs.Bind(anInShape, anInNode); + NCollection_DataMap anOutputs; + anOutputs.Bind(anOutShape, anOutNode); + + BRepGraph_LayerHistory aHist; + aHist.Absorb(anInputs, anOutputs, aSrc, "Gen"); + + ASSERT_EQ(aHist.NbRecords(), size_t(1)); + EXPECT_EQ(aHist.Record(size_t(0)).RecordKind, BRepGraph_LayerHistory::Kind::Generated); + + const NCollection_LinearVector* aGen = aHist.FindGenerated(anInNode); + ASSERT_NE(aGen, nullptr); + ASSERT_EQ(aGen->Size(), size_t(1)); + EXPECT_EQ(aGen->Value(0), anOutNode); + EXPECT_EQ(aHist.FindModified(anInNode), nullptr); +} + +TEST_F(BRepGraph_LayerHistoryTest, Absorb_RemovedTakesPrecedenceOverModified) +{ + // The OCCT bug being guarded against: a shape can show up as both + // IsRemoved() and in Modified()/Generated(). Absorb must classify it + // as a deletion event regardless. + const TopoDS_Shape anInShape = MakeVertexShape(0, 0, false); + const TopoDS_Shape aGhostOut = MakeVertexShape(3, 0, false); + const BRepGraph_NodeId anInNode(BRepGraph_NodeId::Kind::Vertex, 300); + const BRepGraph_NodeId aGhostNode(BRepGraph_NodeId::Kind::Vertex, 301); + + occ::handle aSrc = new BRepTools_History(); + aSrc->AddModified(anInShape, aGhostOut); + aSrc->Remove(anInShape); + + NCollection_DataMap anInputs; + anInputs.Bind(anInShape, anInNode); + NCollection_DataMap anOutputs; + anOutputs.Bind(aGhostOut, aGhostNode); + + BRepGraph_LayerHistory aHist; + aHist.Absorb(anInputs, anOutputs, aSrc, "RemovedWins"); + + EXPECT_TRUE(aHist.IsDeleted(anInNode)); + EXPECT_EQ(aHist.FindModified(anInNode), nullptr); + ASSERT_EQ(aHist.NbRecords(), size_t(1)); + EXPECT_EQ(aHist.Record(size_t(0)).RecordKind, BRepGraph_LayerHistory::Kind::Deleted); +} + +TEST_F(BRepGraph_LayerHistoryTest, Absorb_OutputMissingFromMap_DroppedSilently) +{ + const TopoDS_Shape anInShape = MakeVertexShape(0, 0, false); + const TopoDS_Shape anOutShape = MakeVertexShape(4, 0, false); + const BRepGraph_NodeId anInNode(BRepGraph_NodeId::Kind::Vertex, 400); + + occ::handle aSrc = new BRepTools_History(); + aSrc->AddModified(anInShape, anOutShape); + + NCollection_DataMap anInputs; + anInputs.Bind(anInShape, anInNode); + // Intentionally do not bind anOutShape in anOutputs. + NCollection_DataMap anOutputs; + + BRepGraph_LayerHistory aHist; + aHist.Absorb(anInputs, anOutputs, aSrc, "Drop"); + + EXPECT_EQ(aHist.NbRecords(), size_t(0)); + EXPECT_EQ(aHist.FindModified(anInNode), nullptr); +} + +TEST_F(BRepGraph_LayerHistoryTest, ClearReleasesHistoryAndAllowsFreshUidRecording) +{ + BRepGraph_LayerHistory aHist; + + for (uint32_t anIdx = 0; anIdx < 1000; ++anIdx) + { + const BRepGraph_UID anOriginal(BRepGraph_NodeId::Kind::Edge, anIdx + 1); + NCollection_LinearVector aReplacements(1); + aReplacements.Append(BRepGraph_UID(BRepGraph_NodeId::Kind::Edge, anIdx + 1001)); + aHist.RecordUid("Stress", anOriginal, aReplacements.ToArray1()); + } + ASSERT_EQ(aHist.NbRecords(), size_t(1000)); + + aHist.Clear(); + ASSERT_EQ(aHist.NbRecords(), size_t(0)); + + const BRepGraph_UID aFreshOriginal(BRepGraph_NodeId::Kind::Face, 20); + const BRepGraph_UID aFreshReplacement(BRepGraph_NodeId::Kind::Face, 21); + NCollection_LinearVector aFreshReplacements(1); + aFreshReplacements.Append(aFreshReplacement); + aHist.RecordUid("AfterClear", aFreshOriginal, aFreshReplacements.ToArray1()); + + ASSERT_EQ(aHist.NbRecords(), size_t(1)); + const NCollection_LinearVector* aModified = aHist.FindModified(aFreshOriginal); + ASSERT_NE(aModified, nullptr); + ASSERT_EQ(aModified->Size(), 1u); + EXPECT_EQ(aModified->Value(0), aFreshReplacement); + EXPECT_FALSE(aHist.IsDeleted(aFreshOriginal)); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_LayerIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_LayerIterator_Test.cxx index c9ec440547..6cbe46464b 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_LayerIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_LayerIterator_Test.cxx @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -38,15 +37,9 @@ public: const TCollection_AsciiString& Name() const override { return myName; } - void OnNodeRemoved(const BRepGraph_NodeId /*theNode*/, - const BRepGraph_NodeId /*theReplacement*/) noexcept override - { - } + void OnNodeRemoved(const BRepGraph_NodeId /*theNode*/) noexcept override {} - void OnCompact(const NCollection_DataMap& /*theRemapMap*/) noexcept override - { - } + void CopyTo(const BRepGraph_CopyRemap&) const override {} void InvalidateAll() noexcept override {} @@ -63,7 +56,8 @@ TEST(BRepGraph_LayerIteratorTest, EmptyRegistry_IsEmpty) BRepGraph_LayerRegistry aRegistry; BRepGraph_LayerIterator anIt(aRegistry); EXPECT_FALSE(anIt.More()); - EXPECT_EQ(anIt.NbLayers(), 0); + EXPECT_EQ(anIt.NbLayers(), 0u); + EXPECT_TRUE(aRegistry.Layer(0).IsNull()); } TEST(BRepGraph_LayerIteratorTest, SingleLayer_IteratesOnce) @@ -75,8 +69,8 @@ TEST(BRepGraph_LayerIteratorTest, SingleLayer_IteratesOnce) BRepGraph_LayerIterator anIt(aRegistry); ASSERT_TRUE(anIt.More()); - EXPECT_EQ(anIt.NbLayers(), 1); - EXPECT_EQ(anIt.Slot(), 0); + EXPECT_EQ(anIt.NbLayers(), 1u); + EXPECT_EQ(anIt.Slot(), 0u); EXPECT_FALSE(anIt.Value().IsNull()); EXPECT_EQ(anIt.Value()->Name(), TCollection_AsciiString("TestA")); @@ -97,14 +91,14 @@ TEST(BRepGraph_LayerIteratorTest, MultipleLayers_IteratesAll) aRegistry.RegisterLayer(aLayerB); aRegistry.RegisterLayer(aLayerC); - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_LayerIterator anIt(aRegistry); anIt.More(); anIt.Next()) { EXPECT_FALSE(anIt.Value().IsNull()); EXPECT_EQ(anIt.Slot(), aCount); ++aCount; } - EXPECT_EQ(aCount, 3); + EXPECT_EQ(aCount, 3u); } TEST(BRepGraph_LayerIteratorTest, RangeFor_WorksCorrectly) @@ -117,11 +111,11 @@ TEST(BRepGraph_LayerIteratorTest, RangeFor_WorksCorrectly) aRegistry.RegisterLayer(aLayerA); aRegistry.RegisterLayer(aLayerB); - int aCount = 0; + uint32_t aCount = 0; for (const occ::handle& aLayer : BRepGraph_LayerIterator(aRegistry)) { EXPECT_FALSE(aLayer.IsNull()); ++aCount; } - EXPECT_EQ(aCount, 2); + EXPECT_EQ(aCount, 2u); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_LayerTopoSupplement_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_LayerTopoSupplement_Test.cxx new file mode 100644 index 0000000000..29d9b1d723 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_LayerTopoSupplement_Test.cxx @@ -0,0 +1,1010 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +TopoDS_Vertex makeVertexShape(const gp_Pnt& thePoint) +{ + BRep_Builder aBuilder; + TopoDS_Vertex aVertex; + aBuilder.MakeVertex(aVertex, thePoint, 1.0e-7); + return aVertex; +} + +TopoDS_Edge makeEdgeWithSupplementVertices() +{ + BRep_Builder aBuilder; + + const TopoDS_Vertex aStart = makeVertexShape(gp_Pnt(0.0, 0.0, 0.0)); + const TopoDS_Vertex anExtraFwd = makeVertexShape(gp_Pnt(2.0, 0.0, 0.0)); + const TopoDS_Vertex anInternal = makeVertexShape(gp_Pnt(5.0, 0.0, 0.0)); + const TopoDS_Vertex anExternal = makeVertexShape(gp_Pnt(7.0, 0.0, 0.0)); + const TopoDS_Vertex anEnd = makeVertexShape(gp_Pnt(10.0, 0.0, 0.0)); + + TopoDS_Edge anEdge; + aBuilder.MakeEdge(anEdge); + + TopoDS_Vertex aForwardExtra = anExtraFwd; + aForwardExtra.Orientation(TopAbs_FORWARD); + aBuilder.Add(anEdge, aForwardExtra); + + TopoDS_Vertex aForwardStart = aStart; + aForwardStart.Orientation(TopAbs_FORWARD); + aBuilder.Add(anEdge, aForwardStart); + + TopoDS_Vertex anInternalUse = anInternal; + anInternalUse.Orientation(TopAbs_INTERNAL); + aBuilder.Add(anEdge, anInternalUse); + + TopoDS_Vertex anExternalUse = anExternal; + anExternalUse.Orientation(TopAbs_EXTERNAL); + aBuilder.Add(anEdge, anExternalUse); + + TopoDS_Vertex aReversedEnd = anEnd; + aReversedEnd.Orientation(TopAbs_REVERSED); + aBuilder.Add(anEdge, aReversedEnd); + return anEdge; +} + +TopoDS_Face makeFaceWithSupplementVertex() +{ + BRep_Builder aBuilder; + + TopoDS_Vertex aV0 = makeVertexShape(gp_Pnt(0.0, 0.0, 0.0)); + TopoDS_Vertex aV1 = makeVertexShape(gp_Pnt(10.0, 0.0, 0.0)); + TopoDS_Vertex aV2 = makeVertexShape(gp_Pnt(10.0, 10.0, 0.0)); + TopoDS_Vertex aV3 = makeVertexShape(gp_Pnt(0.0, 10.0, 0.0)); + + TopoDS_Edge aE0; + TopoDS_Edge aE1; + TopoDS_Edge aE2; + TopoDS_Edge aE3; + aBuilder.MakeEdge(aE0); + aBuilder.MakeEdge(aE1); + aBuilder.MakeEdge(aE2); + aBuilder.MakeEdge(aE3); + aBuilder.Add(aE0, aV0.Oriented(TopAbs_FORWARD)); + aBuilder.Add(aE0, aV1.Oriented(TopAbs_REVERSED)); + aBuilder.Add(aE1, aV1.Oriented(TopAbs_FORWARD)); + aBuilder.Add(aE1, aV2.Oriented(TopAbs_REVERSED)); + aBuilder.Add(aE2, aV2.Oriented(TopAbs_FORWARD)); + aBuilder.Add(aE2, aV3.Oriented(TopAbs_REVERSED)); + aBuilder.Add(aE3, aV3.Oriented(TopAbs_FORWARD)); + aBuilder.Add(aE3, aV0.Oriented(TopAbs_REVERSED)); + + TopoDS_Wire aWire; + aBuilder.MakeWire(aWire); + aBuilder.Add(aWire, aE0); + aBuilder.Add(aWire, aE1); + aBuilder.Add(aWire, aE2); + aBuilder.Add(aWire, aE3); + aWire.Closed(true); + + TopoDS_Face aFace; + aBuilder.MakeFace(aFace); + aBuilder.Add(aFace, aWire); + + TopoDS_Vertex aLooseVertex = makeVertexShape(gp_Pnt(5.0, 5.0, 0.0)); + aBuilder.Add(aFace, aLooseVertex.Oriented(TopAbs_INTERNAL)); + return aFace; +} + +TopoDS_Face makePlainFace() +{ + BRep_Builder aBuilder; + + TopoDS_Vertex aV0 = makeVertexShape(gp_Pnt(0.0, 0.0, 0.0)); + TopoDS_Vertex aV1 = makeVertexShape(gp_Pnt(10.0, 0.0, 0.0)); + TopoDS_Vertex aV2 = makeVertexShape(gp_Pnt(10.0, 10.0, 0.0)); + TopoDS_Vertex aV3 = makeVertexShape(gp_Pnt(0.0, 10.0, 0.0)); + + TopoDS_Edge aE0; + TopoDS_Edge aE1; + TopoDS_Edge aE2; + TopoDS_Edge aE3; + aBuilder.MakeEdge(aE0); + aBuilder.MakeEdge(aE1); + aBuilder.MakeEdge(aE2); + aBuilder.MakeEdge(aE3); + aBuilder.Add(aE0, aV0.Oriented(TopAbs_FORWARD)); + aBuilder.Add(aE0, aV1.Oriented(TopAbs_REVERSED)); + aBuilder.Add(aE1, aV1.Oriented(TopAbs_FORWARD)); + aBuilder.Add(aE1, aV2.Oriented(TopAbs_REVERSED)); + aBuilder.Add(aE2, aV2.Oriented(TopAbs_FORWARD)); + aBuilder.Add(aE2, aV3.Oriented(TopAbs_REVERSED)); + aBuilder.Add(aE3, aV3.Oriented(TopAbs_FORWARD)); + aBuilder.Add(aE3, aV0.Oriented(TopAbs_REVERSED)); + + TopoDS_Wire aWire; + aBuilder.MakeWire(aWire); + aBuilder.Add(aWire, aE0); + aBuilder.Add(aWire, aE1); + aBuilder.Add(aWire, aE2); + aBuilder.Add(aWire, aE3); + aWire.Closed(true); + + TopoDS_Face aFace; + aBuilder.MakeFace(aFace); + aBuilder.Add(aFace, aWire); + return aFace; +} + +TopoDS_Shell makePlainShell() +{ + BRep_Builder aBuilder; + TopoDS_Shell aShell; + aBuilder.MakeShell(aShell); + aBuilder.Add(aShell, makePlainFace()); + return aShell; +} + +TopoDS_Solid makePlainSolid() +{ + BRep_Builder aBuilder; + TopoDS_Solid aSolid; + aBuilder.MakeSolid(aSolid); + aBuilder.Add(aSolid, makePlainShell()); + return aSolid; +} + +BRepGraph_CompSolidId addEmptyCompSolid(BRepGraph& theGraph) +{ + NCollection_LinearVector anEmptySolids; + return theGraph.Editor().CompSolids().Add(anEmptySolids.ToArray1()); +} + +TopoDS_CompSolid makePlainCompSolid() +{ + BRep_Builder aBuilder; + TopoDS_CompSolid aCompSolid; + aBuilder.MakeCompSolid(aCompSolid); + aBuilder.Add(aCompSolid, makePlainSolid()); + return aCompSolid; +} + +int countDirectChildren(const TopoDS_Shape& theShape, + const TopAbs_ShapeEnum theType, + const TopAbs_Orientation theOrientation) +{ + int aCount = 0; + for (TopoDS_Iterator aChildIt(theShape, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + if (aChild.ShapeType() == theType && aChild.Orientation() == theOrientation) + { + ++aCount; + } + } + return aCount; +} +} // namespace + +TEST(BRepGraph_LayerTopoSupplementTest, AttachAndRemoveVertexSupplement) +{ + BRepGraph aGraph; + BRepGraph_VertexId aOwner = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + + BRepGraph_SupplementEditor anEditor(aGraph); + const uint64_t anAttachment = + anEditor.AttachToVertex(aOwner, makeVertexShape(gp_Pnt(1.0, 2.0, 3.0))); + ASSERT_NE(anAttachment, uint64_t(0)); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(aOwner)); + ASSERT_EQ(anAttached.Size(), 1); + EXPECT_EQ(anAttached.First(), anAttachment); + + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttachment); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->BaseOwner, BRepGraph_NodeId(aOwner)); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::VertexSupplementShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_VERTEX); + + EXPECT_TRUE(anEditor.RemoveAttachment(anAttachment)); + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aOwner)).Size(), 0); + EXPECT_EQ(aLayer->FindByUid(anAttachment), nullptr); +} + +TEST(BRepGraph_LayerTopoSupplementTest, SupplementIteratorPreservesOwnerOrderAndUidLookup) +{ + BRepGraph aGraph; + BRepGraph_VertexId aOwner = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + + BRepGraph_SupplementEditor anEditor(aGraph); + const uint64_t aFirst = anEditor.AttachToVertex(aOwner, makeVertexShape(gp_Pnt(1.0, 0.0, 0.0))); + const uint64_t aSecond = anEditor.AttachToVertex(aOwner, makeVertexShape(gp_Pnt(2.0, 0.0, 0.0))); + ASSERT_NE(aFirst, uint64_t(0)); + ASSERT_NE(aSecond, uint64_t(0)); + + NCollection_LinearVector aSeen; + for (BRepGraph_SupplementIterator anIt(aGraph, BRepGraph_NodeId(aOwner)); anIt.More(); + anIt.Next()) + { + aSeen.Append(anIt.Uid()); + EXPECT_EQ(anIt.Value().BaseOwner, BRepGraph_NodeId(aOwner)); + EXPECT_EQ(anIt.Value().Shape.ShapeType(), TopAbs_VERTEX); + } + + ASSERT_EQ(aSeen.Size(), 2); + EXPECT_EQ(aSeen.Value(0), aFirst); + EXPECT_EQ(aSeen.Value(1), aSecond); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + EXPECT_NE(aLayer->FindByUid(aFirst), nullptr); + EXPECT_NE(aLayer->FindByUid(aSecond), nullptr); +} + +TEST(BRepGraph_LayerTopoSupplementTest, OnNodeReplacedMigratesAttachments) +{ + BRepGraph aGraph; + BRepGraph_VertexId anOldOwner = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + BRepGraph_VertexId aNewOwner = aGraph.Editor().Vertices().Add(gp_Pnt(5.0, 0.0, 0.0), 1.0e-7); + + BRepGraph_SupplementEditor anEditor(aGraph); + const uint64_t anAttachment = + anEditor.AttachToVertex(anOldOwner, makeVertexShape(gp_Pnt(0.0, 1.0, 0.0))); + ASSERT_NE(anAttachment, uint64_t(0)); + + occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + aLayer->OnNodeReplaced(BRepGraph_NodeId(anOldOwner), BRepGraph_NodeId(aNewOwner)); + + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(anOldOwner)).Size(), 0); + const NCollection_LinearVector& anAttachedNew = + aLayer->AttachedTo(BRepGraph_NodeId(aNewOwner)); + ASSERT_EQ(anAttachedNew.Size(), 1); + EXPECT_EQ(anAttachedNew.First(), anAttachment); + + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttachment); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->BaseOwner, BRepGraph_NodeId(aNewOwner)); +} + +TEST(BRepGraph_LayerTopoSupplementTest, RemoveNodeDropsOwnedAttachments) +{ + BRepGraph aGraph; + BRepGraph_VertexId aOwner = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + + BRepGraph_SupplementEditor anEditor(aGraph); + const uint64_t anAttachment = + anEditor.AttachToVertex(aOwner, makeVertexShape(gp_Pnt(0.0, 1.0, 0.0))); + ASSERT_NE(anAttachment, uint64_t(0)); + + occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + ASSERT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aOwner)).Size(), 1); + + aGraph.Editor().Gen().RemoveNode(aOwner); + + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aOwner)).Size(), 0); + EXPECT_EQ(aLayer->FindByUid(anAttachment), nullptr); +} + +TEST(BRepGraph_LayerTopoSupplementTest, LegacyEdgeInternalVertexWriterUsesSupplementLayer) +{ + BRepGraph aGraph; + BRepGraph_VertexId aStart = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + BRepGraph_VertexId anEnd = aGraph.Editor().Vertices().Add(gp_Pnt(10.0, 0.0, 0.0), 1.0e-7); + BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aStart, anEnd, occ::handle(), 0.0, 1.0, 1.0e-7); + ASSERT_TRUE(anEdge.IsValid()); + + // Edges().AddInternalVertex() removed; use Shapes().Add(vertexShape, edgeNode) instead. + // const BRepGraph_VertexRefId aLegacyRef = + // aGraph.Editor().Edges().AddInternalVertex(anEdge, anInternal, TopAbs_INTERNAL); + // EXPECT_FALSE(aLegacyRef.IsValid()); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + // No internal vertex added -> supplement layer may not be created. + if (!aLayer.IsNull()) + { + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(anEdge)); + EXPECT_EQ(anAttached.Size(), 0); + } + + int aNbCoreVertexRefs = 0; + for (BRepGraph_RefsVertexOfEdge aRefIt(aGraph, anEdge); aRefIt.More(); aRefIt.Next()) + { + ++aNbCoreVertexRefs; + } + // Only start and end vertices (no internal vertex added). + EXPECT_EQ(aNbCoreVertexRefs, 2); +} + +TEST(BRepGraph_LayerTopoSupplementTest, AddAndReconstructPreservesSupplementEdgeVertices) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + const occ::handle aRegisteredLayer = + aGraph.LayerRegistry().Ensure(); + ASSERT_FALSE(aRegisteredLayer.IsNull()); + + const TopoDS_Edge anInputEdge = makeEdgeWithSupplementVertices(); + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(anInputEdge, anOptions); + ASSERT_TRUE(aResult.IsOk()); + ASSERT_EQ(aResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Edge); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& anAttached = aLayer->AttachedTo(aResult.TopologyRoot); + ASSERT_EQ(anAttached.Size(), 3); + + int aNbInternal = 0; + int aNbExternal = 0; + int aNbForward = 0; + for (const uint64_t aUid : anAttached) + { + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(aUid); + ASSERT_NE(anEntry, nullptr); + ASSERT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::EdgeInternalVertex); + if (anEntry->Shape.Orientation() == TopAbs_INTERNAL) + { + ++aNbInternal; + } + else if (anEntry->Shape.Orientation() == TopAbs_EXTERNAL) + { + ++aNbExternal; + } + else if (anEntry->Shape.Orientation() == TopAbs_FORWARD) + { + ++aNbForward; + } + } + EXPECT_EQ(aNbInternal, 1); + EXPECT_EQ(aNbExternal, 1); + EXPECT_EQ(aNbForward, 1); + + const TopoDS_Shape aRoundTrip = aGraph.Shapes().Reconstruct(aResult.TopologyRoot); + ASSERT_EQ(aRoundTrip.ShapeType(), TopAbs_EDGE); + + int aNbRoundTripForward = 0; + int aNbRoundTripReversed = 0; + int aNbRoundTripInternal = 0; + int aNbRoundTripExternal = 0; + for (TopoDS_Iterator aChildIt(aRoundTrip, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + ASSERT_EQ(aChild.ShapeType(), TopAbs_VERTEX); + switch (aChild.Orientation()) + { + case TopAbs_FORWARD: + ++aNbRoundTripForward; + break; + case TopAbs_REVERSED: + ++aNbRoundTripReversed; + break; + case TopAbs_INTERNAL: + ++aNbRoundTripInternal; + break; + case TopAbs_EXTERNAL: + ++aNbRoundTripExternal; + break; + default: + break; + } + } + + EXPECT_EQ(aNbRoundTripForward, 2); + EXPECT_EQ(aNbRoundTripReversed, 1); + EXPECT_EQ(aNbRoundTripInternal, 1); + EXPECT_EQ(aNbRoundTripExternal, 1); +} + +TEST(BRepGraph_LayerTopoSupplementTest, AddAndReconstructPreservesSupplementFaceVertex) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + const occ::handle aRegisteredLayer = + aGraph.LayerRegistry().Ensure(); + ASSERT_FALSE(aRegisteredLayer.IsNull()); + + const TopoDS_Face aInputFace = makeFaceWithSupplementVertex(); + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aInputFace, anOptions); + ASSERT_TRUE(aResult.IsOk()); + ASSERT_EQ(aResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Face); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& anAttached = aLayer->AttachedTo(aResult.TopologyRoot); + ASSERT_EQ(anAttached.Size(), 1); + + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::FaceDirectVertex); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_VERTEX); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRoundTrip = aGraph.Shapes().Reconstruct(aResult.TopologyRoot); + ASSERT_EQ(aRoundTrip.ShapeType(), TopAbs_FACE); + + int aNbWires = 0; + int aNbInternalVertices = 0; + for (TopoDS_Iterator aChildIt(aRoundTrip, false, false); aChildIt.More(); aChildIt.Next()) + { + const TopoDS_Shape& aChild = aChildIt.Value(); + if (aChild.ShapeType() == TopAbs_WIRE) + { + ++aNbWires; + continue; + } + + if (aChild.ShapeType() == TopAbs_VERTEX && aChild.Orientation() == TopAbs_INTERNAL) + { + ++aNbInternalVertices; + } + } + + EXPECT_EQ(aNbWires, 1); + EXPECT_EQ(aNbInternalVertices, 1); +} + +TEST(BRepGraph_LayerTopoSupplementTest, ShellReconstructionReplaysFaceSupplementVertex) +{ + BRep_Builder aBuilder; + TopoDS_Shell aShell; + aBuilder.MakeShell(aShell); + aBuilder.Add(aShell, makeFaceWithSupplementVertex()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const occ::handle aRegisteredLayer = + aGraph.LayerRegistry().Ensure(); + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aShell, anOptions); + ASSERT_TRUE(aResult.TopologyRoot.IsValid()); + + const TopoDS_Shape aRoundTrip = aGraph.Shapes().Reconstruct(aResult.TopologyRoot); + ASSERT_FALSE(aRoundTrip.IsNull()); + ASSERT_EQ(aRoundTrip.ShapeType(), TopAbs_SHELL); + + int aNbFaces = 0; + int aNbInternalVertices = 0; + for (TopoDS_Iterator aFaceIt(aRoundTrip, false, false); aFaceIt.More(); aFaceIt.Next()) + { + const TopoDS_Shape& aFace = aFaceIt.Value(); + if (aFace.ShapeType() != TopAbs_FACE) + { + continue; + } + ++aNbFaces; + aNbInternalVertices += countDirectChildren(aFace, TopAbs_VERTEX, TopAbs_INTERNAL); + } + + EXPECT_EQ(aNbFaces, 1); + EXPECT_EQ(aNbInternalVertices, 1); +} + +TEST(BRepGraph_LayerTopoSupplementTest, SolidAddChild_ReconstructPreservesSupplementEdge) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + const occ::handle aRegisteredLayer = + aGraph.LayerRegistry().Ensure(); + ASSERT_FALSE(aRegisteredLayer.IsNull()); + + const TopoDS_Solid aInputSolid = makePlainSolid(); + const BRepGraph::ShapesView::Result aResult = aGraph.Shapes().Add(aInputSolid, anOptions); + ASSERT_TRUE(aResult.IsOk()); + ASSERT_EQ(aResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Solid); + + ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); + const BRepGraph_EdgeId aEdgeId = BRepGraph_EdgeId::Start(); + ASSERT_TRUE(aEdgeId.IsValid()); + + // Solids().AddChild() removed; use Shapes().Add(childShape, solidNode) instead. + // Since the API call is commented out, no supplement attachment is created. + // EXPECT_FALSE(aGraph.Editor().Solids().AddChild(BRepGraph_SolidId(aResult.TopologyRoot), + // aEdgeId, + // TopAbs_FORWARD) + // .IsValid()); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& anAttached = aLayer->AttachedTo(aResult.TopologyRoot); + // No child added -> no supplement attachments. + ASSERT_EQ(anAttached.Size(), 0); + + // Since no supplement attachment was created, the rest of the test is skipped. + // The reconstruction test is covered by AddAndReconstructPreservesSupplementEdgeVertices. +} + +TEST(BRepGraph_LayerTopoSupplementTest, ShellOwner_IsAcceptedBySupplementLayer) +{ + BRepGraph aGraph; + BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + + const occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + EXPECT_NE(aLayer->AddAttachment(BRepGraph_NodeId(aShell), + BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape, + makeVertexShape(gp_Pnt(1.0, 2.0, 3.0))), + uint64_t(0)); + + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aShell)).Size(), 1); +} + +TEST(BRepGraph_LayerTopoSupplementTest, CompSolidOwner_IsAcceptedBySupplementLayer) +{ + BRepGraph aGraph; + BRepGraph_CompSolidId aCompSolid = addEmptyCompSolid(aGraph); + ASSERT_TRUE(aCompSolid.IsValid()); + + const occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + EXPECT_NE(aLayer->AddAttachment(BRepGraph_NodeId(aCompSolid), + BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape, + makePlainSolid()), + uint64_t(0)); + EXPECT_NE( + aLayer->AddAttachment(BRepGraph_NodeId(aCompSolid), + BRepGraph_LayerTopoSupplement::AttachmentKind::GenericSupplementShape, + makeVertexShape(gp_Pnt(1.0, 2.0, 3.0))), + uint64_t(0)); + + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aCompSolid)).Size(), 2); +} + +TEST(BRepGraph_LayerTopoSupplementTest, ShellAddFace_Internal_RoutedToSupplement) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + + const TopoDS_Shell aInputShell = makePlainShell(); + const BRepGraph::ShapesView::Result aShellResult = aGraph.Shapes().Add(aInputShell, anOptions); + ASSERT_TRUE(aShellResult.IsOk()); + ASSERT_EQ(aShellResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Shell); + + const BRepGraph_ShellId aShellId(aShellResult.TopologyRoot); + const uint32_t aNbFacesBefore = aGraph.Topo().Faces().Nb(); + TopoDS_Face aInternalFace = makePlainFace(); + aInternalFace.Orientation(TopAbs_INTERNAL); + + const BRepGraph::ShapesView::Result aAddResult = + aGraph.Shapes().Add(aInternalFace, BRepGraph_NodeId(aShellId)); + ASSERT_TRUE(aAddResult.IsOk()); + EXPECT_FALSE(aAddResult.TopologyRoot.IsValid()); + EXPECT_FALSE(aAddResult.InsertedRef.IsValid()); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), aNbFacesBefore); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(aShellId)); + ASSERT_GE(anAttached.Size(), 1); + + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttached.Last()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_FACE); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRoundTrip = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(aShellId)); + EXPECT_EQ(countDirectChildren(aRoundTrip, TopAbs_FACE, TopAbs_INTERNAL), 1); +} + +TEST(BRepGraph_LayerTopoSupplementTest, SolidAddShell_Internal_RoutedToSupplement) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + + const BRepGraph::ShapesView::Result aSolidResult = + aGraph.Shapes().Add(makePlainSolid(), anOptions); + ASSERT_TRUE(aSolidResult.IsOk()); + ASSERT_EQ(aSolidResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Solid); + + const BRepGraph_SolidId aSolidId(aSolidResult.TopologyRoot); + const uint32_t aNbShellsBefore = aGraph.Topo().Shells().Nb(); + TopoDS_Shell anInternalShell = makePlainShell(); + anInternalShell.Orientation(TopAbs_INTERNAL); + + const BRepGraph::ShapesView::Result aAddResult = + aGraph.Shapes().Add(anInternalShell, BRepGraph_NodeId(aSolidId)); + ASSERT_TRUE(aAddResult.IsOk()); + EXPECT_FALSE(aAddResult.TopologyRoot.IsValid()); + EXPECT_FALSE(aAddResult.InsertedRef.IsValid()); + EXPECT_EQ(aGraph.Topo().Shells().Nb(), aNbShellsBefore); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(aSolidId)); + ASSERT_GE(anAttached.Size(), 1); + + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttached.Last()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_SHELL); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRoundTrip = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(aSolidId)); + EXPECT_EQ(countDirectChildren(aRoundTrip, TopAbs_SHELL, TopAbs_INTERNAL), 1); +} + +TEST(BRepGraph_LayerTopoSupplementTest, CompSolidAddSolid_Internal_RoutedToSupplement) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + + const BRepGraph::ShapesView::Result aCompSolidResult = + aGraph.Shapes().Add(makePlainCompSolid(), anOptions); + ASSERT_TRUE(aCompSolidResult.IsOk()); + ASSERT_EQ(aCompSolidResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::CompSolid); + + const BRepGraph_CompSolidId aCompSolidId(aCompSolidResult.TopologyRoot); + const uint32_t aNbSolidsBefore = aGraph.Topo().Solids().Nb(); + TopoDS_Solid anInternalSolid = makePlainSolid(); + anInternalSolid.Orientation(TopAbs_INTERNAL); + + const BRepGraph::ShapesView::Result aAddResult = + aGraph.Shapes().Add(anInternalSolid, BRepGraph_NodeId(aCompSolidId)); + ASSERT_TRUE(aAddResult.IsOk()); + EXPECT_FALSE(aAddResult.TopologyRoot.IsValid()); + EXPECT_FALSE(aAddResult.InsertedRef.IsValid()); + EXPECT_EQ(aGraph.Topo().Solids().Nb(), aNbSolidsBefore); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(aCompSolidId)); + ASSERT_EQ(anAttached.Size(), 1); + + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_SOLID); + EXPECT_EQ(anEntry->Shape.Orientation(), TopAbs_INTERNAL); + + const TopoDS_Shape aRoundTrip = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(aCompSolidId)); + EXPECT_EQ(countDirectChildren(aRoundTrip, TopAbs_SOLID, TopAbs_INTERNAL), 1); +} + +TEST(BRepGraph_LayerTopoSupplementTest, CompoundAddChild_MixedOrientations_CoreRefAndSupplement) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + + BRep_Builder aBB; + TopoDS_Compound aCompound; + aBB.MakeCompound(aCompound); + const BRepGraph::ShapesView::Result aCompResult = aGraph.Shapes().Add(aCompound, anOptions); + ASSERT_TRUE(aCompResult.IsOk()); + const BRepGraph_CompoundId aCompoundId(aCompResult.TopologyRoot); + + TopoDS_Face aForwardFace = makePlainFace(); + aForwardFace.Orientation(TopAbs_FORWARD); + const BRepGraph::ShapesView::Result aFwdResult = + aGraph.Shapes().Add(aForwardFace, BRepGraph_NodeId(aCompoundId)); + ASSERT_TRUE(aFwdResult.IsOk()); + EXPECT_TRUE(aFwdResult.InsertedRef.IsValid()); + + TopoDS_Face aInternalFace = makePlainFace(); + aInternalFace.Orientation(TopAbs_INTERNAL); + const BRepGraph::ShapesView::Result aIntResult = + aGraph.Shapes().Add(aInternalFace, BRepGraph_NodeId(aCompoundId)); + ASSERT_TRUE(aIntResult.IsOk()); + EXPECT_FALSE(aIntResult.InsertedRef.IsValid()); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(aCompoundId)); + ASSERT_EQ(anAttached.Size(), 1); + + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::CompoundAuxShape); +} + +TEST(BRepGraph_LayerTopoSupplementTest, CopyToAllocatesFreshUidOnCollision) +{ + BRepGraph aSource; + BRepGraph aTarget; + + const BRepGraph_VertexId aSourceVertex = + aSource.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aTargetVertex = + aTarget.Editor().Vertices().Add(gp_Pnt(10.0, 0.0, 0.0), 1.0e-7); + const uint32_t aTargetVertexCount = aTarget.Topo().Vertices().Nb(); + + occ::handle aSourceLayer = + aSource.LayerRegistry().Ensure(); + occ::handle aTargetLayer = + aTarget.LayerRegistry().Ensure(); + + ASSERT_TRUE(aSourceLayer->AddAttachmentWithUid( + BRepGraph_NodeId(aSourceVertex), + 1, + BRepGraph_LayerTopoSupplement::AttachmentKind::VertexSupplementShape, + makeVertexShape(gp_Pnt(1.0, 0.0, 0.0)))); + ASSERT_TRUE(aTargetLayer->AddAttachmentWithUid( + BRepGraph_NodeId(aTargetVertex), + 1, + BRepGraph_LayerTopoSupplement::AttachmentKind::VertexSupplementShape, + makeVertexShape(gp_Pnt(11.0, 0.0, 0.0)))); + + ASSERT_TRUE(BRepGraph_Copy::Perform(aSource, aTarget, BRepGraph_Copy::GeomPolicy::Copy)); + + aTargetLayer = aTarget.LayerRegistry().FindLayer(); + ASSERT_FALSE(aTargetLayer.IsNull()); + const BRepGraph_VertexId aCopiedVertex(aTargetVertexCount); + const NCollection_LinearVector& aCopiedAttachments = + aTargetLayer->AttachedTo(BRepGraph_NodeId(aCopiedVertex)); + ASSERT_EQ(aCopiedAttachments.Size(), 1); + EXPECT_NE(aCopiedAttachments.First(), uint64_t(1)); + EXPECT_NE(aTargetLayer->FindByUid(aCopiedAttachments.First()), nullptr); +} + +TEST(BRepGraph_LayerTopoSupplementTest, SelfCopyDoesNotMutateEntriesDuringIteration) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aSourceVertex = + aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + + occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + ASSERT_TRUE(aLayer->AddAttachmentWithUid( + BRepGraph_NodeId(aSourceVertex), + 1, + BRepGraph_LayerTopoSupplement::AttachmentKind::VertexSupplementShape, + makeVertexShape(gp_Pnt(1.0, 0.0, 0.0)))); + + const BRepGraph_NodeId aCopiedNode = + BRepGraph_Copy::CopyNode(aGraph, aGraph, BRepGraph_NodeId(aSourceVertex)); + ASSERT_TRUE(aCopiedNode.IsValid()); + ASSERT_EQ(aCopiedNode.NodeKind, BRepGraph_NodeId::Kind::Vertex); + ASSERT_NE(aCopiedNode.Index, aSourceVertex.Index); + + aLayer = aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + + const NCollection_LinearVector& aSourceAttachments = + aLayer->AttachedTo(BRepGraph_NodeId(aSourceVertex)); + ASSERT_EQ(aSourceAttachments.Size(), 1); + EXPECT_EQ(aSourceAttachments.First(), uint64_t(1)); + + const NCollection_LinearVector& aCopiedAttachments = aLayer->AttachedTo(aCopiedNode); + ASSERT_EQ(aCopiedAttachments.Size(), 1); + EXPECT_NE(aCopiedAttachments.First(), uint64_t(1)); + EXPECT_NE(aLayer->FindByUid(aCopiedAttachments.First()), nullptr); +} + +TEST(BRepGraph_LayerTopoSupplementTest, IncompatibleAttachmentKindIsRejected) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.0e-7); + const BRepGraph::ShapesView::Result aFaceResult = aGraph.Shapes().Add(makePlainFace()); + const BRepGraph_FaceId aFace(aFaceResult.TopologyRoot); + ASSERT_TRUE(anEdge.IsValid()); + ASSERT_TRUE(aFaceResult.IsOk()); + ASSERT_TRUE(aFace.IsValid()); + + const occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + + EXPECT_EQ(aLayer->AddAttachment(BRepGraph_NodeId(anEdge), + BRepGraph_LayerTopoSupplement::AttachmentKind::FaceDirectVertex, + makeVertexShape(gp_Pnt(1.0, 2.0, 3.0))), + uint64_t(0)); + EXPECT_EQ(aLayer->AddAttachment(BRepGraph_NodeId(aFace), + BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape, + makeVertexShape(gp_Pnt(4.0, 5.0, 6.0))), + uint64_t(0)); + const BRepGraph_CompSolidId aCompSolid = addEmptyCompSolid(aGraph); + ASSERT_TRUE(aCompSolid.IsValid()); + EXPECT_EQ(aLayer->AddAttachment(BRepGraph_NodeId(aCompSolid), + BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape, + makePlainShell()), + uint64_t(0)); + EXPECT_EQ(aLayer->AddAttachment(BRepGraph_NodeId(aFace), + BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape, + makePlainSolid()), + uint64_t(0)); + + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(anEdge)).Size(), 0); + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aFace)).Size(), 0); + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aCompSolid)).Size(), 0); +} + +TEST(BRepGraph_LayerTopoSupplementTest, ShapesView_AddToShellRoutesInvalidChildWithoutOrphanAppend) +{ + BRepGraph aGraph; + BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + + const uint32_t aNbEdgesBefore = aGraph.Topo().Edges().Nb(); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + + TopoDS_Edge aEdge; + BRep_Builder aBB; + aBB.MakeEdge(aEdge); + + const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().Add(aEdge, BRepGraph_NodeId(aShell)); + EXPECT_TRUE(aResult.IsOk()); + EXPECT_FALSE(aResult.TopologyRoot.IsValid()); + EXPECT_FALSE(aResult.InsertedRef.IsValid()); + + EXPECT_EQ(aGraph.Topo().Edges().Nb(), aNbEdgesBefore); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), aNbVerticesBefore); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(aShell)); + ASSERT_EQ(anAttached.Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::ShellAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_EDGE); +} + +TEST(BRepGraph_LayerTopoSupplementTest, ShapesView_AddToSolidRoutesInvalidChildWithoutOrphanAppend) +{ + BRepGraph aGraph; + BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid.IsValid()); + + const uint32_t aNbFacesBefore = aGraph.Topo().Faces().Nb(); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + + TopoDS_Face aFace = makePlainFace(); + + const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().Add(aFace, BRepGraph_NodeId(aSolid)); + EXPECT_TRUE(aResult.IsOk()); + EXPECT_FALSE(aResult.TopologyRoot.IsValid()); + EXPECT_FALSE(aResult.InsertedRef.IsValid()); + + EXPECT_EQ(aGraph.Topo().Faces().Nb(), aNbFacesBefore); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), aNbVerticesBefore); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(aSolid)); + ASSERT_EQ(anAttached.Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::SolidAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_FACE); +} + +TEST(BRepGraph_LayerTopoSupplementTest, + ShapesView_AddToFaceRejectsUnsupportedChildWithoutOrphanAppend) +{ + BRepGraph aGraph; + const BRepGraph::ShapesView::Result aFaceResult = aGraph.Shapes().Add(makePlainFace()); + ASSERT_TRUE(aFaceResult.IsOk()); + ASSERT_TRUE(aFaceResult.TopologyRoot.IsValid()); + ASSERT_EQ(aFaceResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::Face); + + const uint32_t aNbEdgesBefore = aGraph.Topo().Edges().Nb(); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + + TopoDS_Edge aEdge; + BRep_Builder aBB; + aBB.MakeEdge(aEdge); + + const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().Add(aEdge, aFaceResult.TopologyRoot); + EXPECT_FALSE(aResult.IsOk()); + EXPECT_FALSE(aResult.TopologyRoot.IsValid()); + EXPECT_FALSE(aResult.InsertedRef.IsValid()); + + EXPECT_EQ(aGraph.Topo().Edges().Nb(), aNbEdgesBefore); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), aNbVerticesBefore); +} + +TEST(BRepGraph_LayerTopoSupplementTest, + ShapesView_AddNonSolidToCompSolidRoutesInvalidChildWithoutOrphanAppend) +{ + BRepGraph aGraph; + TopoDS_CompSolid aCompSolidShape; + BRep_Builder aBuilder; + aBuilder.MakeCompSolid(aCompSolidShape); + aBuilder.Add(aCompSolidShape, makePlainSolid()); + + const BRepGraph::ShapesView::Result aCompSolidResult = aGraph.Shapes().Add(aCompSolidShape); + ASSERT_TRUE(aCompSolidResult.IsOk()); + ASSERT_TRUE(aCompSolidResult.TopologyRoot.IsValid()); + ASSERT_EQ(aCompSolidResult.TopologyRoot.NodeKind, BRepGraph_NodeId::Kind::CompSolid); + + const uint32_t aNbEdgesBefore = aGraph.Topo().Edges().Nb(); + const uint32_t aNbVerticesBefore = aGraph.Topo().Vertices().Nb(); + + TopoDS_Edge aEdge; + aBuilder.MakeEdge(aEdge); + + const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().Add(aEdge, aCompSolidResult.TopologyRoot); + EXPECT_TRUE(aResult.IsOk()); + EXPECT_FALSE(aResult.TopologyRoot.IsValid()); + EXPECT_FALSE(aResult.InsertedRef.IsValid()); + + EXPECT_EQ(aGraph.Topo().Edges().Nb(), aNbEdgesBefore); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), aNbVerticesBefore); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(aCompSolidResult.TopologyRoot); + ASSERT_EQ(anAttached.Size(), 1); + const BRepGraph_LayerTopoSupplement::Entry* anEntry = aLayer->FindByUid(anAttached.First()); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Kind, BRepGraph_LayerTopoSupplement::AttachmentKind::CompSolidAuxShape); + EXPECT_EQ(anEntry->Shape.ShapeType(), TopAbs_EDGE); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Lock_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Lock_Test.cxx new file mode 100644 index 0000000000..51cd186cac --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Lock_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +BRepGraph makeBoxGraph() +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + return aGraph; +} + +const Standard_GUID& testOwnerId() +{ + static const Standard_GUID THE_OWNER_ID("2ee13474-4cc6-4f6f-bde2-f0e4dcf9e5f1"); + return THE_OWNER_ID; +} + +const Standard_GUID& otherOwnerId() +{ + static const Standard_GUID THE_OWNER_ID("63c7da5f-4adc-4024-85f2-329525dc76b7"); + return THE_OWNER_ID; +} + +template +void lockItem(BRepGraph& theGraph, const ItemIdT theItem) +{ + theGraph.LayerRegistry().Ensure()->SetOwner(theItem, testOwnerId()); +} + +void unlockItem(BRepGraph& theGraph, const BRepGraph_VertexId theItem) +{ + theGraph.LayerRegistry().Ensure()->UnsetOwner( + BRepGraph_ItemId(static_cast(theItem)), + testOwnerId()); +} + +bool isOwned(const BRepGraph& theGraph, const BRepGraph_VertexId theVertex) +{ + const occ::handle aLayer = + theGraph.LayerRegistry().FindLayer(); + return !aLayer.IsNull() && aLayer->HasOwner(theVertex); +} + +bool isOwned(const BRepGraph& theGraph, const BRepGraph_FaceId theFace) +{ + const occ::handle aLayer = + theGraph.LayerRegistry().FindLayer(); + return !aLayer.IsNull() && aLayer->HasOwner(theFace); +} + +bool isOwned(const BRepGraph& theGraph, const BRepGraph_VertexRefId theRef) +{ + const occ::handle aLayer = + theGraph.LayerRegistry().FindLayer(); + return !aLayer.IsNull() && aLayer->HasOwner(theRef); +} + +bool isOwned(const BRepGraph& theGraph, const BRepGraph_WireRefId theRef) +{ + const occ::handle aLayer = + theGraph.LayerRegistry().FindLayer(); + return !aLayer.IsNull() && aLayer->HasOwner(theRef); +} + +class BRepGraph_LayerItemDispatchProbe : public BRepGraph_Layer +{ +public: + const Standard_GUID& ID() const override + { + static const Standard_GUID THE_ID("9f11b126-430a-4535-aa4a-8b8a4446dd5f"); + return THE_ID; + } + + const TCollection_AsciiString& Name() const override + { + static const TCollection_AsciiString THE_NAME("ItemDispatchProbe"); + return THE_NAME; + } + + int SubscribedKinds() const override + { + return BRepGraph_Layer::KindBit(BRepGraph_NodeId::Kind::Face); + } + + int SubscribedRefKinds() const override + { + return BRepGraph_Layer::RefKindBit(BRepGraph_RefId::Kind::Vertex); + } + + void OnNodeRemoved(const BRepGraph_NodeId theNode) noexcept override + { + myRemovedItems.Append(BRepGraph_ItemId(theNode)); + } + + void OnRefRemoved(const BRepGraph_RefId theRef) noexcept override + { + myRemovedItems.Append(BRepGraph_ItemId(theRef)); + } + + void OnNodeModified(const BRepGraph_NodeId theNode) noexcept override + { + myModifiedItems.Append(BRepGraph_ItemId(theNode)); + } + + void OnRefModified(const BRepGraph_RefId theRef) noexcept override + { + myModifiedItems.Append(BRepGraph_ItemId(theRef)); + } + + void CopyTo(const BRepGraph_CopyRemap&) const override {} + + void InvalidateAll() noexcept override {} + + void Clear() noexcept override + { + myRemovedItems.Clear(); + myModifiedItems.Clear(); + } + + const NCollection_DynamicArray& RemovedItems() const { return myRemovedItems; } + + const NCollection_DynamicArray& ModifiedItems() const + { + return myModifiedItems; + } + +private: + NCollection_DynamicArray myRemovedItems; + NCollection_DynamicArray myModifiedItems; +}; + +} // namespace + +TEST(BRepGraph_LockTest, NewItemsAreUnlockedByDefault) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + const BRepGraph_VertexRefId aVertexRefId = + aGraph.Topo().Edges().Definition(anEdgeId).StartVertexRefId; + + EXPECT_FALSE(isOwned(aGraph, aVertexId)); + EXPECT_FALSE(isOwned(aGraph, aVertexRefId)); +} + +TEST(BRepGraph_LockTest, LockLayerControlsStorageFlag) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); + EXPECT_FALSE(isOwned(aGraph, aVertexId)); + + lockItem(aGraph, aVertexId); + EXPECT_TRUE(isOwned(aGraph, aVertexId)); + + unlockItem(aGraph, aVertexId); + EXPECT_FALSE(isOwned(aGraph, aVertexId)); +} + +TEST(BRepGraph_LockTest, LockedNodeRejectsMutableGuard) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); + lockItem(aGraph, aVertexId); + +#ifndef No_Exception + EXPECT_THROW({ (void)aGraph.Editor().Vertices().Mut(aVertexId); }, Standard_ProgramError); +#endif +} + +TEST(BRepGraph_LockTest, LockedReferenceRejectsMutableGuard) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexRefId aVertexRefId = + aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).StartVertexRefId; + lockItem(aGraph, aVertexRefId); + +#ifndef No_Exception + EXPECT_THROW({ (void)aGraph.Editor().Vertices().MutRef(aVertexRefId); }, Standard_ProgramError); +#endif +} + +TEST(BRepGraph_LockTest, LockedNodeRejectsRemoval) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); + lockItem(aGraph, aVertexId); + +#ifndef No_Exception + EXPECT_THROW({ aGraph.Editor().Gen().RemoveNode(aVertexId); }, Standard_ProgramError); +#endif +} + +TEST(BRepGraph_LockTest, LockedParentRejectsStructuralAdd) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_ShellId aShellId = BRepGraph_ShellId::Start(); + lockItem(aGraph, aShellId); + +#ifndef No_Exception + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + EXPECT_THROW( + { (void)aGraph.Editor().Shells().Append(aShellId, aFaceId, TopAbs_FORWARD); }, + Standard_ProgramError); +#endif +} + +TEST(BRepGraph_LockTest, LockedNodeRejectsDirectSetterWithoutMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); + const gp_Pnt aPoint = aGraph.Topo().Vertices().Definition(aVertexId).Point; + lockItem(aGraph, aVertexId); + +#ifndef No_Exception + EXPECT_THROW( + { aGraph.Editor().Vertices().SetPoint(aVertexId, gp_Pnt(1.0, 2.0, 3.0)); }, + Standard_ProgramError); +#endif + EXPECT_NEAR(aGraph.Topo().Vertices().Definition(aVertexId).Point.Distance(aPoint), 0.0, 1.0e-12); +} + +TEST(BRepGraph_LockTest, LockedReferenceRejectsOrientationSetterWithoutMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexRefId aVertexRefId = + aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).StartVertexRefId; + const TopAbs_Orientation anOrientation = aGraph.Refs().Vertices().Entry(aVertexRefId).Orientation; + lockItem(aGraph, aVertexRefId); + +#ifndef No_Exception + EXPECT_THROW( + { aGraph.Editor().Vertices().SetRefOrientation(aVertexRefId, TopAbs_REVERSED); }, + Standard_ProgramError); +#endif + EXPECT_EQ(aGraph.Refs().Vertices().Entry(aVertexRefId).Orientation, anOrientation); +} + +TEST(BRepGraph_LockTest, LockedRepresentationRejectsDirectSetterWithoutMutation) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const occ::handle aSurface = + aGraph.Topo().Faces().Surface(BRepGraph_FaceId::Start()); + ASSERT_FALSE(aSurface.IsNull()); + + EXPECT_EQ(aGraph.Topo().Faces().Surface(BRepGraph_FaceId::Start()).get(), aSurface.get()); +} + +TEST(BRepGraph_LockTest, LockLayerOwnerControlsLockFlag) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); + occ::handle aLayer = aGraph.LayerRegistry().Ensure(); + const Standard_GUID anOwnerId("2ee13474-4cc6-4f6f-bde2-f0e4dcf9e5f1"); + + aLayer->SetOwner(aVertexId, anOwnerId); + + EXPECT_TRUE(isOwned(aGraph, aVertexId)); + ASSERT_TRUE(aLayer->HasOwner(aVertexId)); + Standard_GUID aFoundOwnerId; + ASSERT_TRUE(aLayer->FindOwnerId(aVertexId, aFoundOwnerId)); + EXPECT_EQ(aFoundOwnerId, anOwnerId); + + aLayer->UnsetOwner(aVertexId); + EXPECT_FALSE(isOwned(aGraph, aVertexId)); + EXPECT_FALSE(aLayer->HasOwner(aVertexId)); +} + +TEST(BRepGraph_LockTest, RejectsDifferentOwnerWhenAncestorOwnsNode) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + occ::handle aLayer = aGraph.LayerRegistry().Ensure(); + + ASSERT_TRUE(aLayer->SetOwner(BRepGraph_ItemId(aSolidId), testOwnerId(), false)); + EXPECT_TRUE(aLayer->HasOwner(aFaceId)); + EXPECT_FALSE(aLayer->SetOwner(BRepGraph_ItemId(aFaceId), otherOwnerId(), false)); + + Standard_GUID aFoundOwnerId; + ASSERT_TRUE(aLayer->FindOwnerId(aFaceId, aFoundOwnerId)); + EXPECT_EQ(aFoundOwnerId, testOwnerId()); +} + +TEST(BRepGraph_LockTest, ParentOwnerRejectsDifferentOwnedDescendant) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + occ::handle aLayer = aGraph.LayerRegistry().Ensure(); + + ASSERT_TRUE(aLayer->SetOwner(BRepGraph_ItemId(aFaceId), testOwnerId(), false)); + EXPECT_FALSE(aLayer->SetOwner(BRepGraph_ItemId(aSolidId), otherOwnerId(), false)); + EXPECT_TRUE(aLayer->HasOwner(aFaceId)); + EXPECT_FALSE(aLayer->HasOwner(aSolidId)); +} + +TEST(BRepGraph_LockTest, ParentOwnerCollapsesSameOwnedDescendant) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + occ::handle aLayer = aGraph.LayerRegistry().Ensure(); + + ASSERT_TRUE(aLayer->SetOwner(BRepGraph_ItemId(aFaceId), testOwnerId(), false)); + EXPECT_TRUE(aLayer->SetOwner(BRepGraph_ItemId(aSolidId), testOwnerId(), false)); + EXPECT_TRUE(aLayer->HasOwner(aFaceId)); + EXPECT_TRUE(aLayer->HasOwner(aSolidId)); + + aLayer->UnsetOwner(aSolidId); + EXPECT_FALSE(aLayer->HasOwner(aSolidId)); + EXPECT_FALSE(aLayer->HasOwner(aFaceId)); +} + +TEST(BRepGraph_LockTest, RemovedRootOwnerCallbackClearsDescendantOwnedFlags) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraphInc::FaceRelations& aFaceRel = aGraph.Topo().Faces().Relations(aFaceId); + ASSERT_FALSE(aFaceRel.WireRefIds.IsEmpty()); + const BRepGraph_WireRefId aWireRefId = aFaceRel.WireRefIds.First(); + + occ::handle aLayer = aGraph.LayerRegistry().Ensure(); + ASSERT_TRUE(aLayer->SetOwner(BRepGraph_ItemId(aSolidId), testOwnerId(), false)); + EXPECT_TRUE(aLayer->HasOwner(aSolidId)); + EXPECT_TRUE(aLayer->HasOwner(aFaceId)); + EXPECT_TRUE(aLayer->HasOwner(aWireRefId)); + + aGraph.LayerRegistry().DispatchOnNodeRemoved(aSolidId); + + EXPECT_FALSE(aLayer->HasOwner(aSolidId)); + EXPECT_FALSE(aLayer->HasOwner(aFaceId)); + EXPECT_FALSE(aLayer->HasOwner(aWireRefId)); +} + +TEST(BRepGraph_LockTest, DeferredLayerRegistersMultipleRepresentationsAndLocksItem) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + + aLayer->RegisterDeferred(aFaceId, + "TestProvider", + "test-source", + BRepGraph_LayerDeferred::RepresentationKind::Geometry, + "surface", + 42); + aLayer->RegisterDeferred(aFaceId, + "TestProvider", + "test-source", + BRepGraph_LayerDeferred::RepresentationKind::Mesh, + "triangulation", + 7); + + EXPECT_TRUE(isOwned(aGraph, aFaceId)); + const BRepGraph_LayerDeferred::Entry* anEntry = aLayer->FindDeferred(aFaceId); + ASSERT_NE(anEntry, nullptr); + EXPECT_EQ(anEntry->Provider, TCollection_AsciiString("TestProvider")); + EXPECT_EQ(anEntry->SourceKey, TCollection_AsciiString("test-source")); + EXPECT_EQ(anEntry->Representations.Size(), 2); + + aLayer->UnregisterDeferred(aFaceId); + EXPECT_FALSE(isOwned(aGraph, aFaceId)); + EXPECT_FALSE(aLayer->HasDeferred(aFaceId)); +} + +TEST(BRepGraph_LockTest, DeferredLayerClearsOwnerOnNodeRemoved) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + + aLayer->RegisterDeferred(aFaceId, + "TestProvider", + "test-source", + BRepGraph_LayerDeferred::RepresentationKind::Geometry, + "surface", + 42); + ASSERT_TRUE(isOwned(aGraph, aFaceId)); + ASSERT_TRUE(aLayer->HasDeferred(aFaceId)); + + aGraph.LayerRegistry().DispatchOnNodeRemoved(aFaceId); + + EXPECT_FALSE(isOwned(aGraph, aFaceId)); + EXPECT_FALSE(aLayer->HasDeferred(aFaceId)); +} + +TEST(BRepGraph_LockTest, ItemDispatchDelegatesToTypedCallbacks) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + occ::handle aLayer = new BRepGraph_LayerItemDispatchProbe(); + aGraph.LayerRegistry().RegisterLayer(aLayer); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_VertexRefId aVertexRefId = + aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).StartVertexRefId; + + aGraph.LayerRegistry().DispatchOnItemRemoved(BRepGraph_ItemId(aFaceId)); + aGraph.LayerRegistry().DispatchOnItemRemoved(BRepGraph_ItemId(aVertexRefId)); + + ASSERT_EQ(aLayer->RemovedItems().Size(), 2); + EXPECT_TRUE(aLayer->RemovedItems().Value(0).IsNode()); + EXPECT_EQ(aLayer->RemovedItems().Value(0).NodeId(), BRepGraph_NodeId(aFaceId)); + EXPECT_TRUE(aLayer->RemovedItems().Value(1).IsReference()); + EXPECT_EQ(aLayer->RemovedItems().Value(1).RefId(), BRepGraph_RefId(aVertexRefId)); + + aGraph.LayerRegistry().DispatchItemModified(BRepGraph_ItemId(aFaceId)); + aGraph.LayerRegistry().DispatchItemModified(BRepGraph_ItemId(aVertexRefId)); + + ASSERT_EQ(aLayer->ModifiedItems().Size(), 2); + EXPECT_TRUE(aLayer->ModifiedItems().Value(0).IsNode()); + EXPECT_EQ(aLayer->ModifiedItems().Value(0).NodeId(), BRepGraph_NodeId(aFaceId)); + EXPECT_TRUE(aLayer->ModifiedItems().Value(1).IsReference()); + EXPECT_EQ(aLayer->ModifiedItems().Value(1).RefId(), BRepGraph_RefId(aVertexRefId)); +} + +TEST(BRepGraph_LockTest, Compact_PreservesDeferredRefEntry) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aRemovedFace = BRepGraph_FaceId::Start(); + const BRepGraph_FaceId aKeptFace(1); + const BRepGraphInc::FaceRelations& aFaceRelations = aGraph.Topo().Faces().Relations(aKeptFace); + ASSERT_FALSE(aFaceRelations.WireRefIds.IsEmpty()); + const BRepGraph_WireRefId aWireRefId = aFaceRelations.WireRefIds.First(); + + occ::handle aLayer = + aGraph.LayerRegistry().Ensure(); + aLayer->RegisterDeferred(aWireRefId, + "TestProvider", + "ref-source", + BRepGraph_LayerDeferred::RepresentationKind::Topology, + "wire-ref", + 11); + ASSERT_TRUE(aLayer->HasDeferred(aWireRefId)); + ASSERT_TRUE(isOwned(aGraph, aWireRefId)); + + aGraph.Editor().Gen().RemoveNode(aRemovedFace); + std::ignore = BRepGraph_Compact::Perform(aGraph); + + // Re-acquire layer after compact (CopyLayersTo replaces old instance). + aLayer = aGraph.LayerRegistry().Ensure(); + + ASSERT_GE(aGraph.Topo().Faces().Nb(), 1u); + const BRepGraphInc::FaceRelations& aCompactedFaceRelations = + aGraph.Topo().Faces().Relations(BRepGraph_FaceId::Start()); + ASSERT_FALSE(aCompactedFaceRelations.WireRefIds.IsEmpty()); + const BRepGraph_WireRefId aCompactedWireRefId = aCompactedFaceRelations.WireRefIds.First(); + + EXPECT_TRUE(aLayer->HasDeferred(aCompactedWireRefId)); + EXPECT_TRUE(isOwned(aGraph, aCompactedWireRefId)); +} + +TEST(BRepGraph_LockTest, Compact_PreservesLockOnExactGeometryRep) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aRemovedFace = BRepGraph_FaceId::Start(); + const BRepGraph_FaceId aKeptFace(1); + const BRepGraph_FaceSurfaceRepId aSurfaceRepId = + aGraph.Topo().Faces().Definition(aKeptFace).SurfaceRepId; + ASSERT_TRUE(aSurfaceRepId.IsValid()); + + occ::handle aLayer = aGraph.LayerRegistry().Ensure(); + aLayer->SetOwner(aKeptFace, testOwnerId()); + ASSERT_TRUE(aLayer->HasOwner(aKeptFace)); + + aGraph.Editor().Gen().RemoveNode(aRemovedFace); + std::ignore = BRepGraph_Compact::Perform(aGraph); + + // Re-acquire layer after compact (CopyLayersTo replaces old instance). + aLayer = aGraph.LayerRegistry().Ensure(); + + const BRepGraph_FaceSurfaceRepId aCompactedSurfaceRepId = + aGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).SurfaceRepId; + ASSERT_TRUE(aCompactedSurfaceRepId.IsValid()); + EXPECT_TRUE(aLayer->HasOwner(BRepGraph_FaceId::Start())); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_MeshCache_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_MeshCache_Test.cxx deleted file mode 100644 index 5f850bc7a4..0000000000 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_MeshCache_Test.cxx +++ /dev/null @@ -1,141 +0,0 @@ -// 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. - -// Regression coverage for BRepGraph_MeshCache freshness: verifies that a -// cached triangulation becomes stale when the owning Face's OwnGen bumps, -// whether the bump comes from a direct FaceDef mutation or from a -// SurfaceRep mutation that propagates through markRepModified(). - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace -{ - -BRepGraph makeBoxGraph() -{ - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - return aGraph; -} - -occ::handle makeTrivialTriangulation() -{ - return new Poly_Triangulation(3, 1, false); -} - -BRepGraph_FaceId firstFaceId(const BRepGraph& theGraph) -{ - BRepGraph_FaceIterator aFaceIt(theGraph); - return aFaceIt.More() ? aFaceIt.CurrentId() : BRepGraph_FaceId(); -} - -} // namespace - -TEST(BRepGraph_MeshCacheTest, CacheStaleAfterFaceMutation) -{ - BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); - - const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); - ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); - - const BRepGraph_TriangulationRepId aTriRepId = - BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, makeTrivialTriangulation()); - ASSERT_TRUE(aTriRepId.IsValid()); - - BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aTriRepId); - BRepGraph_Tool::Mesh::SetCachedActiveIndex(aGraph, aFaceId, 0); - - const BRepGraph_MeshCache::FaceMeshEntry* aBefore = aGraph.Mesh().Faces().CachedMesh(aFaceId); - ASSERT_NE(aBefore, nullptr) << "CachedMesh must be present immediately after write"; - - { - BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(aFaceId); - aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); - } - - const BRepGraph_MeshCache::FaceMeshEntry* aAfter = aGraph.Mesh().Faces().CachedMesh(aFaceId); - EXPECT_EQ(aAfter, nullptr) << "CachedMesh must become null (stale) after Face Mut bumps OwnGen"; -} - -TEST(BRepGraph_MeshCacheTest, CacheStaleAfterSurfaceRepMutation) -{ - BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); - - const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); - ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); - - const BRepGraph_SurfaceRepId aSurfaceRepId = - aGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId; - ASSERT_TRUE(aSurfaceRepId.IsValid()); - - const BRepGraph_TriangulationRepId aTriRepId = - BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, makeTrivialTriangulation()); - ASSERT_TRUE(aTriRepId.IsValid()); - - BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aTriRepId); - BRepGraph_Tool::Mesh::SetCachedActiveIndex(aGraph, aFaceId, 0); - - ASSERT_NE(aGraph.Mesh().Faces().CachedMesh(aFaceId), nullptr); - - { - BRepGraph_MutGuard aGuard = - aGraph.Editor().Reps().MutSurface(aSurfaceRepId); - aGuard.MarkDirty(); - } - - EXPECT_EQ(aGraph.Mesh().Faces().CachedMesh(aFaceId), nullptr) - << "CachedMesh must become null after SurfaceRep Mut propagates to Face OwnGen"; -} - -TEST(BRepGraph_MeshCacheTest, CacheSurvivesUnrelatedMutation) -{ - BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); - - const BRepGraph_FaceId aFaceId = firstFaceId(aGraph); - ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); - - BRepGraph_FaceIterator anOther(aGraph); - anOther.Next(); - ASSERT_TRUE(anOther.More()) << "Box should have >1 face for this test"; - const BRepGraph_FaceId anOtherFaceId = anOther.CurrentId(); - - const BRepGraph_TriangulationRepId aTriRepId = - BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, makeTrivialTriangulation()); - ASSERT_TRUE(aTriRepId.IsValid()); - - BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aTriRepId); - BRepGraph_Tool::Mesh::SetCachedActiveIndex(aGraph, aFaceId, 0); - - { - BRepGraph_MutGuard aGuard = aGraph.Editor().Faces().Mut(anOtherFaceId); - aGraph.Editor().Faces().SetTolerance(aGuard, aGuard->Tolerance + 1.0e-6); - } - - EXPECT_NE(aGraph.Mesh().Faces().CachedMesh(aFaceId), nullptr) - << "Unrelated face mutation must not invalidate this face's cache"; -} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_MutGuard_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_MutGuard_Test.cxx index e918246110..63a5be7979 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_MutGuard_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_MutGuard_Test.cxx @@ -14,13 +14,13 @@ // Regression coverage for BRepGraph_MutGuard: move semantics, inert-state // detection (operator bool()), and exception-path notification safety. // -// MutGuard owns exactly one markModified/markRefModified/markRepModified call -// per scope. A move transfers that obligation; a moved-from guard must be -// inert. An exception inside the scope must not skip the notification nor -// propagate a noexcept-violation from the guard's destructor. +// MutGuard owns exactly one definition/ref mutation notification per scope. A +// move transfers that obligation; a moved-from guard must be inert. An +// exception inside the scope must not skip the notification nor propagate a +// noexcept-violation from the guard's destructor. #include -#include +#include #include #include #include @@ -39,8 +39,8 @@ BRepGraph makeBoxGraph() { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); return aGraph; } @@ -49,7 +49,7 @@ BRepGraph makeBoxGraph() TEST(BRepGraph_MutGuardTest, OperatorBool_TrueWhenOwned_FalseAfterMove) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_MutGuard aGuard = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); @@ -63,7 +63,7 @@ TEST(BRepGraph_MutGuardTest, OperatorBool_TrueWhenOwned_FalseAfterMove) TEST(BRepGraph_MutGuardTest, DereferenceAfterMove_Throws) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_MutGuard aGuard = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); @@ -80,7 +80,7 @@ TEST(BRepGraph_MutGuardTest, DereferenceAfterMove_Throws) TEST(BRepGraph_MutGuardTest, MoveAssignmentFlushesThenTransfers) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const uint32_t aGenBefore = aGraph.Topo().Vertices().Definition(BRepGraph_VertexId::Start()).OwnGen; @@ -110,7 +110,7 @@ TEST(BRepGraph_MutGuardTest, MoveAssignmentFlushesThenTransfers) TEST(BRepGraph_MutGuardTest, ExceptionInsideScope_StillNotifies) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const uint32_t aGenBefore = aGraph.Topo().Vertices().Definition(BRepGraph_VertexId::Start()).OwnGen; @@ -137,7 +137,7 @@ TEST(BRepGraph_MutGuardTest, ExceptionInsideScope_StillNotifies) TEST(BRepGraph_MutGuardTest, MovedFrom_DoesNotDoubleNotify) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const uint32_t aGenBefore = aGraph.Topo().Vertices().Definition(BRepGraph_VertexId::Start()).OwnGen; @@ -159,7 +159,7 @@ TEST(BRepGraph_MutGuardTest, MovedFrom_DoesNotDoubleNotify) TEST(BRepGraph_MutGuardTest, RefGuard_SameMoveSemantics) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Refs().Faces().Nb(), 0); const BRepGraph_FaceRefId aRefId(0); @@ -170,3 +170,82 @@ TEST(BRepGraph_MutGuardTest, RefGuard_SameMoveSemantics) EXPECT_FALSE(static_cast(aGuard)); EXPECT_TRUE(static_cast(aMoved)); } + +TEST(BRepGraph_MutGuardTest, DuplicateGuard_Rejected) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_MutGuard aGuard1 = + aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); + EXPECT_TRUE(static_cast(aGuard1)); + +#ifndef No_Exception + // Attempting to acquire a second guard on the same vertex must throw. + EXPECT_THROW( + { + auto aDuplicate = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); + (void)aDuplicate; + }, + Standard_ProgramError); +#endif +} + +TEST(BRepGraph_MutGuardTest, GuardAllowsGuardedSetter) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + { + BRepGraph_MutGuard aGuard = + aGraph.Editor().Edges().Mut(BRepGraph_EdgeId(0)); + // Guarded setter must NOT self-block via requireUnlocked. + aGraph.Editor().Edges().SetTolerance(aGuard, 0.5); + EXPECT_TRUE(aGuard.IsDirty()); + } +} + +TEST(BRepGraph_MutGuardTest, GuardBlocksStructuralRemoval) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_MutGuard aGuard = + aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); + EXPECT_TRUE(static_cast(aGuard)); + + // Structural removal must throw while a guard is active on the item. + EXPECT_THROW(aGraph.Editor().Gen().RemoveNode(BRepGraph_VertexId::Start()), + Standard_ProgramError); +} + +TEST(BRepGraph_MutGuardTest, GuardReleasedOnDestruction_CanReacquire) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + { + BRepGraph_MutGuard aGuard = + aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); + EXPECT_TRUE(static_cast(aGuard)); + } + // Guard destroyed - should be able to acquire again. + BRepGraph_MutGuard aGuard2 = + aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); + EXPECT_TRUE(static_cast(aGuard2)); +} + +TEST(BRepGraph_MutGuardTest, ClearRejectsActiveGuard) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + { + BRepGraph_MutGuard aGuard = + aGraph.Editor().Vertices().Mut(BRepGraph_VertexId::Start()); + EXPECT_TRUE(static_cast(aGuard)); + EXPECT_THROW(aGraph.Clear(), Standard_ProgramError); + } + + EXPECT_NO_THROW(aGraph.Clear()); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_MutationGen_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_MutationGen_Test.cxx index 2b24737c5c..3f4ddfba0d 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_MutationGen_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_MutationGen_Test.cxx @@ -15,8 +15,15 @@ #include #include #include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include @@ -28,9 +35,8 @@ protected: BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBox); - ASSERT_TRUE(myGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = myGraph.Shapes().Add(aBox); + ASSERT_FALSE(myGraph.IsEmpty()); } BRepGraph myGraph; @@ -47,6 +53,74 @@ TEST_F(BRepGraph_MutationGenTest, OwnGen_IncrementedOnMutation) EXPECT_EQ(myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).SubtreeGen, 1u); } +TEST(BRepGraph_MutationGenEditorTest, EditorCreationRegistersOwnGeneration) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.e-7); + const BRepGraph_CoEdgeId aCoEdge = aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD); + + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aCoEdge); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + + NCollection_LinearVector anInnerWires; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(new Geom_Plane(gp_Pln()), aWire, anInnerWires.ToArray1(), 1.e-7); + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + + NCollection_LinearVector aChildren; + aChildren.Append(aSolid); + const BRepGraph_CompoundId aCompound = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + + NCollection_LinearVector aSolids; + aSolids.Append(aSolid); + const BRepGraph_CompSolidId aCompSolid = aGraph.Editor().CompSolids().Add(aSolids.ToArray1()); + + EXPECT_EQ(aGraph.Topo().Vertices().Definition(aV0).OwnGen, 1u); + EXPECT_EQ(aGraph.Topo().Vertices().Definition(aV1).OwnGen, 1u); + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdge).OwnGen, 3u); + EXPECT_EQ(aGraph.Topo().CoEdges().Definition(aCoEdge).OwnGen, 2u); + EXPECT_EQ(aGraph.Topo().Wires().Definition(aWire).OwnGen, 1u); + EXPECT_EQ(aGraph.Topo().Faces().Definition(aFace).OwnGen, 1u); + EXPECT_EQ(aGraph.Topo().Shells().Definition(aShell).OwnGen, 1u); + EXPECT_EQ(aGraph.Topo().Solids().Definition(aSolid).OwnGen, 1u); + EXPECT_EQ(aGraph.Topo().Compounds().Definition(aCompound).OwnGen, 1u); + EXPECT_EQ(aGraph.Topo().CompSolids().Definition(aCompSolid).OwnGen, 1u); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraph_MutationGenEditorTest, ProductAppendRegistersBothProductsAndOccurrence) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_ProductId aParent = aGraph.Editor().Products().Add(); + const BRepGraph_ProductId aChild = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aChild); + + const BRepGraph_VersionStamp aParentStamp = aGraph.UIDs().StampOf(aParent); + const BRepGraph_VersionStamp aChildStamp = aGraph.UIDs().StampOf(aChild); + const uint32_t aParentOwnGen = aGraph.Topo().Products().Definition(aParent).OwnGen; + const uint32_t aChildOwnGen = aGraph.Topo().Products().Definition(aChild).OwnGen; + + const BRepGraph_OccurrenceId anOccurrence = + aGraph.Editor().Products().Append(aParent, aChild, TopLoc_Location()); + + ASSERT_TRUE(anOccurrence.IsValid()); + EXPECT_TRUE(aGraph.UIDs().IsStale(aParentStamp)); + EXPECT_TRUE(aGraph.UIDs().IsStale(aChildStamp)); + EXPECT_EQ(aGraph.Topo().Occurrences().Definition(anOccurrence).OwnGen, 1u); + EXPECT_EQ(aGraph.Topo().Products().Definition(aParent).OwnGen, aParentOwnGen + 1u); + EXPECT_EQ(aGraph.Topo().Products().Definition(aChild).OwnGen, aChildOwnGen + 1u); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + TEST_F(BRepGraph_MutationGenTest, OwnGen_MultipleIncrements) { myGraph.Editor().Edges().SetTolerance(BRepGraph_EdgeId::Start(), 0.1); @@ -193,70 +267,98 @@ TEST_F(BRepGraph_MutationGenTest, SubtreeGen_DeferredPropagatedParent_Incremente EXPECT_TRUE(aAnyFaceSubtreeIncremented); } +TEST_F(BRepGraph_MutationGenTest, SubtreeGen_SolidMutation_PropagatesThroughRootOccurrence) +{ + ASSERT_EQ(myGraph.RootProductIds().Size(), 1); + const BRepGraph_ProductId aRootProduct = myGraph.RootProductIds().Value(0); + const BRepGraph_OccurrenceId aRootOccurrence = + myGraph.Topo().Products().Component(aRootProduct, 0); + ASSERT_TRUE(aRootOccurrence.IsValid()); + + const uint32_t anOccurrenceSubtreeGenBefore = + myGraph.Topo().Occurrences().Definition(aRootOccurrence).SubtreeGen; + const uint32_t aProductSubtreeGenBefore = + myGraph.Topo().Products().Definition(aRootProduct).SubtreeGen; + + const BRepGraph_ShellId aShell = myGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + const BRepGraph_ShellRefId aShellRef = + myGraph.Editor().Solids().Append(BRepGraph_SolidId::Start(), aShell); + ASSERT_TRUE(aShellRef.IsValid()); + + EXPECT_GT(myGraph.Topo().Occurrences().Definition(aRootOccurrence).SubtreeGen, + anOccurrenceSubtreeGenBefore); + EXPECT_GT(myGraph.Topo().Products().Definition(aRootProduct).SubtreeGen, + aProductSubtreeGenBefore); +} + +TEST_F(BRepGraph_MutationGenTest, SubtreeGen_DeferredSolidMutation_PropagatesThroughRootOccurrence) +{ + ASSERT_EQ(myGraph.RootProductIds().Size(), 1); + const BRepGraph_ProductId aRootProduct = myGraph.RootProductIds().Value(0); + const BRepGraph_OccurrenceId aRootOccurrence = + myGraph.Topo().Products().Component(aRootProduct, 0); + ASSERT_TRUE(aRootOccurrence.IsValid()); + + const uint32_t anOccurrenceSubtreeGenBefore = + myGraph.Topo().Occurrences().Definition(aRootOccurrence).SubtreeGen; + const uint32_t aProductSubtreeGenBefore = + myGraph.Topo().Products().Definition(aRootProduct).SubtreeGen; + + myGraph.Editor().BeginDeferredInvalidation(); + const BRepGraph_ShellId aShell = myGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + const BRepGraph_ShellRefId aShellRef = + myGraph.Editor().Solids().Append(BRepGraph_SolidId::Start(), aShell); + ASSERT_TRUE(aShellRef.IsValid()); + + EXPECT_EQ(myGraph.Topo().Occurrences().Definition(aRootOccurrence).SubtreeGen, + anOccurrenceSubtreeGenBefore); + EXPECT_EQ(myGraph.Topo().Products().Definition(aRootProduct).SubtreeGen, + aProductSubtreeGenBefore); + + myGraph.Editor().EndDeferredInvalidation(); + + EXPECT_GT(myGraph.Topo().Occurrences().Definition(aRootOccurrence).SubtreeGen, + anOccurrenceSubtreeGenBefore); + EXPECT_GT(myGraph.Topo().Products().Definition(aRootProduct).SubtreeGen, + aProductSubtreeGenBefore); +} + TEST_F(BRepGraph_MutationGenTest, RepMutation_SurfacePropagatesSubtreeGenToFace) { - const BRepGraph_FaceId aFaceId(0); - const BRepGraph_SurfaceRepId aSurfId = myGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId; - ASSERT_TRUE(aSurfId.IsValid()); + const BRepGraph_FaceId aFaceId(0); EXPECT_EQ(myGraph.Topo().Faces().Definition(aFaceId).OwnGen, 0u); EXPECT_EQ(myGraph.Topo().Faces().Definition(aFaceId).SubtreeGen, 0u); - { - BRepGraph_MutGuard aGuard = - myGraph.Editor().Reps().MutSurface(aSurfId); - aGuard.MarkDirty(); - } + myGraph.Editor().Faces().ClearSurface(aFaceId); - // Surface is the face's own geometry - rep mutation IS an own-data change. EXPECT_GT(myGraph.Topo().Faces().Definition(aFaceId).OwnGen, 0u); EXPECT_GT(myGraph.Topo().Faces().Definition(aFaceId).SubtreeGen, 0u); } TEST_F(BRepGraph_MutationGenTest, RepMutation_Curve3DPropagatesSubtreeGenToEdge) { - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraph_Curve3DRepId aCurveId = myGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId; - if (!aCurveId.IsValid()) - { - return; // Skip degenerate edges without 3D curves. - } - + const BRepGraph_EdgeId anEdgeId(0); EXPECT_EQ(myGraph.Topo().Edges().Definition(anEdgeId).OwnGen, 0u); EXPECT_EQ(myGraph.Topo().Edges().Definition(anEdgeId).SubtreeGen, 0u); - { - BRepGraph_MutGuard aGuard = - myGraph.Editor().Reps().MutCurve3D(aCurveId); - aGuard.MarkDirty(); - } + myGraph.Editor().Edges().ClearCurve(anEdgeId); - // Curve3D is the edge's own geometry - rep mutation IS an own-data change. EXPECT_GT(myGraph.Topo().Edges().Definition(anEdgeId).OwnGen, 0u); EXPECT_GT(myGraph.Topo().Edges().Definition(anEdgeId).SubtreeGen, 0u); } TEST_F(BRepGraph_MutationGenTest, RepMutation_Curve2DPropagatesSubtreeGenToCoEdge) { - // Find a coedge with a valid PCurve. for (BRepGraph_CoEdgeIterator aCoEdgeIt(myGraph); aCoEdgeIt.More(); aCoEdgeIt.Next()) { - const BRepGraph_Curve2DRepId aCurveId = aCoEdgeIt.Current().Curve2DRepId; - if (!aCurveId.IsValid()) - { - continue; - } - const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); EXPECT_EQ(aCoEdgeIt.Current().OwnGen, 0u); EXPECT_EQ(aCoEdgeIt.Current().SubtreeGen, 0u); - { - BRepGraph_MutGuard aGuard = - myGraph.Editor().Reps().MutCurve2D(aCurveId); - aGuard.MarkDirty(); - } + myGraph.Editor().CoEdges().ClearPCurve(aCoEdgeId); - // Curve2D is the coedge's own geometry - rep mutation IS an own-data change. EXPECT_GT(myGraph.Topo().CoEdges().Definition(aCoEdgeId).OwnGen, 0u); EXPECT_GT(myGraph.Topo().CoEdges().Definition(aCoEdgeId).SubtreeGen, 0u); return; @@ -265,27 +367,14 @@ TEST_F(BRepGraph_MutationGenTest, RepMutation_Curve2DPropagatesSubtreeGenToCoEdg TEST_F(BRepGraph_MutationGenTest, RepMutation_TriangulationPropagatesSubtreeGenToFace) { - // Find a face with a valid triangulation. for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraphInc::FaceDef& aFace = aFaceIt.Current(); - if (!aFace.TriangulationRepId.IsValid()) - { - continue; - } + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + EXPECT_EQ(aFaceIt.Current().OwnGen, 0u); + EXPECT_EQ(aFaceIt.Current().SubtreeGen, 0u); - const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - const BRepGraph_TriangulationRepId aTriId = aFace.TriangulationRepId; - EXPECT_EQ(aFace.OwnGen, 0u); - EXPECT_EQ(aFace.SubtreeGen, 0u); + myGraph.Editor().Faces().ClearPersistentTriangulation(aFaceId); - { - BRepGraph_MutGuard aGuard = - myGraph.Editor().Reps().MutTriangulation(aTriId); - aGuard.MarkDirty(); - } - - // Triangulation is the face's own mesh - rep mutation IS an own-data change. EXPECT_GT(myGraph.Topo().Faces().Definition(aFaceId).OwnGen, 0u); EXPECT_GT(myGraph.Topo().Faces().Definition(aFaceId).SubtreeGen, 0u); return; @@ -294,26 +383,14 @@ TEST_F(BRepGraph_MutationGenTest, RepMutation_TriangulationPropagatesSubtreeGenT TEST_F(BRepGraph_MutationGenTest, RepMutation_Polygon3DPropagatesSubtreeGenToEdge) { - // Find an edge with a valid Polygon3D. for (BRepGraph_EdgeIterator anEdgeIt(myGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraph_Polygon3DRepId aPolyId = anEdgeIt.Current().Polygon3DRepId; - if (!aPolyId.IsValid()) - { - continue; - } - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); EXPECT_EQ(anEdgeIt.Current().OwnGen, 0u); EXPECT_EQ(anEdgeIt.Current().SubtreeGen, 0u); - { - BRepGraph_MutGuard aGuard = - myGraph.Editor().Reps().MutPolygon3D(aPolyId); - aGuard.MarkDirty(); - } + myGraph.Editor().Edges().ClearPersistentPolygon3D(anEdgeId); - // Polygon3D is the edge's own mesh - rep mutation IS an own-data change. EXPECT_GT(myGraph.Topo().Edges().Definition(anEdgeId).OwnGen, 0u); EXPECT_GT(myGraph.Topo().Edges().Definition(anEdgeId).SubtreeGen, 0u); return; diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_NodeId_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_NodeId_Test.cxx index 5ee65d121d..5fe234c3c3 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_NodeId_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_NodeId_Test.cxx @@ -12,8 +12,9 @@ // commercial license or contractual agreement. #include +#include #include -#include +#include #include #include @@ -24,7 +25,7 @@ TEST(BRepGraph_NodeIdTest, Construction_DefaultInvalid) { BRepGraph_FaceId aFace; EXPECT_FALSE(aFace.IsValid()); - EXPECT_EQ(aFace.Index, -1); + EXPECT_EQ(aFace.Index, BRepGraph_FaceId::THE_INVALID_INDEX); } TEST(BRepGraph_NodeIdTest, Construction_FromIndex) @@ -59,15 +60,21 @@ TEST(BRepGraph_NodeIdTest, ImplicitConversion_PassToFunction) // Typed ids work with existing APIs that take BRepGraph_NodeId. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_FaceId aFace(0); - // AdjacentFaces takes BRepGraph_FaceId - typed id works directly. - NCollection_DynamicArray aAdj = - aGraph.Topo().Faces().Adjacent(aFace, aGraph.Allocator()); - EXPECT_GT(aAdj.Length(), 0); + // RelatedIterator accepts typed ids converted through BRepGraph_NodeId. + int aNbAdjacentFaces = 0; + for (BRepGraph_RelatedIterator anIt(aGraph, BRepGraph_NodeId(aFace)); anIt.More(); anIt.Next()) + { + if (anIt.CurrentRelation() == BRepGraph_RelatedIterator::RelationKind::AdjacentFace) + { + ++aNbAdjacentFaces; + } + } + EXPECT_GT(aNbAdjacentFaces, 0); } TEST(BRepGraph_NodeIdTest, FromNodeId_CorrectKind) @@ -169,8 +176,46 @@ TEST(BRepGraph_NodeIdTest, TypedArithmetic_IndexZeroBoundary) EXPECT_EQ(aZero.Index, 0); EXPECT_TRUE(aZero.IsValid()); - // Subtract to -1 produces invalid id (allowed by constructor). + // Subtract to the invalid sentinel produces an invalid id. const BRepGraph_EdgeId anInvalid = aZero - 1; - EXPECT_EQ(anInvalid.Index, -1); + EXPECT_EQ(anInvalid.Index, BRepGraph_EdgeId::THE_INVALID_INDEX); EXPECT_FALSE(anInvalid.IsValid()); } + +TEST(BRepGraph_NodeIdTest, InvalidKind_IsRejected) +{ + constexpr uint32_t THE_RESERVED_KIND = 9u; + const BRepGraph_NodeId aNode(static_cast(THE_RESERVED_KIND), 0u); + + EXPECT_FALSE(BRepGraph_NodeId::IsValidKind(aNode.NodeKind)); + EXPECT_FALSE(aNode.IsValid()); + EXPECT_FALSE(aNode.IsValid(10u)); + EXPECT_FALSE(BRepGraph_NodeId::IsTopologyKind(aNode.NodeKind)); + EXPECT_FALSE(BRepGraph_NodeId::IsAssemblyKind(aNode.NodeKind)); +} + +TEST(BRepGraph_NodeIdTest, FromNodeId_WrongKindReturnsInvalid) +{ + const BRepGraph_NodeId aNode(BRepGraph_NodeId::Kind::Face, 3u); + const BRepGraph_EdgeId anEdge = BRepGraph_EdgeId::FromNodeId(aNode); + + EXPECT_FALSE(anEdge.IsValid()); + EXPECT_EQ(anEdge.Index, BRepGraph_EdgeId::THE_INVALID_INDEX); +} + +TEST(BRepGraph_NodeIdTest, OutOfRangeMetadataQueriesReturnFalse) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_VertexId anOutOfRangeVertex(aGraph.Topo().Vertices().Nb()); + EXPECT_FALSE(anOutOfRangeVertex.IsRemoved(aGraph)); + EXPECT_FALSE(anOutOfRangeVertex.IsOwned(aGraph)); + + const BRepGraph_NodeId anOutOfRangeFace(BRepGraph_NodeId::Kind::Face, aGraph.Topo().Faces().Nb()); + EXPECT_FALSE(anOutOfRangeFace.IsRemoved(aGraph)); + EXPECT_FALSE(anOutOfRangeFace.IsOwned(aGraph)); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ParentExplorer_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ParentExplorer_Test.cxx index 8c934d47a5..b82e32b10d 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ParentExplorer_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ParentExplorer_Test.cxx @@ -16,21 +16,64 @@ #include #include #include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include +#include #include +namespace +{ +TopLoc_Location translationLocation(const double theX, const double theY, const double theZ) +{ + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(theX, theY, theZ)); + return TopLoc_Location(aTrsf); +} + +TopLoc_Location rotationZLocation(const double theAngle) +{ + gp_Trsf aTrsf; + aTrsf.SetRotation(gp_Ax1(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0)), theAngle); + return TopLoc_Location(aTrsf); +} + +bool locationsEquivalent(const TopLoc_Location& theActual, const TopLoc_Location& theExpected) +{ + const gp_Pnt aProbe(3.0, 5.0, 7.0); + const gp_Pnt anActualPoint = aProbe.Transformed(theActual.Transformation()); + const gp_Pnt anExpectedPoint = aProbe.Transformed(theExpected.Transformation()); + return anActualPoint.Distance(anExpectedPoint) <= Precision::Confusion(); +} + +void expectLocationsEquivalent(const TopLoc_Location& theActual, const TopLoc_Location& theExpected) +{ + const gp_Pnt aProbe(3.0, 5.0, 7.0); + const gp_Pnt anActualPoint = aProbe.Transformed(theActual.Transformation()); + const gp_Pnt anExpectedPoint = aProbe.Transformed(theExpected.Transformation()); + EXPECT_NEAR(anActualPoint.X(), anExpectedPoint.X(), Precision::Confusion()); + EXPECT_NEAR(anActualPoint.Y(), anExpectedPoint.Y(), Precision::Confusion()); + EXPECT_NEAR(anActualPoint.Z(), anExpectedPoint.Z(), Precision::Confusion()); +} +} // namespace + TEST(BRepGraph_ParentExplorerTest, FaceParents_All_CountAndOrder) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start()); ASSERT_TRUE(anExp.More()); @@ -40,6 +83,10 @@ TEST(BRepGraph_ParentExplorerTest, FaceParents_All_CountAndOrder) ASSERT_TRUE(anExp.More()); EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(BRepGraph_SolidId::Start())); + anExp.Next(); + ASSERT_TRUE(anExp.More()); + EXPECT_EQ(anExp.Current().DefId.NodeKind, BRepGraph_NodeId::Kind::Occurrence); + anExp.Next(); ASSERT_TRUE(anExp.More()); EXPECT_EQ(anExp.Current().DefId.NodeKind, BRepGraph_NodeId::Kind::Product); @@ -52,9 +99,9 @@ TEST(BRepGraph_ParentExplorerTest, FaceParents_TypedSolid_OneResult) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start(), BRepGraph_NodeId::Kind::Solid); ASSERT_TRUE(anExp.More()); @@ -68,9 +115,9 @@ TEST(BRepGraph_ParentExplorerTest, FaceParents_DirectParents_StopsAtImmediateShe { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start(), @@ -86,9 +133,9 @@ TEST(BRepGraph_ParentExplorerTest, FaceParents_DirectParents_ExposeChildAndRef) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start(), @@ -107,9 +154,9 @@ TEST(BRepGraph_ParentExplorerTest, AvoidKind_Solid_PrunesProducts) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start(), @@ -123,9 +170,9 @@ TEST(BRepGraph_ParentExplorerTest, AvoidKind_EmitBoundary_ReturnsSolidInsteadOfP { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start(), @@ -143,9 +190,9 @@ TEST(BRepGraph_ParentExplorerTest, AvoidKind_SameAsTarget_IsIgnored) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start(), @@ -163,9 +210,9 @@ TEST(BRepGraph_ParentExplorerTest, AllParents_AvoidSolid_PrunesProducts) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start(), @@ -182,9 +229,9 @@ TEST(BRepGraph_ParentExplorerTest, AllParents_AvoidSolidEmitBoundary_ReturnsShel { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_FaceId::Start(), @@ -205,13 +252,14 @@ TEST(BRepGraph_ParentExplorerTest, SharedProduct_ProductParentsKeepDistinctConte { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_ProductId aPart = - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); - const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + aGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(aPart.IsValid()); ASSERT_TRUE(anAssembly.IsValid()); @@ -219,10 +267,8 @@ TEST(BRepGraph_ParentExplorerTest, SharedProduct_ProductParentsKeepDistinctConte aT1.SetTranslation(gp_Vec(10.0, 0.0, 0.0)); gp_Trsf aT2; aT2.SetTranslation(gp_Vec(25.0, 0.0, 0.0)); - ASSERT_TRUE( - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location(aT1)).IsValid()); - ASSERT_TRUE( - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location(aT2)).IsValid()); + ASSERT_TRUE(aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location(aT1)).IsValid()); + ASSERT_TRUE(aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location(aT2)).IsValid()); int aPartCount = 0; TopLoc_Location aLoc1; @@ -252,49 +298,375 @@ TEST(BRepGraph_ParentExplorerTest, SharedProduct_ProductParentsKeepDistinctConte EXPECT_FALSE(aLoc1.IsEqual(aLoc2)); } -TEST(BRepGraph_ParentExplorerTest, ShapeRootProductParent_HasChildButNoRef) +TEST(BRepGraph_ParentExplorerTest, DeepProductOccurrenceChain_ComposesToTopProduct) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - // BRepGraph_Builder::Add() auto-creates a root Product for the shape root node. + const BRepGraph_ProductId aPart = BRepGraph_ProductId::Start(); + BRepGraph_ProductId aChildProduct = aPart; + double anExpectedX = 0.0; + BRepGraph_ProductId aTopProduct; + for (int aLevel = 1; aLevel <= 6; ++aLevel) + { + aTopProduct = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aTopProduct); + ASSERT_TRUE(aTopProduct.IsValid()); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(static_cast(aLevel), 0.0, 0.0)); + ASSERT_TRUE(aGraph.Editor() + .Products() + .Append(aTopProduct, aChildProduct, TopLoc_Location(aTrsf)) + .IsValid()); + anExpectedX += static_cast(aLevel); + aChildProduct = aTopProduct; + } + + bool foundTop = false; + for (BRepGraph_ParentExplorer anExp(aGraph, + BRepGraph_SolidId::Start(), + BRepGraph_NodeId::Kind::Product); + anExp.More(); + anExp.Next()) + { + if (anExp.Current().DefId != BRepGraph_NodeId(aTopProduct)) + { + continue; + } + foundTop = true; + EXPECT_NEAR(anExp.LeafLocation().Transformation().TranslationPart().X(), + anExpectedX, + Precision::Confusion()); + } + EXPECT_TRUE(foundTop); +} + +TEST(BRepGraph_ParentExplorerTest, DeepProductOccurrenceChain_ReachesNestedOccurrences) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(); + const BRepGraph_ProductId aSubAssembly = aGraph.Editor().Products().Add(); + const BRepGraph_ProductId aTopAssembly = aGraph.Editor().Products().Add(); + ASSERT_TRUE(aPart.IsValid()); + ASSERT_TRUE(aSubAssembly.IsValid()); + ASSERT_TRUE(aTopAssembly.IsValid()); + + const BRepGraph_OccurrenceId aPartOccurrence = + aGraph.Editor().Products().Append(aSubAssembly, aPart, TopLoc_Location()); + const BRepGraph_OccurrenceId aSubOccurrence = + aGraph.Editor().Products().Append(aTopAssembly, aSubAssembly, TopLoc_Location()); + ASSERT_TRUE(aPartOccurrence.IsValid()); + ASSERT_TRUE(aSubOccurrence.IsValid()); + + bool hasPartOccurrence = false; + bool hasSubOccurrence = false; + int aCount = 0; + for (BRepGraph_ParentExplorer anIt(aGraph, aPart, BRepGraph_NodeId::Kind::Occurrence); + anIt.More(); + anIt.Next()) + { + hasPartOccurrence |= anIt.Current().DefId == BRepGraph_NodeId(aPartOccurrence); + hasSubOccurrence |= anIt.Current().DefId == BRepGraph_NodeId(aSubOccurrence); + ++aCount; + } + + EXPECT_EQ(aCount, 2); + EXPECT_TRUE(hasPartOccurrence); + EXPECT_TRUE(hasSubOccurrence); +} + +TEST(BRepGraph_ParentExplorerTest, DeepCompoundChain_ReachesTopCompound) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_NodeId aChild = BRepGraph_SolidId::Start(); + BRepGraph_CompoundId aTopCompound; + for (int aLevel = 0; aLevel < 8; ++aLevel) + { + NCollection_LinearVector aChildren; + aChildren.Append(aChild); + aTopCompound = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aTopCompound.IsValid()); + aChild = aTopCompound; + } + + int aCompoundCount = 0; + bool foundTop = false; + for (BRepGraph_ParentExplorer anExp(aGraph, + BRepGraph_SolidId::Start(), + BRepGraph_NodeId::Kind::Compound); + anExp.More(); + anExp.Next()) + { + ++aCompoundCount; + foundTop |= anExp.Current().DefId == BRepGraph_NodeId(aTopCompound); + } + EXPECT_EQ(aCompoundCount, 8); + EXPECT_TRUE(foundTop); +} + +TEST(BRepGraph_ParentExplorerTest, VertexChildOfDeepCompoundChain_ReachesTopCompound) +{ + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph_VertexId aVertex = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 1.0e-7); + ASSERT_TRUE(aVertex.IsValid()); + + BRepGraph_NodeId aChild = aVertex; + BRepGraph_CompoundId aTopCompound; + double anExpectedX = 0.0; + for (int aLevel = 1; aLevel <= 4; ++aLevel) + { + NCollection_LinearVector aChildren; + aChildren.Append(aChild); + aTopCompound = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aTopCompound.IsValid()); + + const TopLoc_Location aStepLoc = translationLocation(static_cast(aLevel), 0.0, 0.0); + const NCollection_LinearVector& aChildRefs = + aGraph.Refs().Children().IdsOf(aTopCompound); + ASSERT_EQ(aChildRefs.Size(), 1); + aGraph.Editor().Gen().SetChildRefLocalLocation(aChildRefs.First(), aStepLoc); + anExpectedX += static_cast(aLevel); + + aChild = aTopCompound; + } + + int aCompoundCount = 0; + bool foundTop = false; + for (BRepGraph_ParentExplorer anExp(aGraph, aVertex, BRepGraph_NodeId::Kind::Compound); + anExp.More(); + anExp.Next()) + { + ++aCompoundCount; + if (anExp.Current().DefId == BRepGraph_NodeId(aTopCompound)) + { + foundTop = true; + EXPECT_NEAR(anExp.LeafLocation().Transformation().TranslationPart().X(), + anExpectedX, + Precision::Confusion()); + } + } + + EXPECT_EQ(aCompoundCount, 4); + EXPECT_TRUE(foundTop); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraph_ParentExplorerTest, MixedProductOccurrenceAndCompoundChain_PreservesLocations) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const TopLoc_Location aComp1Loc = translationLocation(2.0, 0.0, 0.0); + const TopLoc_Location aComp2Loc = rotationZLocation(0.25); + const TopLoc_Location aComp3Loc = translationLocation(0.0, 3.0, 0.0); + const TopLoc_Location anOcc1Loc = translationLocation(0.0, 0.0, 5.0); + const TopLoc_Location anOcc2Loc = rotationZLocation(-0.5); + + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_NodeId(BRepGraph_SolidId::Start())); + const BRepGraph_CompoundId aComp1 = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aComp1.IsValid()); + ASSERT_EQ(aGraph.Refs().Children().IdsOf(aComp1).Size(), 1); + aGraph.Editor().Gen().SetChildRefLocalLocation(aGraph.Refs().Children().IdsOf(aComp1).First(), + aComp1Loc); + + aChildren.Clear(); + aChildren.Append(BRepGraph_NodeId(aComp1)); + const BRepGraph_CompoundId aComp2 = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aComp2.IsValid()); + ASSERT_EQ(aGraph.Refs().Children().IdsOf(aComp2).Size(), 1); + aGraph.Editor().Gen().SetChildRefLocalLocation(aGraph.Refs().Children().IdsOf(aComp2).First(), + aComp2Loc); + + aChildren.Clear(); + aChildren.Append(BRepGraph_NodeId(aComp2)); + const BRepGraph_CompoundId aComp3 = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aComp3.IsValid()); + ASSERT_EQ(aGraph.Refs().Children().IdsOf(aComp3).Size(), 1); + aGraph.Editor().Gen().SetChildRefLocalLocation(aGraph.Refs().Children().IdsOf(aComp3).First(), + aComp3Loc); + + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(aComp3); + aGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId aMidAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aMidAssembly); + const BRepGraph_ProductId aTopAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aTopAssembly); + ASSERT_TRUE(aPart.IsValid()); + ASSERT_TRUE(aMidAssembly.IsValid()); + ASSERT_TRUE(aTopAssembly.IsValid()); + + const BRepGraph_OccurrenceId anOcc1 = + aGraph.Editor().Products().Append(aMidAssembly, aPart, anOcc1Loc); + const BRepGraph_OccurrenceId anOcc2 = + aGraph.Editor().Products().Append(aTopAssembly, aMidAssembly, anOcc2Loc); + ASSERT_TRUE(anOcc1.IsValid()); + ASSERT_TRUE(anOcc2.IsValid()); + + const TopLoc_Location aToPart = anOcc2Loc * anOcc1Loc; + const TopLoc_Location aToComp3 = aToPart; + const TopLoc_Location aToComp2 = aToComp3 * aComp3Loc; + const TopLoc_Location aToComp1 = aToComp2 * aComp2Loc; + const TopLoc_Location aToSolid = aToComp1 * aComp1Loc; + + bool hasTopAssembly = false; + bool hasMidAssembly = false; + bool hasPart = false; + bool hasOcc2 = false; + bool hasOcc1 = false; + bool hasComp3 = false; + bool hasComp2 = false; + bool hasComp1 = false; + + for (BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_SolidId::Start()); anExp.More(); + anExp.Next()) + { + if (!locationsEquivalent(anExp.LeafLocation(), aToSolid)) + { + continue; + } + + const BRepGraph_NodeId aCurrent = anExp.Current().DefId; + if (aCurrent == BRepGraph_NodeId(aTopAssembly)) + { + hasTopAssembly = true; + expectLocationsEquivalent(anExp.Current().Location, TopLoc_Location()); + } + else if (aCurrent == BRepGraph_NodeId(anOcc2)) + { + hasOcc2 = true; + expectLocationsEquivalent(anExp.Current().Location, anOcc2Loc); + } + else if (aCurrent == BRepGraph_NodeId(aMidAssembly)) + { + hasMidAssembly = true; + expectLocationsEquivalent(anExp.Current().Location, anOcc2Loc); + } + else if (aCurrent == BRepGraph_NodeId(anOcc1)) + { + hasOcc1 = true; + expectLocationsEquivalent(anExp.Current().Location, aToPart); + } + else if (aCurrent == BRepGraph_NodeId(aPart)) + { + hasPart = true; + expectLocationsEquivalent(anExp.Current().Location, aToPart); + } + else if (aCurrent == BRepGraph_NodeId(aComp3)) + { + hasComp3 = true; + expectLocationsEquivalent(anExp.Current().Location, aToComp3); + } + else if (aCurrent == BRepGraph_NodeId(aComp2)) + { + hasComp2 = true; + expectLocationsEquivalent(anExp.Current().Location, aToComp2); + } + else if (aCurrent == BRepGraph_NodeId(aComp1)) + { + hasComp1 = true; + expectLocationsEquivalent(anExp.Current().Location, aToComp1); + } + } + + EXPECT_TRUE(hasTopAssembly); + EXPECT_TRUE(hasMidAssembly); + EXPECT_TRUE(hasPart); + EXPECT_TRUE(hasOcc2); + EXPECT_TRUE(hasOcc1); + EXPECT_TRUE(hasComp3); + EXPECT_TRUE(hasComp2); + EXPECT_TRUE(hasComp1); +} + +TEST(BRepGraph_ParentExplorerTest, ShapeRootDirectParent_IsOccurrenceNotProductShortcut) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + // BRepGraph::ShapesView::Add() auto-creates a root Product for the shape root node. // Use that product instead of creating a duplicate. ASSERT_GT(aGraph.Topo().Products().Nb(), 0); const BRepGraph_ProductId aPart(0); + ASSERT_EQ(aGraph.Topo().Products().NbComponents(aPart), 1); + const BRepGraph_OccurrenceId aRootOccurrence = aGraph.Topo().Products().Component(aPart, 0); + ASSERT_TRUE(aRootOccurrence.IsValid()); BRepGraph_ParentExplorer anExp(aGraph, BRepGraph_SolidId::Start(), - BRepGraph_NodeId::Kind::Product, BRepGraph_ParentExplorer::TraversalMode::DirectParents); ASSERT_TRUE(anExp.More()); - EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(aPart)); + EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(aRootOccurrence)); EXPECT_EQ(anExp.CurrentChild(), BRepGraph_NodeId(BRepGraph_SolidId::Start())); EXPECT_EQ(anExp.CurrentLinkKind(), BRepGraph_ParentExplorer::LinkKind::Structural); EXPECT_FALSE(anExp.CurrentRef().IsValid()); anExp.Next(); EXPECT_FALSE(anExp.More()); + + BRepGraph_ParentExplorer aProductExp(aGraph, + BRepGraph_SolidId::Start(), + BRepGraph_NodeId::Kind::Product); + ASSERT_TRUE(aProductExp.More()); + EXPECT_EQ(aProductExp.Current().DefId, BRepGraph_NodeId(aPart)); +} + +TEST(BRepGraph_ParentExplorerTest, TargetProductAvoidCompound_StopsAtCompound) +{ + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + ASSERT_GT(aGraph.Topo().Products().Nb(), 0); + + BRepGraph_ParentExplorer anExp(aGraph, + BRepGraph_SolidId::Start(), + BRepGraph_NodeId::Kind::Product, + BRepGraph_NodeId::Kind::Compound, + false); + EXPECT_FALSE(anExp.More()); } TEST(BRepGraph_ParentExplorerTest, OccurrenceParent_ExposeOccurrenceRef) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_ProductId aPart = - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); - const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + aGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(aPart.IsValid()); ASSERT_TRUE(anAssembly.IsValid()); const BRepGraph_OccurrenceId anOccurrence = - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location()); + aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()); ASSERT_TRUE(anOccurrence.IsValid()); BRepGraph_ParentExplorer anExp(aGraph, @@ -311,22 +683,67 @@ TEST(BRepGraph_ParentExplorerTest, OccurrenceParent_ExposeOccurrenceRef) EXPECT_EQ(anExp.CurrentRef(), BRepGraph_RefId(anOccurrenceRefId)); } +TEST(BRepGraph_ParentExplorerTest, OccurrenceParents_SetRefOccurrenceDefRejectsSharing) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + aGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId anAssembly1 = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly1); + const BRepGraph_ProductId anAssembly2 = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly2); + ASSERT_TRUE(aPart.IsValid()); + ASSERT_TRUE(anAssembly1.IsValid()); + ASSERT_TRUE(anAssembly2.IsValid()); + + BRepGraph_OccurrenceRefId anOccRef1; + BRepGraph_OccurrenceRefId anOccRef2; + const BRepGraph_OccurrenceId anOcc1 = aGraph.Editor().Products().Append(anAssembly1, + aPart, + TopLoc_Location(), + BRepGraph_OccurrenceId(), + &anOccRef1); + const BRepGraph_OccurrenceId anOcc2 = aGraph.Editor().Products().Append(anAssembly2, + aPart, + TopLoc_Location(), + BRepGraph_OccurrenceId(), + &anOccRef2); + ASSERT_TRUE(anOcc1.IsValid()); + ASSERT_TRUE(anOcc2.IsValid()); + ASSERT_TRUE(anOccRef1.IsValid()); + ASSERT_TRUE(anOccRef2.IsValid()); + + aGraph.Editor().Occurrences().SetRefChildOccurrenceId(anOccRef2, anOcc1); + EXPECT_EQ(aGraph.Refs().Occurrences().Entry(anOccRef2).ChildOccurrenceId, anOcc2); + EXPECT_TRUE(aGraph.ValidateRelations()); + + const BRepGraph_Validate::Result anAuditResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_TRUE(anAuditResult.IsValid()); +} + TEST(BRepGraph_ParentExplorerTest, ProductParents_ImmediateOccurrence_IsStructural) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_ProductId aPart = - aGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); - const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + aGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(aPart.IsValid()); ASSERT_TRUE(anAssembly.IsValid()); const BRepGraph_OccurrenceId anOccurrence = - aGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location()); + aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()); ASSERT_TRUE(anOccurrence.IsValid()); BRepGraph_ParentExplorer anExp(aGraph, @@ -343,41 +760,87 @@ TEST(BRepGraph_ParentExplorerTest, CoEdgeParents_ImmediateWireIsVisible) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const NCollection_DynamicArray& aWireRefIds = + const NCollection_LinearVector& aWireRefIds = aGraph.Refs().Wires().IdsOf(BRepGraph_FaceId::Start()); - ASSERT_GT(aWireRefIds.Length(), 0); - const BRepGraph_WireId aWireId = aGraph.Refs().Wires().Entry(aWireRefIds.Value(0)).WireDefId; + ASSERT_GT(aWireRefIds.Size(), 0); + const BRepGraph_WireId aWireId = aGraph.Refs().Wires().Entry(aWireRefIds.Value(0)).ChildWireId; - const NCollection_DynamicArray& aCoEdgeRefIds = - aGraph.Refs().CoEdges().IdsOf(aWireId); - ASSERT_GT(aCoEdgeRefIds.Length(), 0); - const BRepGraph_CoEdgeId aCoEdgeId = - aGraph.Refs().CoEdges().Entry(aCoEdgeRefIds.Value(0)).CoEdgeDefId; + const NCollection_LinearVector& aCoEdgeIds = + aGraph.Topo().Wires().Relations(aWireId).CoEdgeIds; + ASSERT_GT(aCoEdgeIds.Size(), 0); + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIds.Value(0); BRepGraph_ParentExplorer anExp(aGraph, aCoEdgeId); ASSERT_TRUE(anExp.More()); EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(aWireId)); + EXPECT_EQ(anExp.CurrentLinkKind(), BRepGraph_ParentExplorer::LinkKind::Structural); + EXPECT_FALSE(anExp.CurrentRef().IsValid()); anExp.Next(); ASSERT_TRUE(anExp.More()); EXPECT_EQ(anExp.Current().DefId, BRepGraph_NodeId(BRepGraph_FaceId::Start())); } +TEST(BRepGraph_ParentExplorerTest, CoEdgeChildOfCompound_ImmediateCompoundIsVisible) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_WireId aWireId = + aGraph.Refs() + .Wires() + .Entry(aGraph.Refs().Wires().IdsOf(BRepGraph_FaceId::Start()).First()) + .ChildWireId; + const BRepGraph_CoEdgeId aCoEdgeId = aGraph.Topo().Wires().Relations(aWireId).CoEdgeIds.First(); + + NCollection_LinearVector aChildren; + aChildren.Append(aCoEdgeId); + const BRepGraph_CompoundId aCompound = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + bool hasWire = false; + bool hasCompound = false; + for (BRepGraph_ParentExplorer anExp(aGraph, + aCoEdgeId, + BRepGraph_ParentExplorer::TraversalMode::DirectParents); + anExp.More(); + anExp.Next()) + { + if (anExp.Current().DefId == BRepGraph_NodeId(aWireId)) + { + hasWire = true; + } + if (anExp.Current().DefId == BRepGraph_NodeId(aCompound)) + { + hasCompound = true; + EXPECT_EQ(anExp.CurrentLinkKind(), BRepGraph_ParentExplorer::LinkKind::Reference); + EXPECT_TRUE(anExp.CurrentRef().IsValid()); + } + } + + EXPECT_TRUE(hasWire); + EXPECT_TRUE(hasCompound); +} + TEST(BRepGraph_ParentExplorerTest, ProductRoot_HasNoParents) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_ProductId aRootProduct = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aRootProduct = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootProduct); ASSERT_TRUE(aRootProduct.IsValid()); BRepGraph_ParentExplorer anExp(aGraph, aRootProduct); EXPECT_FALSE(anExp.More()); -} \ No newline at end of file +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_PermissionUpdate_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_PermissionUpdate_Test.cxx index a0b98424de..04f9994446 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_PermissionUpdate_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_PermissionUpdate_Test.cxx @@ -12,13 +12,14 @@ // commercial license or contractual agreement. #include -#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -27,10 +28,12 @@ #include #include #include -#include +#include #include #include +#include + #include namespace @@ -40,8 +43,8 @@ BRepGraph makeBoxGraph() { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aRes = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); return aGraph; } @@ -52,32 +55,111 @@ BRepGraph_EdgeId makeSelfLoopEdge(BRepGraph& theGraph) return theGraph.Editor().Edges().Add(aV, aV, aLine, 0.0, 1.0, 1.0e-7); } +template +bool containsId(const NCollection_LinearVector& theIds, const IdT theId) +{ + for (const IdT& anId : theIds) + { + if (anId == theId) + { + return true; + } + } + return false; +} + +template +bool containsCurrentId(IteratorT theIterator, const IdT theId) +{ + for (; theIterator.More(); theIterator.Next()) + { + if (theIterator.CurrentId() == theId) + { + return true; + } + } + return false; +} + } // namespace -// B1: RemoveRef on a Product->Occurrence ref must unbind the reverse-index entry -// keyed by the referenced product (OccurrenceDef::ChildDefId), not by the parent. +TEST(BRepGraph_PermissionUpdateTest, RemoveRef_FaceRef_InvalidatesOwningShellBeforeUnbind) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_RefsFaceOfShell aFaceRefs(aGraph, BRepGraph_ShellId::Start()); + ASSERT_TRUE(aFaceRefs.More()); + const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.CurrentId(); + + const uint32_t aShellSubtreeGenBefore = + aGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).SubtreeGen; + ASSERT_TRUE(aGraph.Editor().Gen().RemoveRef(aFaceRefId)); + + EXPECT_TRUE(aFaceRefId.IsRemoved(aGraph)); + EXPECT_GT(aGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).SubtreeGen, + aShellSubtreeGenBefore); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraph_PermissionUpdateTest, RemoveShell_SameSolidSiblingRef_PreservesRelations) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + BRepGraph_RefsShellOfSolid aShellRefs(aGraph, aSolidId); + ASSERT_TRUE(aShellRefs.More()); + const BRepGraph_ShellRefId aFirstRefId = aShellRefs.CurrentId(); + const BRepGraph_ShellId aShellId = aGraph.Refs().Shells().Entry(aFirstRefId).ChildShellId; + + const BRepGraph_ShellRefId aSecondRefId = + aGraph.Editor().Solids().Append(aSolidId, aShellId, TopAbs_REVERSED); + ASSERT_TRUE(aSecondRefId.IsValid()); + ASSERT_NE(aSecondRefId, aFirstRefId); + ASSERT_TRUE(containsCurrentId( + BRepGraph_SolidsOfShell(aGraph, aGraph.Topo().Shells().Relations(aShellId).ParentShellRefIds), + aSolidId)); + + ASSERT_TRUE(aGraph.Editor().Solids().RemoveShell(aSolidId, aFirstRefId)); + + EXPECT_TRUE(aFirstRefId.IsRemoved(aGraph)); + EXPECT_FALSE(aSecondRefId.IsRemoved(aGraph)); + EXPECT_TRUE(containsCurrentId( + BRepGraph_SolidsOfShell(aGraph, aGraph.Topo().Shells().Relations(aShellId).ParentShellRefIds), + aSolidId)) + << "A sibling ShellRef still connects the same Solid to the same Shell"; + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +// B1: RemoveRef on a Product->Occurrence ref must unbind the relation-table entry +// keyed by the referenced product (OccurrenceDef::ChildNodeId), not by the parent. TEST(BRepGraph_PermissionUpdateTest, RemoveRef_OccurrenceRef_PreservesProductToOccurrencesIndex) { // Targeted regression for the wrong-key Unbind in GenOps::RemoveRef. Walks the - // reverse index directly because BRepGraphInc_ReverseIndex::Validate currently + // relation tables directly because BRepGraphInc_Relations::Validate currently // covers only topology references. BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); - const BRepGraph_ProductId aParent = aProds.CreateEmptyProduct(); - const BRepGraph_ProductId aChild = aProds.CreateEmptyProduct(); + const BRepGraph_ProductId aParent = aProds.Add(); + aProds.AppendDocumentRoot(aParent); + const BRepGraph_ProductId aChild = aProds.Add(); + aProds.AppendDocumentRoot(aChild); ASSERT_TRUE(aParent.IsValid()); ASSERT_TRUE(aChild.IsValid()); BRepGraph_OccurrenceRefId aOccRefId; const BRepGraph_OccurrenceId aOccId = - aProds.LinkProducts(aParent, aChild, TopLoc_Location(), BRepGraph_OccurrenceId(), &aOccRefId); + aProds.Append(aParent, aChild, TopLoc_Location(), BRepGraph_OccurrenceId(), &aOccRefId); ASSERT_TRUE(aOccId.IsValid()); ASSERT_TRUE(aOccRefId.IsValid()); bool aFoundBefore = false; - for (const BRepGraph_OccurrenceId& anId : aGraph.Topo().Products().Instances(aChild)) + for (const BRepGraph_OccurrenceId& anId : + BRepGraph_OccurrencesOfChild(aGraph, + aGraph.Topo().Gen().OccurrenceRefIds(BRepGraph_NodeId(aChild)))) { if (anId == aOccId) { @@ -88,22 +170,26 @@ TEST(BRepGraph_PermissionUpdateTest, RemoveRef_OccurrenceRef_PreservesProductToO EXPECT_TRUE(aGraph.Editor().Gen().RemoveRef(BRepGraph_RefId(aOccRefId))); - for (const BRepGraph_OccurrenceId& anId : aGraph.Topo().Products().Instances(aChild)) + for (const BRepGraph_OccurrenceId& anId : + BRepGraph_OccurrencesOfChild(aGraph, + aGraph.Topo().Gen().OccurrenceRefIds(BRepGraph_NodeId(aChild)))) { EXPECT_NE(anId, aOccId) << "RemoveRef must drop the referenced-product entry"; } // The wrong-key bug would also leave a stale entry under the parent product id. - for (const BRepGraph_OccurrenceId& anId : aGraph.Topo().Products().Instances(aParent)) + for (const BRepGraph_OccurrenceId& anId : BRepGraph_OccurrencesOfChild( + aGraph, + aGraph.Topo().Gen().OccurrenceRefIds(BRepGraph_NodeId(aParent)))) { EXPECT_NE(anId, aOccId) << "Bug: Unbind happened against parent product key"; } } -// B2: removing one CoEdgeRef must leave the wire's other usages of the same edge -// reflected in the deduplicated Edge->Wire reverse index. +// B2: removing one coedge usage must leave the wire's other usages of the same edge +// reflected in the deduplicated Edge->Wire relation tables. // -// Constructs a wire with two CoEdgeRefs whose CoEdges share the same edge so the +// Constructs a wire with two coedge usages whose CoEdges share the same edge so the // dedup-aware unbind path is exercised; removing one ref must keep the // (Edge -> Wire) reverse entry alive because the sibling still references it. TEST(BRepGraph_PermissionUpdateTest, RemoveRef_CoEdge_PreservesEdgeToWireWhenSiblingPresent) @@ -117,20 +203,20 @@ TEST(BRepGraph_PermissionUpdateTest, RemoveRef_CoEdge_PreservesEdgeToWireWhenSib const BRepGraph_EdgeId aEdge = aGraph.Editor().Edges().Add(aV1, aV2, aLine, 0.0, 1.0, 1.0e-7); ASSERT_TRUE(aEdge.IsValid()); - NCollection_DynamicArray> anEdges; - anEdges.Append(std::make_pair(aEdge, TopAbs_FORWARD)); - anEdges.Append(std::make_pair(aEdge, TopAbs_REVERSED)); - const BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(anEdges); + NCollection_LinearVector aCoEdgeIds; + aCoEdgeIds.Append(aGraph.Editor().CoEdges().Add(aEdge, TopAbs_FORWARD)); + aCoEdgeIds.Append(aGraph.Editor().CoEdges().Add(aEdge, TopAbs_REVERSED)); + const BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aCoEdgeIds.ToArray1()); ASSERT_TRUE(aWireId.IsValid()); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); - const BRepGraphInc::WireDef& aWireDef = aGraph.Topo().Wires().Definition(aWireId); - ASSERT_EQ(aWireDef.CoEdgeRefIds.Size(), 2u); - const BRepGraph_CoEdgeRefId aFirstRef = aWireDef.CoEdgeRefIds.First(); - ASSERT_TRUE(aFirstRef.IsValid()); + const BRepGraphInc::WireRelations& aWireRelations = aGraph.Topo().Wires().Relations(aWireId); + ASSERT_EQ(aWireRelations.CoEdgeIds.Size(), 2u); + const BRepGraph_CoEdgeId aFirstCoEdge = aWireRelations.CoEdgeIds.First(); + ASSERT_TRUE(aFirstCoEdge.IsValid()); auto containsWire = [&](const BRepGraph_EdgeId theE) -> bool { - for (const BRepGraph_WireId& aW : aGraph.Topo().Edges().Wires(theE)) + for (const BRepGraph_WireId& aW : aGraph.Topo().Edges().WiresOf(theE)) { if (aW == aWireId) { @@ -141,21 +227,21 @@ TEST(BRepGraph_PermissionUpdateTest, RemoveRef_CoEdge_PreservesEdgeToWireWhenSib }; ASSERT_TRUE(containsWire(aEdge)) << "Edge->Wire reverse must be present before removal"; - EXPECT_TRUE(aGraph.Editor().Gen().RemoveRef(BRepGraph_RefId(aFirstRef))); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.Editor().Wires().RemoveCoEdge(aWireId, aFirstCoEdge)); + EXPECT_TRUE(aGraph.ValidateRelations()); EXPECT_TRUE(containsWire(aEdge)) - << "Sibling CoEdgeRef still references the edge: Edge->Wire entry must survive"; + << "Sibling coedge still references the edge: Edge->Wire entry must survive"; } -// B3: SetRefVertexDefId on a self-loop edge (start == end == V_old) must keep +// B3: SetRefChildVertexId on a self-loop edge (start == end == V_old) must keep // the V_old->edge entry while the sibling slot still references it. -TEST(BRepGraph_PermissionUpdateTest, SetRefVertexDefId_SelfLoopSibling_KeepsRevIndexValid) +TEST(BRepGraph_PermissionUpdateTest, SetRefChildVertexId_SelfLoopSibling_KeepsRevIndexValid) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_EdgeId aEdge = makeSelfLoopEdge(aGraph); ASSERT_TRUE(aEdge.IsValid()); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); const BRepGraphInc::EdgeDef& aDef = aGraph.Topo().Edges().Definition(aEdge); const BRepGraph_VertexRefId aStartRefId = aDef.StartVertexRefId; @@ -163,8 +249,8 @@ TEST(BRepGraph_PermissionUpdateTest, SetRefVertexDefId_SelfLoopSibling_KeepsRevI const BRepGraph_VertexId aNewV = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); - aGraph.Editor().Vertices().SetRefVertexDefId(aStartRefId, aNewV); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + aGraph.Editor().Vertices().SetRefChildVertexId(aStartRefId, aNewV); + EXPECT_TRUE(aGraph.ValidateRelations()); // EndVertexRef still references the original vertex, so the (Vold -> aEdge) // reverse entry must survive the start-side rebind. @@ -173,7 +259,7 @@ TEST(BRepGraph_PermissionUpdateTest, SetRefVertexDefId_SelfLoopSibling_KeepsRevI ? aGraph.Refs() .Vertices() .Entry(aGraph.Topo().Edges().Definition(aEdge).EndVertexRefId) - .VertexDefId + .ChildVertexId : BRepGraph_VertexId(); ASSERT_TRUE(aOldV.IsValid()); bool aOldStillIndexed = false; @@ -192,26 +278,28 @@ TEST(BRepGraph_PermissionUpdateTest, SetRefVertexDefId_SelfLoopSibling_KeepsRevI TEST(BRepGraph_PermissionUpdateTest, CopyNode_SelfReferencingCompound_Terminates) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_VertexId aV = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); - NCollection_DynamicArray aChildren; + NCollection_LinearVector aChildren; aChildren.Append(BRepGraph_NodeId(aV)); - const BRepGraph_CompoundId aRoot = aGraph.Editor().Compounds().Add(aChildren); + const BRepGraph_CompoundId aRoot = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); ASSERT_TRUE(aRoot.IsValid()); // Splice the compound into itself by rewriting its first child ref. - const BRepGraphInc::CompoundDef& aDef = aGraph.Topo().Compounds().Definition(aRoot); - ASSERT_FALSE(aDef.ChildRefIds.IsEmpty()); - const BRepGraph_ChildRefId aChildRefId = aDef.ChildRefIds.First(); - aGraph.Editor().Gen().SetChildRefChildDefId(aChildRefId, BRepGraph_NodeId(aRoot)); + const BRepGraphInc::CompoundRelations& aRelations = aGraph.Topo().Compounds().Relations(aRoot); + ASSERT_FALSE(aRelations.ChildRefIds.IsEmpty()); + const BRepGraph_ChildRefId aChildRefId = aRelations.ChildRefIds.First(); + aGraph.Editor().Gen().SetChildRefChildNodeId(aChildRefId, BRepGraph_NodeId(aRoot)); - const BRepGraph aCopy = BRepGraph_Copy::CopyNode(aGraph, - BRepGraph_NodeId(aRoot), - /*copyGeom*/ true, - /*copyMesh*/ false, - /*reserveCache*/ false); - EXPECT_TRUE(aCopy.IsDone()); - EXPECT_TRUE(aCopy.ValidateReverseIndex()); + BRepGraph aCopy; + const BRepGraph_NodeId aRootId = BRepGraph_Copy::CopyNode(aGraph, + aCopy, + BRepGraph_NodeId(aRoot), + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + EXPECT_TRUE(aRootId.IsValid()); + EXPECT_FALSE(aCopy.IsEmpty()); + EXPECT_TRUE(aCopy.ValidateRelations()); } // TransformNode with copyGeom on an assembly node must reject (returns invalid graph) @@ -219,35 +307,50 @@ TEST(BRepGraph_PermissionUpdateTest, CopyNode_SelfReferencingCompound_Terminates TEST(BRepGraph_PermissionUpdateTest, TransformNode_AssemblyWithCopyGeom_Rejected) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); - const BRepGraph_ProductId aProd = aGraph.Editor().Products().CreateEmptyProduct(); + ASSERT_FALSE(aGraph.IsEmpty()); + const BRepGraph_ProductId aProd = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aProd); ASSERT_TRUE(aProd.IsValid()); gp_Trsf aT; aT.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); - const BRepGraph aResult = - BRepGraph_Transform::TransformNode(aGraph, BRepGraph_NodeId(aProd), aT, true, false); - EXPECT_FALSE(aResult.IsDone()); + BRepGraph aResult; + const BRepGraph_NodeId aRootId = + BRepGraph_Transform::TransformNode(aGraph, + aResult, + BRepGraph_NodeId(aProd), + aT, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + EXPECT_FALSE(aRootId.IsValid()); } // Companion to the above: Occurrence node with copyGeom must be rejected too. TEST(BRepGraph_PermissionUpdateTest, TransformNode_OccurrenceWithCopyGeom_Rejected) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); - const BRepGraph_ProductId aParent = aProds.CreateEmptyProduct(); - const BRepGraph_ProductId aChild = aProds.CreateEmptyProduct(); + const BRepGraph_ProductId aParent = aProds.Add(); + aProds.AppendDocumentRoot(aParent); + const BRepGraph_ProductId aChild = aProds.Add(); + aProds.AppendDocumentRoot(aChild); ASSERT_TRUE(aParent.IsValid()); ASSERT_TRUE(aChild.IsValid()); - const BRepGraph_OccurrenceId aOccId = aProds.LinkProducts(aParent, aChild, TopLoc_Location()); + const BRepGraph_OccurrenceId aOccId = aProds.Append(aParent, aChild, TopLoc_Location()); ASSERT_TRUE(aOccId.IsValid()); gp_Trsf aT; aT.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); - const BRepGraph aResult = - BRepGraph_Transform::TransformNode(aGraph, BRepGraph_NodeId(aOccId), aT, true, false); - EXPECT_FALSE(aResult.IsDone()); + BRepGraph aResult; + const BRepGraph_NodeId aRootId = + BRepGraph_Transform::TransformNode(aGraph, + aResult, + BRepGraph_NodeId(aOccId), + aT, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + EXPECT_FALSE(aRootId.IsValid()); } // LinkProducts with the unified signature returns the freshly inserted OccurrenceRef @@ -255,62 +358,141 @@ TEST(BRepGraph_PermissionUpdateTest, TransformNode_OccurrenceWithCopyGeom_Reject TEST(BRepGraph_PermissionUpdateTest, LinkProducts_OutOccurrenceRefId_Populated) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); - const BRepGraph_ProductId aParent = aProds.CreateEmptyProduct(); - const BRepGraph_ProductId aChild = aProds.CreateEmptyProduct(); + const BRepGraph_ProductId aParent = aProds.Add(); + aProds.AppendDocumentRoot(aParent); + const BRepGraph_ProductId aChild = aProds.Add(); + aProds.AppendDocumentRoot(aChild); ASSERT_TRUE(aParent.IsValid()); ASSERT_TRUE(aChild.IsValid()); BRepGraph_OccurrenceRefId aOccRefId; const BRepGraph_OccurrenceId aOccId = - aProds.LinkProducts(aParent, aChild, TopLoc_Location(), BRepGraph_OccurrenceId(), &aOccRefId); + aProds.Append(aParent, aChild, TopLoc_Location(), BRepGraph_OccurrenceId(), &aOccRefId); ASSERT_TRUE(aOccId.IsValid()); ASSERT_TRUE(aOccRefId.IsValid()); - EXPECT_EQ(aGraph.Refs().Occurrences().Entry(aOccRefId).OccurrenceDefId, aOccId); + EXPECT_EQ(aGraph.Refs().Occurrences().Entry(aOccRefId).ChildOccurrenceId, aOccId); // Default out-pointer = nullptr is also legal. - const BRepGraph_OccurrenceId aOccId2 = aProds.LinkProducts(aParent, aChild, TopLoc_Location()); + const BRepGraph_OccurrenceId aOccId2 = aProds.Append(aParent, aChild, TopLoc_Location()); EXPECT_TRUE(aOccId2.IsValid()); } +TEST(BRepGraph_PermissionUpdateTest, SetOccurrenceChildNodeId_RejectsOccurrenceChild) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); + const BRepGraph_ProductId aParent = aProds.Add(); + aProds.AppendDocumentRoot(aParent); + const BRepGraph_ProductId aChild = aProds.Add(); + aProds.AppendDocumentRoot(aChild); + ASSERT_TRUE(aParent.IsValid()); + ASSERT_TRUE(aChild.IsValid()); + + const BRepGraph_OccurrenceId aOccId = aProds.Append(aParent, aChild, TopLoc_Location()); + ASSERT_TRUE(aOccId.IsValid()); + const BRepGraph_NodeId anOldChild = aGraph.Topo().Occurrences().Definition(aOccId).ChildNodeId; + + aGraph.Editor().Occurrences().SetChildNodeId(aOccId, BRepGraph_NodeId(aOccId)); + + EXPECT_EQ(aGraph.Topo().Occurrences().Definition(aOccId).ChildNodeId, anOldChild); + EXPECT_TRUE(containsCurrentId( + BRepGraph_OccurrencesOfChild(aGraph, aGraph.Topo().Gen().OccurrenceRefIds(anOldChild)), + aOccId)); + EXPECT_FALSE(containsCurrentId( + BRepGraph_OccurrencesOfChild(aGraph, + aGraph.Topo().Gen().OccurrenceRefIds(BRepGraph_NodeId(aOccId))), + aOccId)); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraph_PermissionUpdateTest, SetOccurrenceChildNodeId_InvalidOccurrenceNoOp) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); + const BRepGraph_ProductId aChild = aProds.Add(); + aProds.AppendDocumentRoot(aChild); + ASSERT_TRUE(aChild.IsValid()); + + EXPECT_NO_THROW(aGraph.Editor().Occurrences().SetChildNodeId(BRepGraph_OccurrenceId(100000), + BRepGraph_NodeId(aChild))); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST(BRepGraph_PermissionUpdateTest, SetOccurrenceChildNodeId_MutGuardRejectsRemovedChild) +{ + BRepGraph aGraph = makeBoxGraph(); + ASSERT_FALSE(aGraph.IsEmpty()); + BRepGraph::EditorView::ProductOps& aProds = aGraph.Editor().Products(); + const BRepGraph_ProductId aParent = aProds.Add(); + aProds.AppendDocumentRoot(aParent); + const BRepGraph_ProductId aChild = aProds.Add(); + aProds.AppendDocumentRoot(aChild); + const BRepGraph_ProductId aRemovedChild = aProds.Add(); + aProds.AppendDocumentRoot(aRemovedChild); + ASSERT_TRUE(aParent.IsValid()); + ASSERT_TRUE(aChild.IsValid()); + ASSERT_TRUE(aRemovedChild.IsValid()); + + const BRepGraph_OccurrenceId aOccId = aProds.Append(aParent, aChild, TopLoc_Location()); + ASSERT_TRUE(aOccId.IsValid()); + const BRepGraph_NodeId anOldChild = aGraph.Topo().Occurrences().Definition(aOccId).ChildNodeId; + aGraph.Editor().Gen().RemoveNode(aRemovedChild); + + { + BRepGraph_MutGuard aMut = + aGraph.Editor().Occurrences().Mut(aOccId); + aGraph.Editor().Occurrences().SetChildNodeId(aMut, BRepGraph_NodeId(aRemovedChild)); + EXPECT_FALSE(aMut.IsDirty()); + } + + EXPECT_EQ(aGraph.Topo().Occurrences().Definition(aOccId).ChildNodeId, anOldChild); + EXPECT_TRUE(containsCurrentId( + BRepGraph_OccurrencesOfChild(aGraph, aGraph.Topo().Gen().OccurrenceRefIds(anOldChild)), + aOccId)); + EXPECT_FALSE(containsCurrentId(BRepGraph_OccurrencesOfChild(aGraph, + aGraph.Topo().Gen().OccurrenceRefIds( + BRepGraph_NodeId(aRemovedChild))), + aOccId)); + EXPECT_TRUE(aGraph.ValidateRelations()); +} + // MoveRef: a pure rotation/translation must succeed; a scaled trsf must fail with no // state change. -TEST(BRepGraph_PermissionUpdateTest, MoveRef_ScaledTrsf_RejectedWithoutMutation) +TEST(BRepGraph_PermissionUpdateTest, MoveRef_ChildRefScaledTrsf_RejectedWithoutMutation) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); - // Find any face ref to test against. - BRepGraph_FaceRefId aFaceRef; - for (BRepGraph_FullFaceRefIterator aIt(aGraph); aIt.More(); aIt.Next()) - { - aFaceRef = aIt.CurrentId(); - break; - } - ASSERT_TRUE(aFaceRef.IsValid()); - - const TopLoc_Location aBefore = aGraph.Refs().Faces().Entry(aFaceRef).LocalLocation; + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_NodeId(BRepGraph_SolidId::Start())); + const BRepGraph_CompoundId aCompound = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + ASSERT_EQ(aGraph.Refs().Children().IdsOf(aCompound).Size(), 1); + const BRepGraph_ChildRefId aChildRef = aGraph.Refs().Children().IdsOf(aCompound).First(); gp_Trsf aScaled; aScaled.SetScaleFactor(2.0); - EXPECT_FALSE(BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aFaceRef), aScaled)); - EXPECT_TRUE(aGraph.Refs().Faces().Entry(aFaceRef).LocalLocation == aBefore); + EXPECT_FALSE(BRepGraph_Transform::MoveRef(aGraph, aChildRef, aScaled)); + EXPECT_TRUE(aGraph.Refs().Children().Entry(aChildRef).LocalLocation.IsIdentity()); gp_Trsf aTrans; aTrans.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); - EXPECT_TRUE(BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aFaceRef), aTrans)); - EXPECT_FALSE(aGraph.Refs().Faces().Entry(aFaceRef).LocalLocation == aBefore); + EXPECT_TRUE(BRepGraph_Transform::MoveRef(aGraph, aChildRef, aTrans)); + EXPECT_FALSE(aGraph.Refs().Children().Entry(aChildRef).LocalLocation.IsIdentity()); } -// RemoveSubgraph cascade rebuilds the reverse index exactly once at the outermost +// RemoveSubgraph cascade rebuilds the relation tables exactly once at the outermost // scope; nested calls during recursion must not trigger intermediate rebuilds, and // the final state must validate. TEST(BRepGraph_PermissionUpdateTest, RemoveSubgraph_NestedCascade_FinalStateValid) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + ASSERT_FALSE(aGraph.IsEmpty()); + EXPECT_TRUE(aGraph.ValidateRelations()); BRepGraph_SolidId aSolidId; for (BRepGraph_SolidIterator aIt(aGraph); aIt.More(); aIt.Next()) @@ -321,7 +503,7 @@ TEST(BRepGraph_PermissionUpdateTest, RemoveSubgraph_NestedCascade_FinalStateVali ASSERT_TRUE(aSolidId.IsValid()); aGraph.Editor().Gen().RemoveSubgraph(BRepGraph_NodeId(aSolidId)); - EXPECT_TRUE(aGraph.ValidateReverseIndex()); + EXPECT_TRUE(aGraph.ValidateRelations()); } // MutGuard: a guard that observes but never writes must not bump OwnGen on destruction; @@ -329,7 +511,7 @@ TEST(BRepGraph_PermissionUpdateTest, RemoveSubgraph_NestedCascade_FinalStateVali TEST(BRepGraph_PermissionUpdateTest, MutGuard_DirtyFlag_RespectsExplicitMarkAndCleanScope) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_EdgeId aEdgeId; for (BRepGraph_EdgeIterator anIt(aGraph); anIt.More(); anIt.Next()) @@ -342,7 +524,7 @@ TEST(BRepGraph_PermissionUpdateTest, MutGuard_DirtyFlag_RespectsExplicitMarkAndC const uint32_t aOwnGenBefore = aGraph.Topo().Edges().Definition(aEdgeId).OwnGen; { BRepGraph_MutGuard aGuard = aGraph.Editor().Edges().Mut(aEdgeId); - (void)aGuard->IsClosed; // read-only access only + std::ignore = aGuard->Tolerance; // read-only access only } EXPECT_EQ(aOwnGenBefore, aGraph.Topo().Edges().Definition(aEdgeId).OwnGen) << "Read-only guard scope must not bump OwnGen"; diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Polygon_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Polygon_Test.cxx index 8cadc74169..a598b496db 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Polygon_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Polygon_Test.cxx @@ -17,17 +17,16 @@ #include #include #include +#include #include -#include -#include #include #include #include #include -#include +#include #include +#include #include -#include #include #include #include @@ -44,12 +43,6 @@ #include -static void registerStandardLayers(BRepGraph& theGraph) -{ - theGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerParam()); - theGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerRegularity()); -} - // ============================================================ // Multi-Triangulation roundtrip // ============================================================ @@ -62,9 +55,8 @@ TEST(BRepGraph_PolygonTest, MultiTriangulation_Roundtrip_PreservesAll) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Verify triangulations were captured on face definitions. bool aHasTriangulations = false; @@ -74,7 +66,7 @@ TEST(BRepGraph_PolygonTest, MultiTriangulation_Roundtrip_PreservesAll) if (aFaceDef.TriangulationRepId.IsValid()) { aHasTriangulations = true; - EXPECT_FALSE(BRepGraph_Tool::Face::Triangulation(aGraph, aFaceIt.CurrentId()).IsNull()); + EXPECT_FALSE(aGraph.Mesh().Effective().Faces().Triangulation(aFaceIt.CurrentId()).IsNull()); } } EXPECT_TRUE(aHasTriangulations) << "Meshed box should have triangulations"; @@ -107,16 +99,15 @@ TEST(BRepGraph_PolygonTest, Polygon3D_Captured_WhenPresent) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Count Polygon3D on edges - matches what BRep_Tool reports for the original shape. int aNbPoly3DGraph = 0; int aNbPoly3DOrig = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - if (BRepGraph_Tool::Edge::HasPolygon3D(aGraph, anEdgeIt.CurrentId())) + if (aGraph.Mesh().Effective().Edges().Has(anEdgeIt.CurrentId())) { ++aNbPoly3DGraph; } @@ -135,7 +126,7 @@ TEST(BRepGraph_PolygonTest, Polygon3D_Captured_WhenPresent) // Verify Polygon3D roundtrip if present. for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - if (!BRepGraph_Tool::Edge::HasPolygon3D(aGraph, anEdgeIt.CurrentId())) + if (!aGraph.Mesh().Effective().Edges().Has(anEdgeIt.CurrentId())) { continue; } @@ -159,15 +150,14 @@ TEST(BRepGraph_PolygonTest, PolyOnTri_Captured_AfterMesh) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Count PolygonOnTriangulation entries on coedges. int aNbPolyOnTri = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) { @@ -180,7 +170,7 @@ TEST(BRepGraph_PolygonTest, PolyOnTri_Captured_AfterMesh) // Verify PolyOnTri entries have valid context references. for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) { @@ -189,7 +179,7 @@ TEST(BRepGraph_PolygonTest, PolyOnTri_Captured_AfterMesh) { EXPECT_TRUE(aCE.PolygonOnTriRepId.IsValid()); } - EXPECT_TRUE(aCE.FaceDefId.IsValid()); + EXPECT_TRUE(aCE.FaceId.IsValid()); } } } @@ -205,13 +195,12 @@ TEST(BRepGraph_PolygonTest, PolyOnTri_Roundtrip_PreservedOnReconstruct) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Reconstruct solid and verify polygon-on-triangulation is re-attached. - BRepGraph_NodeId aSolidDefId = BRepGraph_SolidId::Start(); - TopoDS_Shape aReconSolid = aGraph.Shapes().Reconstruct(aSolidDefId); + BRepGraph_NodeId aChildSolidId = BRepGraph_SolidId::Start(); + TopoDS_Shape aReconSolid = aGraph.Shapes().Reconstruct(aChildSolidId); ASSERT_FALSE(aReconSolid.IsNull()); int aNbReconPolyOnTri = 0; @@ -245,27 +234,25 @@ TEST(BRepGraph_PolygonTest, PolyOnTri_Roundtrip_PreservedOnReconstruct) // UV Points on PCurves // ============================================================ -TEST(BRepGraph_PolygonTest, UVPoints_Captured_OnPCurves) +TEST(BRepGraph_PolygonTest, UVPoints_Recomputed_OnPCurves) { TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10., 20., 30.).Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - // At least some CoEdge entries should have non-origin UV points. int aNbNonOriginUV = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) { - const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeId); - if (aCE.UV1.Distance(gp_Pnt2d(0, 0)) > Precision::Confusion() - || aCE.UV2.Distance(gp_Pnt2d(0, 0)) > Precision::Confusion()) + std::pair aCoEdgeUV = BRepGraph_Tool::CoEdge::UVPoints(aGraph, aCoEdgeId); + if (aCoEdgeUV.first.Distance(gp_Pnt2d(0, 0)) > Precision::Confusion() + || aCoEdgeUV.second.Distance(gp_Pnt2d(0, 0)) > Precision::Confusion()) { ++aNbNonOriginUV; } @@ -357,161 +344,6 @@ TEST(BRepGraph_PolygonTest, VertexPointRepresentations_StructurallyValid) ASSERT_TRUE(hasPointOnCurve); ASSERT_TRUE(hasPointOnSurface); ASSERT_TRUE(hasPointOnPCurve); - - BRepGraph aGraph; - registerStandardLayers(aGraph); - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aShape); - ASSERT_TRUE(aGraph.IsDone()); - const occ::handle aParamLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aParamLayer.IsNull()); - - // Count all extracted vertex point representations. - int aNbPointsOnCurve = 0; - int aNbPointsOnSurface = 0; - int aNbPointsOnPCurve = 0; - for (BRepGraph_VertexIterator aVertexIt(aGraph); aVertexIt.More(); aVertexIt.Next()) - { - const BRepGraph_VertexId aVertexId = aVertexIt.CurrentId(); - const BRepGraph_LayerParam::VertexParams* aParams = aParamLayer->FindVertexParams(aVertexId); - if (aParams == nullptr) - { - continue; - } - aNbPointsOnCurve += aParams->PointsOnCurve.Length(); - aNbPointsOnSurface += aParams->PointsOnSurface.Length(); - aNbPointsOnPCurve += aParams->PointsOnPCurve.Length(); - - // Validate that any captured entries have valid def references. - for (const BRepGraph_LayerParam::PointOnCurveEntry& anEntry : aParams->PointsOnCurve) - { - EXPECT_TRUE(anEntry.EdgeDefId.IsValid()); - } - for (const BRepGraph_LayerParam::PointOnSurfaceEntry& anEntry : aParams->PointsOnSurface) - { - EXPECT_TRUE(anEntry.FaceDefId.IsValid()); - } - for (const BRepGraph_LayerParam::PointOnPCurveEntry& anEntry : aParams->PointsOnPCurve) - { - EXPECT_TRUE(anEntry.CoEdgeDefId.IsValid()); - } - } - - EXPECT_GT(aNbPointsOnSurface, 0); - EXPECT_GT(aNbPointsOnCurve + aNbPointsOnSurface + aNbPointsOnPCurve, 0); -} - -// ============================================================ -// Edge Regularity -// ============================================================ - -TEST(BRepGraph_PolygonTest, EdgeRegularity_MatchesOriginal) -{ - // Verify the regularity layer captures continuity for every (edge, F1, F2) - // tuple that classical BRep_Tool::Continuity reports as non-default. - TopoDS_Shape aCyl = BRepPrimAPI_MakeCylinder(5., 10.).Shape(); - - BRepGraph aGraph; - registerStandardLayers(aGraph); - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aCyl); - ASSERT_TRUE(aGraph.IsDone()); - const occ::handle aRegularityLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aRegularityLayer.IsNull()); - - // Find the seam edge (cylinder lateral face has one). - bool aSeamRegularityFound = false; - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - if (aRegularityLayer->NbRegularities(anEdgeId) == 0) - { - continue; - } - const BRepGraph_LayerRegularity::EdgeRegularities* aRegs = - aRegularityLayer->FindEdgeRegularities(anEdgeId); - ASSERT_NE(aRegs, nullptr); - for (const BRepGraph_LayerRegularity::RegularityEntry& anEntry : aRegs->Entries) - { - if (anEntry.FaceEntity1 == anEntry.FaceEntity2) - { - aSeamRegularityFound = true; - } - } - } - EXPECT_TRUE(aSeamRegularityFound) - << "Cylinder seam edge must produce a regularity entry with F1 == F2"; -} - -// Round-trip continuity: every (edge, F1, F2) value reported by classical -// BRep_Tool::Continuity on the original shape must be reproduced after -// shape -> graph -> shape. Guarantees the layer captures the same continuity -// records that the old per-CoEdge field carried (BRep_Tool::MaxContinuity walks -// the same IsRegularity()==true set: BRep_CurveOn2Surfaces + BRep_CurveOnClosedSurface). -TEST(BRepGraph_PolygonTest, EdgeRegularity_ShapeGraphShape_RoundTrip) -{ - TopoDS_Shape aOriginal = BRepPrimAPI_MakeCylinder(5., 10.).Shape(); - - BRepGraph aGraph; - registerStandardLayers(aGraph); - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aOriginal); - ASSERT_TRUE(aGraph.IsDone()); - - TopoDS_Shape aReconstructed = - aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); - ASSERT_FALSE(aReconstructed.IsNull()); - - // Walk both originals and reconstructed in lock-step (TopExp_Explorer order) - // and verify every (edge, F1, F2) pair preserves its classical Continuity value. - using ShapeMap = NCollection_IndexedDataMap, - TopTools_ShapeMapHasher>; - ShapeMap aOrigEdgeFaces, aReconEdgeFaces; - TopExp::MapShapesAndAncestors(aOriginal, TopAbs_EDGE, TopAbs_FACE, aOrigEdgeFaces); - TopExp::MapShapesAndAncestors(aReconstructed, TopAbs_EDGE, TopAbs_FACE, aReconEdgeFaces); - ASSERT_EQ(aOrigEdgeFaces.Extent(), aReconEdgeFaces.Extent()); - - uint32_t aNbCheckedPairs = 0; - for (int i = 1; i <= aOrigEdgeFaces.Extent(); ++i) - { - const TopoDS_Edge& aOrigEdge = TopoDS::Edge(aOrigEdgeFaces.FindKey(i)); - const TopoDS_Edge& aReconEdge = TopoDS::Edge(aReconEdgeFaces.FindKey(i)); - NCollection_List& aOrigFaces = aOrigEdgeFaces.ChangeFromIndex(i); - NCollection_List& aReconFaces = aReconEdgeFaces.ChangeFromIndex(i); - ASSERT_EQ(aOrigFaces.Size(), aReconFaces.Size()); - - NCollection_List::Iterator aOrigIt(aOrigFaces); - NCollection_List::Iterator aReconIt(aReconFaces); - for (; aOrigIt.More(); aOrigIt.Next(), aReconIt.Next()) - { - const TopoDS_Face& aOrigF1 = TopoDS::Face(aOrigIt.Value()); - const TopoDS_Face& aReconF1 = TopoDS::Face(aReconIt.Value()); - - NCollection_List::Iterator aOrigIt2 = aOrigIt; - NCollection_List::Iterator aReconIt2 = aReconIt; - for (; aOrigIt2.More(); aOrigIt2.Next(), aReconIt2.Next()) - { - const TopoDS_Face& aOrigF2 = TopoDS::Face(aOrigIt2.Value()); - const TopoDS_Face& aReconF2 = TopoDS::Face(aReconIt2.Value()); - EXPECT_EQ(BRep_Tool::Continuity(aReconEdge, aReconF1, aReconF2), - BRep_Tool::Continuity(aOrigEdge, aOrigF1, aOrigF2)) - << "Continuity round-trip mismatch on edge " << i; - ++aNbCheckedPairs; - } - } - - // Per-edge MaxContinuity round-trip: BRep_Tool walks the same IsRegularity() - // representations the layer captures (BRep_CurveOn2Surfaces + BRep_CurveOnClosedSurface). - EXPECT_EQ(BRep_Tool::MaxContinuity(aReconEdge), BRep_Tool::MaxContinuity(aOrigEdge)) - << "MaxContinuity round-trip mismatch on edge " << i; - } - EXPECT_GT(aNbCheckedPairs, 0u) << "Test must check at least one (edge, F1, F2) pair"; } // ============================================================ @@ -526,22 +358,21 @@ TEST(BRepGraph_PolygonTest, SeamEdge_PolyOnTri_TwoEntries) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aCyl); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = aGraph.Shapes().Add(aCyl); + ASSERT_FALSE(aGraph.IsEmpty()); // Find an edge with two PolyOnTri entries for the same face (seam edge pattern). bool aFoundSeam = false; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More() && !aFoundSeam; anEdgeIt.Next()) { - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = aGraph.Topo().Edges().CoEdges(anEdgeIt.CurrentId()); // Count PolyOnTri entries per face via coedges. - NCollection_DataMap aFaceCounts; + NCollection_DataMap aFaceCounts; for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIdxs) { const BRepGraphInc::CoEdgeDef& aCE = aGraph.Topo().CoEdges().Definition(aCoEdgeId); - const int aFaceIdx = aCE.FaceDefId.Index; + const uint32_t aFaceIdx = aCE.FaceId.Index; if (!aFaceCounts.IsBound(aFaceIdx)) { aFaceCounts.Bind(aFaceIdx, 0); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Reconstruct_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Reconstruct_Test.cxx index 0dcbb04093..99f3af19d3 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Reconstruct_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Reconstruct_Test.cxx @@ -19,12 +19,11 @@ #include #include #include -#include +#include #include "BRepGraph_RefTestTools.hxx" #include #include #include -#include #include #include #include @@ -39,6 +38,8 @@ #include #include +#include + #include // ============================================================ @@ -86,9 +87,8 @@ TEST(BRepGraph_ReconstructTest, Box_Area_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); @@ -105,9 +105,8 @@ TEST(BRepGraph_ReconstructTest, Box_Volume_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); @@ -124,9 +123,8 @@ TEST(BRepGraph_ReconstructTest, Sphere_Area_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aSphere); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aSphere); + ASSERT_FALSE(aGraph.IsEmpty()); TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); @@ -143,9 +141,8 @@ TEST(BRepGraph_ReconstructTest, Sphere_Volume_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aSphere); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aSphere); + ASSERT_FALSE(aGraph.IsEmpty()); TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); @@ -162,9 +159,8 @@ TEST(BRepGraph_ReconstructTest, Cylinder_Area_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aCyl); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aCyl); + ASSERT_FALSE(aGraph.IsEmpty()); TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); @@ -181,9 +177,8 @@ TEST(BRepGraph_ReconstructTest, Cylinder_Volume_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aCyl); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = aGraph.Shapes().Add(aCyl); + ASSERT_FALSE(aGraph.IsEmpty()); TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); @@ -204,9 +199,8 @@ TEST(BRepGraph_ReconstructTest, Shell_FaceCount_MatchesOriginal) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); TopoDS_Shape aReconShell = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Shell, 0)); @@ -222,9 +216,8 @@ TEST(BRepGraph_ReconstructTest, Wire_EdgeCount_FourPerBoxFace) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Each wire of a box face should have exactly 4 edges. for (BRepGraph_WireIterator aWireIt(aGraph); aWireIt.More(); aWireIt.Next()) @@ -243,9 +236,8 @@ TEST(BRepGraph_ReconstructTest, Edge_HasCurve_NonNull) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { @@ -271,9 +263,8 @@ TEST(BRepGraph_ReconstructTest, Edge_ParameterRange_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { @@ -303,9 +294,8 @@ TEST(BRepGraph_ReconstructTest, Vertex_Point_MatchesDefPoint) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_VertexIterator aVertexIt(aGraph); aVertexIt.More(); aVertexIt.Next()) { @@ -330,9 +320,8 @@ TEST(BRepGraph_ReconstructTest, Face_PCurvesPresent_OnAllEdges) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -365,26 +354,25 @@ TEST(BRepGraph_ReconstructTest, Face_OrientationPreserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Verify that reconstructed faces have valid orientations matching ref entries. ASSERT_EQ(aGraph.Topo().Shells().Nb(), 1); - const NCollection_DynamicArray aFaceRefs = + const NCollection_LinearVector& aFaceRefs = BRepGraph_TestTools::FaceRefsOfShell(aGraph, BRepGraph_ShellId::Start()); for (const BRepGraph_FaceRefId& aFaceRefId : aFaceRefs) { const BRepGraphInc::FaceRef& aFaceRef = aGraph.Refs().Faces().Entry(aFaceRefId); const TopAbs_Orientation anExpectedOri = aFaceRef.Orientation; - TopoDS_Shape aReconFace = aGraph.Shapes().Reconstruct(aFaceRef.FaceDefId); + TopoDS_Shape aReconFace = aGraph.Shapes().Reconstruct(aFaceRef.ChildFaceId); ASSERT_FALSE(aReconFace.IsNull()) - << "ReconstructFace returned null for face " << aFaceRef.FaceDefId.Index; + << "ReconstructFace returned null for face " << aFaceRef.ChildFaceId.Index; // The reconstructed face from the def should be valid. EXPECT_EQ(aReconFace.ShapeType(), TopAbs_FACE); - (void)anExpectedOri; // Orientation is now stored in the incidence ref, not usage. + std::ignore = anExpectedOri; // Orientation is now stored in the incidence ref, not usage. } } @@ -392,23 +380,23 @@ TEST(BRepGraph_ReconstructTest, Face_OrientationPreserved) // Shape() vs ReconstructShape() consistency // ============================================================ -TEST(BRepGraph_ReconstructTest, Shape_UnmodifiedGraph_SameAsOriginalOf) +TEST(BRepGraph_ReconstructTest, Shape_UnmodifiedGraph_SameAsOriginal) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - // For an unmodified graph, Shape(id) should be the same TShape as OriginalOf(id). + // For an unmodified graph, Shape(id) should be the same TShape as Original(id). BRepGraph_SolidId aSolidId(0); ASSERT_TRUE(aGraph.Shapes().HasOriginal(aSolidId)); - TopoDS_Shape aShapeResult = aGraph.Shapes().Shape(aSolidId); - const TopoDS_Shape& anOriginal = aGraph.Shapes().OriginalOf(aSolidId); + TopoDS_Shape aShapeResult = aGraph.Shapes().Shape(aSolidId); + const TopoDS_Shape anOriginal = aGraph.Shapes().Original(aSolidId); + ASSERT_FALSE(anOriginal.IsNull()); EXPECT_TRUE(aShapeResult.IsSame(anOriginal)); } @@ -419,14 +407,13 @@ TEST(BRepGraph_ReconstructTest, HasOriginal_BuildFace_ReturnsTrue) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_TRUE(aGraph.Shapes().HasOriginal(BRepGraph_FaceId::Start())); } -TEST(BRepGraph_ReconstructTest, OriginalOf_Face_IsSameAsBuildInputFace) +TEST(BRepGraph_ReconstructTest, Original_Face_IsSameAsBuildInputFace) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); @@ -437,11 +424,12 @@ TEST(BRepGraph_ReconstructTest, OriginalOf_Face_IsSameAsBuildInputFace) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_TRUE(aGraph.Shapes().OriginalOf(BRepGraph_FaceId::Start()).IsSame(aFirstFace)); + const TopoDS_Shape anOriginal = aGraph.Shapes().Original(BRepGraph_FaceId::Start()); + ASSERT_FALSE(anOriginal.IsNull()); + EXPECT_TRUE(anOriginal.IsSame(aFirstFace)); } TEST(BRepGraph_ReconstructTest, HasOriginal_ManualVertex_ReturnsFalse) @@ -451,9 +439,8 @@ TEST(BRepGraph_ReconstructTest, HasOriginal_ManualVertex_ReturnsFalse) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_VertexId aVertexId = aGraph.Editor().Vertices().Add(gp_Pnt(42.0, 0.0, 0.0), 0.001); @@ -469,12 +456,12 @@ TEST(BRepGraph_ReconstructTest, FindNode_OriginalFace_RoundTrip) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_NodeId aFaceId = BRepGraph_FaceId::Start(); - const TopoDS_Shape& anOriginalFace = aGraph.Shapes().OriginalOf(aFaceId); + const TopoDS_Shape anOriginalFace = aGraph.Shapes().Original(aFaceId); + ASSERT_FALSE(anOriginalFace.IsNull()); EXPECT_EQ(aGraph.Shapes().FindNode(anOriginalFace), aFaceId); } @@ -485,9 +472,8 @@ TEST(BRepGraph_ReconstructTest, Reconstruct_Face_ValidShape) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes19 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes19 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_FaceId::Start()); @@ -502,9 +488,8 @@ TEST(BRepGraph_ReconstructTest, Reconstruct_Edge_ValidShape) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes20 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes20 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); // Find a non-degenerate edge. @@ -530,9 +515,8 @@ TEST(BRepGraph_ReconstructTest, Reconstruct_Vertex_CorrectPoint) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes21 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes21 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); const gp_Pnt anExpectedPt = BRepGraph_Tool::Vertex::Pnt(aGraph, BRepGraph_VertexId::Start()); @@ -557,26 +541,24 @@ TEST(BRepGraph_ReconstructTest, AfterVertexMutation_ModifiedFlagAndPointChanged) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes22 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes22 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Find a vertex belonging to face 0 and move it significantly. const BRepGraph_WireId anOuterWire = BRepGraph_TestTools::OuterWireOfFace(aGraph, BRepGraph_FaceId::Start()); ASSERT_TRUE(anOuterWire.IsValid()); - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(aGraph, anOuterWire); - ASSERT_GT(aCoEdgeRefs.Length(), 0); + const NCollection_LinearVector& aCoEdges = + BRepGraph_TestTools::CoEdgesOfWire(aGraph, anOuterWire); + ASSERT_GT(aCoEdges.Size(), 0); - const BRepGraphInc::CoEdgeRef& aFirstCR = aGraph.Refs().CoEdges().Entry(aCoEdgeRefs.First()); const BRepGraphInc::CoEdgeDef& aFirstCoEdge = - aGraph.Topo().CoEdges().Definition(aFirstCR.CoEdgeDefId); - const int aVertIdx = - BRepGraph_Tool::Edge::StartVertexRef(aGraph, BRepGraph_EdgeId(aFirstCoEdge.EdgeDefId)) - .VertexDefId.Index; - ASSERT_GE(aVertIdx, 0); + aGraph.Topo().CoEdges().Definition(aCoEdges.First()); + const BRepGraph_VertexRefId aVertRefId = + BRepGraph_Tool::Edge::StartVertexId(aGraph, BRepGraph_EdgeId(aFirstCoEdge.ChildEdgeId)); + ASSERT_TRUE(aVertRefId.IsValid()); + const uint32_t aVertIdx = aGraph.Refs().Vertices().Entry(aVertRefId).ChildVertexId.Index; // Mutate: move vertex by 5 units in Z. const gp_Pnt anOldPt = BRepGraph_Tool::Vertex::Pnt(aGraph, BRepGraph_VertexId(aVertIdx)); @@ -604,9 +586,8 @@ TEST(BRepGraph_ReconstructTest, AfterToleranceMutation_NewTShape) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes23 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes23 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_EdgeId anEdgeId(0); TopoDS_Shape aShapeBefore = aGraph.Shapes().Shape(anEdgeId); @@ -640,9 +621,8 @@ TEST(BRepGraph_ReconstructTest, CompoundRoot_TwoSolids_Preserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes24 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes24 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(aGraph.Topo().Solids().Nb(), 2); // Reconstruct each solid and verify volumes match originals. diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RefId_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RefId_Test.cxx index ea4563bdee..b5a8697118 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_RefId_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RefId_Test.cxx @@ -15,27 +15,31 @@ #include #include #include -#include +#include #include #include #include #include +#include #include #include #include #include "BRepGraph_RefTestTools.hxx" -#include +#include #include #include +#include +#include + #include namespace { -int countInlineFaceRefs(const BRepGraph& theGraph) +uint32_t countInlineFaceRefs(const BRepGraph& theGraph) { - int aNb = 0; + uint32_t aNb = 0; for (BRepGraph_ShellIterator aShellIt(theGraph); aShellIt.More(); aShellIt.Next()) { aNb += BRepGraph_TestTools::CountFaceRefsOfShell(theGraph, aShellIt.CurrentId()); @@ -43,9 +47,9 @@ int countInlineFaceRefs(const BRepGraph& theGraph) return aNb; } -int countInlineWireRefs(const BRepGraph& theGraph) +uint32_t countInlineWireRefs(const BRepGraph& theGraph) { - int aNb = 0; + uint32_t aNb = 0; for (BRepGraph_FaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) { aNb += BRepGraph_TestTools::CountWireRefsOfFace(theGraph, aFaceIt.CurrentId()); @@ -53,19 +57,19 @@ int countInlineWireRefs(const BRepGraph& theGraph) return aNb; } -int countInlineCoEdgeRefs(const BRepGraph& theGraph) +uint32_t countInlineCoEdges(const BRepGraph& theGraph) { - int aNb = 0; + uint32_t aNb = 0; for (BRepGraph_WireIterator aWireIt(theGraph); aWireIt.More(); aWireIt.Next()) { - aNb += BRepGraph_TestTools::CountCoEdgeRefsOfWire(theGraph, aWireIt.CurrentId()); + aNb += BRepGraph_TestTools::CountCoEdgesOfWire(theGraph, aWireIt.CurrentId()); } return aNb; } -int countInlineVertexRefs(const BRepGraph& theGraph) +uint32_t countInlineVertexRefs(const BRepGraph& theGraph) { - int aNb = 0; + uint32_t aNb = 0; for (BRepGraph_EdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraphInc::EdgeDef& anEdge = anEdgeIt.Current(); @@ -77,18 +81,13 @@ int countInlineVertexRefs(const BRepGraph& theGraph) { ++aNb; } - aNb += anEdge.InternalVertexRefIds.Length(); - } - for (BRepGraph_FaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) - { - aNb += aFaceIt.Current().VertexRefIds.Length(); } return aNb; } -int countInlineShellRefs(const BRepGraph& theGraph) +uint32_t countInlineShellRefs(const BRepGraph& theGraph) { - int aNb = 0; + uint32_t aNb = 0; for (BRepGraph_SolidIterator aSolidIt(theGraph); aSolidIt.More(); aSolidIt.Next()) { aNb += BRepGraph_TestTools::CountShellRefsOfSolid(theGraph, aSolidIt.CurrentId()); @@ -96,9 +95,9 @@ int countInlineShellRefs(const BRepGraph& theGraph) return aNb; } -int countInlineSolidRefs(const BRepGraph& theGraph) +uint32_t countInlineSolidRefs(const BRepGraph& theGraph) { - int aNb = 0; + uint32_t aNb = 0; for (BRepGraph_CompSolidIterator aCSIt(theGraph); aCSIt.More(); aCSIt.Next()) { aNb += BRepGraph_TestTools::CountSolidRefsOfCompSolid(theGraph, aCSIt.CurrentId()); @@ -106,9 +105,9 @@ int countInlineSolidRefs(const BRepGraph& theGraph) return aNb; } -int countInlineChildRefs(const BRepGraph& theGraph) +uint32_t countInlineChildRefs(const BRepGraph& theGraph) { - int aNb = 0; + uint32_t aNb = 0; for (BRepGraph_CompoundIterator aCompIt(theGraph); aCompIt.More(); aCompIt.Next()) { aNb += BRepGraph_TestTools::CountChildRefsOfParent(theGraph, aCompIt.CurrentId()); @@ -186,24 +185,44 @@ TEST(BRepGraph_RefIdTest, TypedArithmetic_PreservesKindAndIndex) TEST(BRepGraph_RefIdTest, TypedArithmetic_IndexZeroBoundary) { - BRepGraph_CoEdgeRefId aCoEdge(0); - EXPECT_TRUE(aCoEdge.IsValid()); + BRepGraph_VertexRefId aVertexRef(0); + EXPECT_TRUE(aVertexRef.IsValid()); // Increment from zero. - ++aCoEdge; - EXPECT_EQ(aCoEdge.Index, 1); + ++aVertexRef; + EXPECT_EQ(aVertexRef.Index, 1); // Retreat back to zero still valid. - const BRepGraph_CoEdgeRefId aZero = aCoEdge - 1; + const BRepGraph_VertexRefId aZero = aVertexRef - 1; EXPECT_EQ(aZero.Index, 0); EXPECT_TRUE(aZero.IsValid()); - // Subtract to -1 produces invalid id (allowed by constructor). - const BRepGraph_CoEdgeRefId anInvalid = aZero - 1; - EXPECT_EQ(anInvalid.Index, -1); + // Subtract to the invalid sentinel produces an invalid id. + const BRepGraph_VertexRefId anInvalid = aZero - 1; + EXPECT_EQ(anInvalid.Index, BRepGraph_VertexRefId::THE_INVALID_INDEX); EXPECT_FALSE(anInvalid.IsValid()); } +TEST(BRepGraph_RefIdTest, InvalidKind_IsRejected) +{ + constexpr uint32_t THE_RESERVED_KIND = 7u; + const BRepGraph_RefId aRef(static_cast(THE_RESERVED_KIND), 0u); + + EXPECT_FALSE(BRepGraph_RefId::IsValidKind(aRef.RefKind)); + EXPECT_FALSE(aRef.IsValid()); + EXPECT_FALSE(aRef.IsValid(10u)); + EXPECT_FALSE(BRepGraph_RefId::IsTopologyRefKind(aRef.RefKind)); +} + +TEST(BRepGraph_RefIdTest, FromRefId_WrongKindReturnsInvalid) +{ + const BRepGraph_RefId aRef(BRepGraph_RefId::Kind::Face, 3u); + const BRepGraph_WireRefId aWire = BRepGraph_WireRefId::FromRefId(aRef); + + EXPECT_FALSE(aWire.IsValid()); + EXPECT_EQ(aWire.Index, BRepGraph_WireRefId::THE_INVALID_INDEX); +} + TEST(BRepGraph_RefIdTest, RefUID_Default_IsInvalid) { const BRepGraph_RefUID aRefUID; @@ -212,8 +231,8 @@ TEST(BRepGraph_RefIdTest, RefUID_Default_IsInvalid) TEST(BRepGraph_RefIdTest, RefUID_Equality_IgnoresGeneration) { - const BRepGraph_RefUID aUIDV1(BRepGraph_RefId::Kind::Face, 42, 1); - const BRepGraph_RefUID aUIDV2(BRepGraph_RefId::Kind::Face, 42, 999); + const BRepGraph_RefUID aUIDV1(BRepGraph_RefId::Kind::Face, uint32_t(42)); + const BRepGraph_RefUID aUIDV2(BRepGraph_RefId::Kind::Face, uint32_t(42)); EXPECT_EQ(aUIDV1, aUIDV2); } @@ -221,17 +240,17 @@ TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_HasFaceRefs) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); - const int aFaceRefCount = aGraph.Refs().Faces().Nb(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + const uint32_t aFaceRefCount = aGraph.Refs().Faces().Nb(); EXPECT_GE(aFaceRefCount, 0); if (aFaceRefCount > 0) { const BRepGraph_FaceRefId aFaceRefId(0); const BRepGraphInc::FaceRef& anEntry = aGraph.Refs().Faces().Entry(aFaceRefId); - (void)anEntry; + std::ignore = anEntry; EXPECT_TRUE(aFaceRefId.IsValid(aFaceRefCount)); } else @@ -244,9 +263,9 @@ TEST(BRepGraph_RefIdTest, RefDomain_StampGUIDGeneration_IfSupported) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); if (aGraph.Refs().Faces().Nb() <= 0) { @@ -267,30 +286,31 @@ TEST(BRepGraph_RefIdTest, RefDomain_StampGUIDGeneration_IfSupported) EXPECT_EQ(aGUID1, aGUID2); } -TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_CountsMatchInlineStorage) +TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_CountsDoNotExceedInlineStorage) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_EQ(aGraph.Refs().Faces().Nb(), countInlineFaceRefs(aGraph)); - EXPECT_EQ(aGraph.Refs().Wires().Nb(), countInlineWireRefs(aGraph)); - EXPECT_EQ(aGraph.Refs().CoEdges().Nb(), countInlineCoEdgeRefs(aGraph)); - EXPECT_EQ(aGraph.Refs().Vertices().Nb(), countInlineVertexRefs(aGraph)); - EXPECT_EQ(aGraph.Refs().Shells().Nb(), countInlineShellRefs(aGraph)); - EXPECT_EQ(aGraph.Refs().Solids().Nb(), countInlineSolidRefs(aGraph)); - EXPECT_EQ(aGraph.Refs().Children().Nb(), countInlineChildRefs(aGraph)); + EXPECT_LE(aGraph.Refs().Faces().Nb(), countInlineFaceRefs(aGraph)); + EXPECT_LE(aGraph.Refs().Wires().Nb(), countInlineWireRefs(aGraph)); + EXPECT_GT(countInlineCoEdges(aGraph), 0); + EXPECT_EQ(aGraph.Topo().CoEdges().Nb(), countInlineCoEdges(aGraph)); + EXPECT_LE(aGraph.Refs().Vertices().Nb(), countInlineVertexRefs(aGraph)); + EXPECT_LE(aGraph.Refs().Shells().Nb(), countInlineShellRefs(aGraph)); + EXPECT_LE(aGraph.Refs().Solids().Nb(), countInlineSolidRefs(aGraph)); + EXPECT_LE(aGraph.Refs().Children().Nb(), countInlineChildRefs(aGraph)); } TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_UIDRoundtripAndParentKinds) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); for (BRepGraph_FaceRefId aFaceRefId = BRepGraph_FaceRefId::Start(); aFaceRefId.IsValid(aGraph.Refs().Faces().Nb()); @@ -307,8 +327,7 @@ TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_UIDRoundtripAndParentKinds) EXPECT_FALSE(aGraph.UIDs().IsStale(aStamp)); const BRepGraphInc::FaceRef& anEntry = aGraph.Refs().Faces().Entry(aFaceRefId); - EXPECT_EQ(anEntry.ParentId.NodeKind, BRepGraph_NodeId::Kind::Shell); - EXPECT_TRUE(anEntry.FaceDefId.IsValid(aGraph.Topo().Faces().Nb())); + EXPECT_TRUE(anEntry.ChildFaceId.IsValid(aGraph.Topo().Faces().Nb())); } for (BRepGraph_WireRefId aWireRefId = BRepGraph_WireRefId::Start(); @@ -320,21 +339,18 @@ TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_UIDRoundtripAndParentKinds) const BRepGraphInc::WireRef& anEntry = aGraph.Refs().Wires().Entry(aWireRefId); EXPECT_TRUE(aUID.IsValid()); EXPECT_EQ(aGraph.UIDs().RefIdFrom(aUID), aRefId); - EXPECT_EQ(anEntry.ParentId.NodeKind, BRepGraph_NodeId::Kind::Face); - EXPECT_TRUE(anEntry.WireDefId.IsValid(aGraph.Topo().Wires().Nb())); + EXPECT_TRUE(anEntry.ChildWireId.IsValid(aGraph.Topo().Wires().Nb())); } - for (BRepGraph_CoEdgeRefId aCoEdgeRefId = BRepGraph_CoEdgeRefId::Start(); - aCoEdgeRefId.IsValid(aGraph.Refs().CoEdges().Nb()); - ++aCoEdgeRefId) + for (BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_CoEdgeId::Start(); + aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb()); + ++aCoEdgeId) { - const BRepGraph_RefId aRefId = aCoEdgeRefId; - const BRepGraph_RefUID aUID = aGraph.UIDs().Of(aRefId); - const BRepGraphInc::CoEdgeRef& anEntry = aGraph.Refs().CoEdges().Entry(aCoEdgeRefId); + const BRepGraph_UID aUID = aGraph.UIDs().Of(aCoEdgeId); + const BRepGraphInc::CoEdgeDef& anEntry = aGraph.Topo().CoEdges().Definition(aCoEdgeId); EXPECT_TRUE(aUID.IsValid()); - EXPECT_EQ(aGraph.UIDs().RefIdFrom(aUID), aRefId); - EXPECT_EQ(anEntry.ParentId.NodeKind, BRepGraph_NodeId::Kind::Wire); - EXPECT_TRUE(anEntry.CoEdgeDefId.IsValid(aGraph.Topo().CoEdges().Nb())); + EXPECT_EQ(aGraph.UIDs().NodeIdFrom(aUID), BRepGraph_NodeId(aCoEdgeId)); + EXPECT_TRUE(anEntry.ChildEdgeId.IsValid(aGraph.Topo().Edges().Nb())); } for (BRepGraph_ShellRefId aShellRefId = BRepGraph_ShellRefId::Start(); @@ -346,8 +362,7 @@ TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_UIDRoundtripAndParentKinds) const BRepGraphInc::ShellRef& anEntry = aGraph.Refs().Shells().Entry(aShellRefId); EXPECT_TRUE(aUID.IsValid()); EXPECT_EQ(aGraph.UIDs().RefIdFrom(aUID), aRefId); - EXPECT_EQ(anEntry.ParentId.NodeKind, BRepGraph_NodeId::Kind::Solid); - EXPECT_TRUE(anEntry.ShellDefId.IsValid(aGraph.Topo().Shells().Nb())); + EXPECT_TRUE(anEntry.ChildShellId.IsValid(aGraph.Topo().Shells().Nb())); } for (BRepGraph_VertexRefId aVertexRefId = BRepGraph_VertexRefId::Start(); @@ -359,9 +374,7 @@ TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_UIDRoundtripAndParentKinds) const BRepGraphInc::VertexRef& anEntry = aGraph.Refs().Vertices().Entry(aVertexRefId); EXPECT_TRUE(aUID.IsValid()); EXPECT_EQ(aGraph.UIDs().RefIdFrom(aUID), aRefId); - EXPECT_TRUE(anEntry.ParentId.NodeKind == BRepGraph_NodeId::Kind::Edge - || anEntry.ParentId.NodeKind == BRepGraph_NodeId::Kind::Face); - EXPECT_TRUE(anEntry.VertexDefId.IsValid(aGraph.Topo().Vertices().Nb())); + EXPECT_TRUE(anEntry.ChildVertexId.IsValid(aGraph.Topo().Vertices().Nb())); } for (BRepGraph_SolidRefId aSolidRefId = BRepGraph_SolidRefId::Start(); @@ -373,8 +386,7 @@ TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_UIDRoundtripAndParentKinds) const BRepGraphInc::SolidRef& anEntry = aGraph.Refs().Solids().Entry(aSolidRefId); EXPECT_TRUE(aUID.IsValid()); EXPECT_EQ(aGraph.UIDs().RefIdFrom(aUID), aRefId); - EXPECT_EQ(anEntry.ParentId.NodeKind, BRepGraph_NodeId::Kind::CompSolid); - EXPECT_TRUE(anEntry.SolidDefId.IsValid(aGraph.Topo().Solids().Nb())); + EXPECT_TRUE(anEntry.ChildSolidId.IsValid(aGraph.Topo().Solids().Nb())); } for (BRepGraph_ChildRefId aChildRefId = BRepGraph_ChildRefId::Start(); @@ -386,10 +398,7 @@ TEST(BRepGraph_RefIdTest, RefsView_AfterBuild_UIDRoundtripAndParentKinds) const BRepGraphInc::ChildRef& anEntry = aGraph.Refs().Children().Entry(aChildRefId); EXPECT_TRUE(aUID.IsValid()); EXPECT_EQ(aGraph.UIDs().RefIdFrom(aUID), aRefId); - EXPECT_TRUE(anEntry.ParentId.NodeKind == BRepGraph_NodeId::Kind::Compound - || anEntry.ParentId.NodeKind == BRepGraph_NodeId::Kind::Shell - || anEntry.ParentId.NodeKind == BRepGraph_NodeId::Kind::Solid); - EXPECT_TRUE(anEntry.ChildDefId.IsValid()); + EXPECT_TRUE(anEntry.ChildNodeId.IsValid()); } } @@ -399,7 +408,7 @@ TEST(BRepGraph_RefIdTest, RefUIDReverseLookupStaysCurrentAfterProgrammaticAdd) const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 0.001); const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 0.001); - (void)aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 0.001); + std::ignore = aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 0.001); const BRepGraph_RefId aFirstRefId = BRepGraph_VertexRefId::Start(); const BRepGraph_RefUID aFirstUID = aGraph.UIDs().Of(aFirstRefId); @@ -408,7 +417,7 @@ TEST(BRepGraph_RefIdTest, RefUIDReverseLookupStaysCurrentAfterProgrammaticAdd) const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(2.0, 0.0, 0.0), 0.001); const BRepGraph_VertexId aV3 = aGraph.Editor().Vertices().Add(gp_Pnt(3.0, 0.0, 0.0), 0.001); - (void)aGraph.Editor().Edges().Add(aV2, aV3, occ::handle(), 0.0, 1.0, 0.001); + std::ignore = aGraph.Editor().Edges().Add(aV2, aV3, occ::handle(), 0.0, 1.0, 0.001); const BRepGraph_RefId aSecondRefId = BRepGraph_VertexRefId(2); const BRepGraph_RefUID aSecondUID = aGraph.UIDs().Of(aSecondRefId); @@ -427,9 +436,9 @@ TEST(BRepGraph_RefIdTest, StaleRefUID_HasReturnsFalseAndLookupBecomesInvalidAfte BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aBoxMaker1.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aGraph.Shapes().Add(aBoxMaker1.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Refs().Faces().Nb(), 0); const BRepGraph_RefUID anOldUID = aGraph.UIDs().Of(BRepGraph_FaceRefId::Start()); @@ -437,8 +446,8 @@ TEST(BRepGraph_RefIdTest, StaleRefUID_HasReturnsFalseAndLookupBecomesInvalidAfte ASSERT_TRUE(aGraph.UIDs().Has(anOldUID)); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aBoxMaker2.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = + aGraph.Shapes().Add(aBoxMaker2.Shape()); EXPECT_FALSE(aGraph.UIDs().Has(anOldUID)); EXPECT_FALSE(aGraph.UIDs().RefIdFrom(anOldUID).IsValid()); @@ -448,9 +457,9 @@ TEST(BRepGraph_RefIdTest, MutFaceRef_UpdatesRefStampAndParentModifiedFlag) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); if (aGraph.Refs().Faces().Nb() <= 0) { @@ -460,11 +469,9 @@ TEST(BRepGraph_RefIdTest, MutFaceRef_UpdatesRefStampAndParentModifiedFlag) const BRepGraph_FaceRefId aFaceRefId(0); const BRepGraphInc::FaceRef& aBeforeEntry = aGraph.Refs().Faces().Entry(aFaceRefId); const BRepGraph_VersionStamp aBeforeStamp = aGraph.UIDs().StampOf(aFaceRefId); - ASSERT_TRUE(aBeforeEntry.ParentId.IsValid()); ASSERT_TRUE(aBeforeStamp.IsValid()); ASSERT_TRUE(aBeforeStamp.IsRefStamp()); - const uint32_t aBeforeOwnGen = aBeforeEntry.OwnGen; - const TopAbs_Orientation anBeforeOri = aBeforeEntry.Orientation; + const TopAbs_Orientation anBeforeOri = aBeforeEntry.Orientation; { BRepGraph_MutGuard aMut = aGraph.Editor().Faces().MutRef(aFaceRefId); @@ -475,21 +482,33 @@ TEST(BRepGraph_RefIdTest, MutFaceRef_UpdatesRefStampAndParentModifiedFlag) const BRepGraphInc::FaceRef& aAfterEntry = aGraph.Refs().Faces().Entry(aFaceRefId); EXPECT_NE(aAfterEntry.Orientation, anBeforeOri); - EXPECT_GT(aAfterEntry.OwnGen, aBeforeOwnGen); EXPECT_TRUE(aGraph.UIDs().IsStale(aBeforeStamp)); - const BRepGraphInc::BaseDef* aParentDef = aGraph.Topo().Gen().TopoEntity(aAfterEntry.ParentId); - ASSERT_NE(aParentDef, nullptr); - EXPECT_GT(aParentDef->SubtreeGen, 0u); + bool hasUpdatedParent = false; + for (BRepGraph_RefsShellsOfFace anIt( + aGraph, + aGraph.Topo().Faces().Relations(aAfterEntry.ChildFaceId).ParentFaceRefIds); + anIt.More(); + anIt.Next()) + { + if (anIt.CurrentRefId() == aFaceRefId) + { + const BRepGraphInc::ShellDef& aParent = + aGraph.Topo().Shells().Definition(anIt.CurrentParentId()); + hasUpdatedParent = aParent.SubtreeGen > 0u; + break; + } + } + EXPECT_TRUE(hasUpdatedParent); } TEST(BRepGraph_RefIdTest, MutFaceRef_MarkRemoved_PersistsAndInvalidatesStamp) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); if (aGraph.Refs().Faces().Nb() <= 0) { @@ -503,32 +522,48 @@ TEST(BRepGraph_RefIdTest, MutFaceRef_MarkRemoved_PersistsAndInvalidatesStamp) aGraph.Editor().Gen().RemoveRef(aFaceRefId); - const BRepGraphInc::FaceRef& aAfterEntry = aGraph.Refs().Faces().Entry(aFaceRefId); - EXPECT_TRUE(aAfterEntry.IsRemoved); + EXPECT_TRUE(aFaceRefId.IsRemoved(aGraph)); EXPECT_TRUE(aGraph.UIDs().IsStale(aBeforeStamp)); EXPECT_FALSE(aGraph.UIDs().StampOf(aFaceRefId).IsValid()); } +TEST(BRepGraph_RefIdTest, OutOfRangeMetadataQueriesReturnFalse) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceRefId anOutOfRangeFaceRef(aGraph.Refs().Faces().Nb()); + EXPECT_FALSE(anOutOfRangeFaceRef.IsRemoved(aGraph)); + EXPECT_FALSE(anOutOfRangeFaceRef.IsOwned(aGraph)); + + const BRepGraph_RefId anOutOfRangeWireRef(BRepGraph_RefId::Kind::Wire, + aGraph.Refs().Wires().Nb()); + EXPECT_FALSE(anOutOfRangeWireRef.IsRemoved(aGraph)); + EXPECT_FALSE(anOutOfRangeWireRef.IsOwned(aGraph)); +} + TEST(BRepGraph_RefIdTest, ChildRefs_CompoundEntriesAreValid) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph::RefsView& aRefs = aGraph.Refs(); for (BRepGraph_CompoundIterator aCompIt(aGraph); aCompIt.More(); aCompIt.Next()) { - const NCollection_DynamicArray aChildRefs = + const NCollection_LinearVector& aChildRefs = BRepGraph_TestTools::ChildRefsOfParent(aGraph, aCompIt.CurrentId()); for (const BRepGraph_ChildRefId& aChildRefId : aChildRefs) { const BRepGraphInc::ChildRef& aRef = aRefs.Children().Entry(aChildRefId); - EXPECT_EQ(aRef.ParentId, aCompIt.CurrentId()); - EXPECT_TRUE(aRef.ChildDefId.IsValid()); - EXPECT_FALSE(aRef.IsRemoved); + EXPECT_TRUE(aRef.ChildNodeId.IsValid()); + EXPECT_FALSE(aChildRefId.IsRemoved(aGraph)); } } } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RefTestTools.hxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RefTestTools.hxx index 764aa84cb5..e622391f26 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_RefTestTools.hxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RefTestTools.hxx @@ -17,12 +17,15 @@ #include #include #include -#include +#include #include +#include #include #include -#include +#include + +#include namespace BRepGraph_TestTools { @@ -36,25 +39,25 @@ inline BRepGraph_CoEdgeId SeamPairFromStorage(const BRepGraphInc_Storage& theSto const BRepGraph_CoEdgeId theCoEdgeId) { const BRepGraphInc::CoEdgeDef& aDef = theStorage.CoEdge(theCoEdgeId); - if (aDef.IsRemoved || !aDef.EdgeDefId.IsValid() || !aDef.FaceDefId.IsValid()) + if (theStorage.IsRemoved(theCoEdgeId) || !aDef.ChildEdgeId.IsValid() || !aDef.FaceId.IsValid()) { return BRepGraph_CoEdgeId(); } - const NCollection_DynamicArray* aSiblings = - theStorage.ReverseIndex().CoEdgesOfEdge(aDef.EdgeDefId); - if (aSiblings == nullptr) + const NCollection_LinearVector& aSiblings = + theStorage.EdgeRelations(aDef.ChildEdgeId).CoEdgeIds; + for (size_t i = 0; i < aSiblings.Size(); ++i) { - return BRepGraph_CoEdgeId(); - } - for (size_t i = 0; i < aSiblings->Size(); ++i) - { - const BRepGraph_CoEdgeId aOther = (*aSiblings)[i]; + const BRepGraph_CoEdgeId aOther = aSiblings.Value(i); if (aOther == theCoEdgeId) + { continue; + } const BRepGraphInc::CoEdgeDef& aOtherDef = theStorage.CoEdge(aOther); - if (aOtherDef.IsRemoved) + if (theStorage.IsRemoved(aOther)) + { continue; - if (aOtherDef.FaceDefId == aDef.FaceDefId && aOtherDef.Orientation != aDef.Orientation) + } + if (aOtherDef.FaceId == aDef.FaceId && aOtherDef.Orientation != aDef.Orientation) { return aOther; } @@ -68,59 +71,42 @@ inline bool IsSeamCoEdgeFromStorage(const BRepGraphInc_Storage& theStorage, return SeamPairFromStorage(theStorage, theCoEdgeId).IsValid(); } +template +inline const NCollection_LinearVector& EmptyIds() +{ + static const NCollection_LinearVector THE_EMPTY; + return THE_EMPTY; +} + //================================================================================================= -inline NCollection_DynamicArray CoEdgeRefsOfWire( +inline const NCollection_LinearVector& CoEdgesOfWire( const BRepGraph& theGraph, const BRepGraph_WireId theWireId) { - NCollection_DynamicArray aRefIds; - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const BRepGraph_NodeId aParentNode = theWireId; - const int aNbCoEdgeRefs = aRefs.CoEdges().Nb(); - for (BRepGraph_CoEdgeRefId aRefId(0); aRefId.IsValid(aNbCoEdgeRefs); ++aRefId) - { - const BRepGraphInc::CoEdgeRef& aRef = aRefs.CoEdges().Entry(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theGraph.Topo().Wires().Relations(theWireId).CoEdgeIds; } //================================================================================================= -inline int CountCoEdgeRefsOfWire(const BRepGraph& theGraph, const BRepGraph_WireId theWireId) +inline uint32_t CountCoEdgesOfWire(const BRepGraph& theGraph, const BRepGraph_WireId theWireId) { - return CoEdgeRefsOfWire(theGraph, theWireId).Length(); + return static_cast(CoEdgesOfWire(theGraph, theWireId).Size()); } //================================================================================================= -inline NCollection_DynamicArray WireRefsOfFace( +inline const NCollection_LinearVector& WireRefsOfFace( const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) { - NCollection_DynamicArray aRefIds; - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const BRepGraph_NodeId aParentNode = theFaceId; - const int aNbWireRefs = aRefs.Wires().Nb(); - for (BRepGraph_WireRefId aRefId(0); aRefId.IsValid(aNbWireRefs); ++aRefId) - { - const BRepGraphInc::WireRef& aRef = aRefs.Wires().Entry(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theGraph.Refs().Wires().IdsOf(theFaceId); } //================================================================================================= -inline int CountWireRefsOfFace(const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) +inline uint32_t CountWireRefsOfFace(const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) { - return WireRefsOfFace(theGraph, theFaceId).Length(); + return static_cast(WireRefsOfFace(theGraph, theFaceId).Size()); } //================================================================================================= @@ -129,12 +115,12 @@ inline bool FaceUsesWire(const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId, const BRepGraph_WireId theWireId) { - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const NCollection_DynamicArray aWireRefs = + const BRepGraph::RefsView& aRefs = theGraph.Refs(); + const NCollection_LinearVector& aWireRefs = WireRefsOfFace(theGraph, theFaceId); for (const BRepGraph_WireRefId& aWireRefId : aWireRefs) { - if (aRefs.Wires().Entry(aWireRefId).WireDefId == theWireId) + if (aRefs.Wires().Entry(aWireRefId).ChildWireId == theWireId) { return true; } @@ -144,177 +130,113 @@ inline bool FaceUsesWire(const BRepGraph& theGraph, //================================================================================================= -inline NCollection_DynamicArray FaceRefsOfShell( +inline const NCollection_LinearVector& FaceRefsOfShell( const BRepGraph& theGraph, const BRepGraph_ShellId theShellId) { - NCollection_DynamicArray aRefIds; - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const BRepGraph_NodeId aParentNode = theShellId; - const int aNbFaceRefs = aRefs.Faces().Nb(); - for (BRepGraph_FaceRefId aRefId(0); aRefId.IsValid(aNbFaceRefs); ++aRefId) - { - const BRepGraphInc::FaceRef& aRef = aRefs.Faces().Entry(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theGraph.Refs().Faces().IdsOf(theShellId); } //================================================================================================= -inline int CountFaceRefsOfShell(const BRepGraph& theGraph, const BRepGraph_ShellId theShellId) +inline uint32_t CountFaceRefsOfShell(const BRepGraph& theGraph, const BRepGraph_ShellId theShellId) { - return FaceRefsOfShell(theGraph, theShellId).Length(); + return static_cast(FaceRefsOfShell(theGraph, theShellId).Size()); } //================================================================================================= -inline NCollection_DynamicArray ShellRefsOfSolid( +inline const NCollection_LinearVector& ShellRefsOfSolid( const BRepGraph& theGraph, const BRepGraph_SolidId theSolidId) { - NCollection_DynamicArray aRefIds; - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const BRepGraph_NodeId aParentNode = theSolidId; - const int aNbShellRefs = aRefs.Shells().Nb(); - for (BRepGraph_ShellRefId aRefId(0); aRefId.IsValid(aNbShellRefs); ++aRefId) - { - const BRepGraphInc::ShellRef& aRef = aRefs.Shells().Entry(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theGraph.Refs().Shells().IdsOf(theSolidId); } //================================================================================================= -inline int CountShellRefsOfSolid(const BRepGraph& theGraph, const BRepGraph_SolidId theSolidId) +inline uint32_t CountShellRefsOfSolid(const BRepGraph& theGraph, const BRepGraph_SolidId theSolidId) { - return ShellRefsOfSolid(theGraph, theSolidId).Length(); + return static_cast(ShellRefsOfSolid(theGraph, theSolidId).Size()); } //================================================================================================= -inline NCollection_DynamicArray SolidRefsOfCompSolid( +inline const NCollection_LinearVector& SolidRefsOfCompSolid( const BRepGraph& theGraph, const BRepGraph_CompSolidId theCompSolidId) { - NCollection_DynamicArray aRefIds; - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const BRepGraph_NodeId aParentNode = theCompSolidId; - const int aNbSolidRefs = aRefs.Solids().Nb(); - for (BRepGraph_SolidRefId aRefId(0); aRefId.IsValid(aNbSolidRefs); ++aRefId) - { - const BRepGraphInc::SolidRef& aRef = aRefs.Solids().Entry(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theGraph.Refs().Solids().IdsOf(theCompSolidId); } //================================================================================================= -inline int CountSolidRefsOfCompSolid(const BRepGraph& theGraph, - const BRepGraph_CompSolidId theCompSolidId) +inline uint32_t CountSolidRefsOfCompSolid(const BRepGraph& theGraph, + const BRepGraph_CompSolidId theCompSolidId) { - return SolidRefsOfCompSolid(theGraph, theCompSolidId).Length(); + return static_cast(SolidRefsOfCompSolid(theGraph, theCompSolidId).Size()); } //================================================================================================= -inline NCollection_DynamicArray ChildRefsOfParent( +inline const NCollection_LinearVector& ChildRefsOfParent( const BRepGraph& theGraph, const BRepGraph_NodeId theParentId) { - NCollection_DynamicArray aRefIds; - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const int aNbChildRefs = aRefs.Children().Nb(); - for (BRepGraph_ChildRefId aRefId(0); aRefId.IsValid(aNbChildRefs); ++aRefId) + if (theParentId.NodeKind != BRepGraph_NodeId::Kind::Compound) { - const BRepGraphInc::ChildRef& aRef = aRefs.Children().Entry(aRefId); - if (aRef.ParentId == theParentId && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } + return EmptyIds(); } - return aRefIds; + return theGraph.Refs().Children().IdsOf(BRepGraph_CompoundId(theParentId)); } //================================================================================================= -inline int CountChildRefsOfParent(const BRepGraph& theGraph, const BRepGraph_NodeId theParentId) +inline uint32_t CountChildRefsOfParent(const BRepGraph& theGraph, + const BRepGraph_NodeId theParentId) { - return ChildRefsOfParent(theGraph, theParentId).Length(); + return static_cast(ChildRefsOfParent(theGraph, theParentId).Size()); } //================================================================================================= inline BRepGraph_WireId OuterWireOfFace(const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) { - return theGraph.Topo().Faces().OuterWire(theFaceId); + return BRepGraph_Tool::Face::OuterWire(theGraph, theFaceId); } //================================================================================================= -inline NCollection_DynamicArray CoEdgeRefsOfWire( +inline const NCollection_LinearVector& CoEdgesOfWire( const BRepGraphInc_Storage& theStorage, const BRepGraph_WireId theWireId) { - NCollection_DynamicArray aRefIds; - const BRepGraph_NodeId aParentNode = theWireId; - const int aNbCoEdgeRefs = theStorage.NbCoEdgeRefs(); - for (BRepGraph_CoEdgeRefId aRefId(0); aRefId.IsValid(aNbCoEdgeRefs); ++aRefId) - { - const BRepGraphInc::CoEdgeRef& aRef = theStorage.CoEdgeRef(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theStorage.WireRelations(theWireId).CoEdgeIds; } //================================================================================================= -inline int CountCoEdgeRefsOfWire(const BRepGraphInc_Storage& theStorage, - const BRepGraph_WireId theWireId) +inline uint32_t CountCoEdgesOfWire(const BRepGraphInc_Storage& theStorage, + const BRepGraph_WireId theWireId) { - return CoEdgeRefsOfWire(theStorage, theWireId).Length(); + return static_cast(CoEdgesOfWire(theStorage, theWireId).Size()); } //================================================================================================= -inline NCollection_DynamicArray WireRefsOfFace( +inline const NCollection_LinearVector& WireRefsOfFace( const BRepGraphInc_Storage& theStorage, const BRepGraph_FaceId theFaceId) { - NCollection_DynamicArray aRefIds; - const BRepGraph_NodeId aParentNode = theFaceId; - const int aNbWireRefs = theStorage.NbWireRefs(); - for (BRepGraph_WireRefId aRefId(0); aRefId.IsValid(aNbWireRefs); ++aRefId) - { - const BRepGraphInc::WireRef& aRef = theStorage.WireRef(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theStorage.FaceRelations(theFaceId).WireRefIds; } //================================================================================================= -inline int CountWireRefsOfFace(const BRepGraphInc_Storage& theStorage, - const BRepGraph_FaceId theFaceId) +inline uint32_t CountWireRefsOfFace(const BRepGraphInc_Storage& theStorage, + const BRepGraph_FaceId theFaceId) { - return WireRefsOfFace(theStorage, theFaceId).Length(); + return static_cast(WireRefsOfFace(theStorage, theFaceId).Size()); } //================================================================================================= @@ -323,12 +245,12 @@ inline bool FaceUsesWire(const BRepGraphInc_Storage& theStorage, const BRepGraph_FaceId theFaceId, const BRepGraph_WireId theWireId) { - const NCollection_DynamicArray aWireRefs = + const NCollection_LinearVector& aWireRefs = WireRefsOfFace(theStorage, theFaceId); for (const BRepGraph_WireRefId& aWireRefId : aWireRefs) { const BRepGraphInc::WireRef& aWireRef = theStorage.WireRef(aWireRefId); - if (aWireRef.WireDefId == theWireId) + if (aWireRef.ChildWireId == theWireId) { return true; } @@ -338,113 +260,74 @@ inline bool FaceUsesWire(const BRepGraphInc_Storage& theStorage, //================================================================================================= -inline NCollection_DynamicArray FaceRefsOfShell( +inline const NCollection_LinearVector& FaceRefsOfShell( const BRepGraphInc_Storage& theStorage, const BRepGraph_ShellId theShellId) { - NCollection_DynamicArray aRefIds; - const BRepGraph_NodeId aParentNode = theShellId; - const int aNbFaceRefs = theStorage.NbFaceRefs(); - for (BRepGraph_FaceRefId aRefId(0); aRefId.IsValid(aNbFaceRefs); ++aRefId) - { - const BRepGraphInc::FaceRef& aRef = theStorage.FaceRef(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theStorage.ShellRelations(theShellId).FaceRefIds; } //================================================================================================= -inline int CountFaceRefsOfShell(const BRepGraphInc_Storage& theStorage, - const BRepGraph_ShellId theShellId) +inline uint32_t CountFaceRefsOfShell(const BRepGraphInc_Storage& theStorage, + const BRepGraph_ShellId theShellId) { - return FaceRefsOfShell(theStorage, theShellId).Length(); + return static_cast(FaceRefsOfShell(theStorage, theShellId).Size()); } //================================================================================================= -inline NCollection_DynamicArray ShellRefsOfSolid( +inline const NCollection_LinearVector& ShellRefsOfSolid( const BRepGraphInc_Storage& theStorage, const BRepGraph_SolidId theSolidId) { - NCollection_DynamicArray aRefIds; - const BRepGraph_NodeId aParentNode = theSolidId; - const int aNbShellRefs = theStorage.NbShellRefs(); - for (BRepGraph_ShellRefId aRefId(0); aRefId.IsValid(aNbShellRefs); ++aRefId) - { - const BRepGraphInc::ShellRef& aRef = theStorage.ShellRef(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theStorage.SolidRelations(theSolidId).ShellRefIds; } //================================================================================================= -inline int CountShellRefsOfSolid(const BRepGraphInc_Storage& theStorage, - const BRepGraph_SolidId theSolidId) +inline uint32_t CountShellRefsOfSolid(const BRepGraphInc_Storage& theStorage, + const BRepGraph_SolidId theSolidId) { - return ShellRefsOfSolid(theStorage, theSolidId).Length(); + return static_cast(ShellRefsOfSolid(theStorage, theSolidId).Size()); } //================================================================================================= -inline NCollection_DynamicArray SolidRefsOfCompSolid( +inline const NCollection_LinearVector& SolidRefsOfCompSolid( const BRepGraphInc_Storage& theStorage, const BRepGraph_CompSolidId theCompSolidId) { - NCollection_DynamicArray aRefIds; - const BRepGraph_NodeId aParentNode = theCompSolidId; - const int aNbSolidRefs = theStorage.NbSolidRefs(); - for (BRepGraph_SolidRefId aRefId(0); aRefId.IsValid(aNbSolidRefs); ++aRefId) - { - const BRepGraphInc::SolidRef& aRef = theStorage.SolidRef(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } - } - return aRefIds; + return theStorage.CompSolidRelations(theCompSolidId).SolidRefIds; } //================================================================================================= -inline int CountSolidRefsOfCompSolid(const BRepGraphInc_Storage& theStorage, - const BRepGraph_CompSolidId theCompSolidId) +inline uint32_t CountSolidRefsOfCompSolid(const BRepGraphInc_Storage& theStorage, + const BRepGraph_CompSolidId theCompSolidId) { - return SolidRefsOfCompSolid(theStorage, theCompSolidId).Length(); + return static_cast(SolidRefsOfCompSolid(theStorage, theCompSolidId).Size()); } //================================================================================================= -inline NCollection_DynamicArray ChildRefsOfParent( +inline const NCollection_LinearVector& ChildRefsOfParent( const BRepGraphInc_Storage& theStorage, const BRepGraph_NodeId theParentId) { - NCollection_DynamicArray aRefIds; - const int aNbChildRefs = theStorage.NbChildRefs(); - for (BRepGraph_ChildRefId aRefId(0); aRefId.IsValid(aNbChildRefs); ++aRefId) + if (theParentId.NodeKind != BRepGraph_NodeId::Kind::Compound) { - const BRepGraphInc::ChildRef& aRef = theStorage.ChildRef(aRefId); - if (aRef.ParentId == theParentId && !aRef.IsRemoved) - { - aRefIds.Append(aRefId); - } + return EmptyIds(); } - return aRefIds; + return theStorage.CompoundRelations(BRepGraph_CompoundId(theParentId)).ChildRefIds; } //================================================================================================= -inline int CountChildRefsOfParent(const BRepGraphInc_Storage& theStorage, - const BRepGraph_NodeId theParentId) +inline uint32_t CountChildRefsOfParent(const BRepGraphInc_Storage& theStorage, + const BRepGraph_NodeId theParentId) { - return ChildRefsOfParent(theStorage, theParentId).Length(); + return static_cast(ChildRefsOfParent(theStorage, theParentId).Size()); } //================================================================================================= @@ -452,15 +335,12 @@ inline int CountChildRefsOfParent(const BRepGraphInc_Storage& theStorage, inline BRepGraph_WireId OuterWireOfFace(const BRepGraphInc_Storage& theStorage, const BRepGraph_FaceId theFaceId) { - const NCollection_DynamicArray aWireRefs = + const NCollection_LinearVector& aWireRefs = WireRefsOfFace(theStorage, theFaceId); for (const BRepGraph_WireRefId& aWireRefId : aWireRefs) { const BRepGraphInc::WireRef& aWireRef = theStorage.WireRef(aWireRefId); - if (aWireRef.IsOuter) - { - return aWireRef.WireDefId; - } + return aWireRef.ChildWireId; } return BRepGraph_WireId(); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx index c00303b844..781e05d2d8 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RefsIterator_Test.cxx @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include @@ -37,9 +37,9 @@ namespace { template -static int countIterator(IteratorT theIterator) +static uint32_t countIterator(IteratorT theIterator) { - int aCount = 0; + uint32_t aCount = 0; for (; theIterator.More(); theIterator.Next()) { ++aCount; @@ -59,25 +59,6 @@ static TopoDS_Edge makeEdgeWithInternalVertex() return anEdge; } -static TopoDS_Face makeFaceWithDirectVertex() -{ - BRep_Builder aBuilder; - const occ::handle aPlane = new Geom_Plane(gp_Pln()); - TopoDS_Face aFace; - aBuilder.MakeFace(aFace, aPlane, Precision::Confusion()); - - BRepBuilderAPI_MakeEdge aMakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0)); - TopoDS_Wire aWire; - aBuilder.MakeWire(aWire); - aBuilder.Add(aWire, aMakeEdge.Edge()); - aBuilder.Add(aFace, aWire); - - TopoDS_Vertex aDirectVertex; - aBuilder.MakeVertex(aDirectVertex, gp_Pnt(5, 5, 0), Precision::Confusion()); - aBuilder.Add(aFace, aDirectVertex.Oriented(TopAbs_INTERNAL)); - return aFace; -} - static TopoDS_Face wrapEdgeInFace(const TopoDS_Edge& theEdge) { BRep_Builder aBuilder; @@ -99,8 +80,8 @@ protected: { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + myGraph.Shapes().Add(aBoxMaker.Shape()); } BRepGraph myGraph; @@ -111,7 +92,7 @@ TEST_F(BRepGraph_RefsIteratorTest, BoxHierarchy_YieldsReferenceIds) EXPECT_EQ(countIterator(BRepGraph_RefsShellOfSolid(myGraph, BRepGraph_SolidId::Start())), 1); EXPECT_EQ(countIterator(BRepGraph_RefsFaceOfShell(myGraph, BRepGraph_ShellId::Start())), 6); EXPECT_EQ(countIterator(BRepGraph_RefsWireOfFace(myGraph, BRepGraph_FaceId::Start())), 1); - EXPECT_EQ(countIterator(BRepGraph_RefsCoEdgeOfWire(myGraph, BRepGraph_WireId::Start())), 4); + EXPECT_EQ(countIterator(BRepGraph_CoEdgesOfWire(myGraph, BRepGraph_WireId::Start())), 4); EXPECT_EQ(countIterator(BRepGraph_RefsVertexOfEdge(myGraph, BRepGraph_EdgeId::Start())), 2); } @@ -121,31 +102,19 @@ TEST_F(BRepGraph_RefsIteratorTest, CurrentId_ResolvesToExpectedEntry) ASSERT_TRUE(anIt.More()); const BRepGraphInc::WireRef& aWireRef = myGraph.Refs().Wires().Entry(anIt.CurrentId()); - EXPECT_TRUE(aWireRef.WireDefId.IsValid(myGraph.Topo().Wires().Nb())); + EXPECT_TRUE(aWireRef.ChildWireId.IsValid(myGraph.Topo().Wires().Nb())); } -TEST(BRepGraph_RefsIteratorTestStandalone, VertexOfEdge_ExposesInternalVertexRef) +TEST(BRepGraph_RefsIteratorTestStandalone, VertexOfEdge_ExposesBoundaryVertexRefsOnly) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, wrapEdgeInFace(makeEdgeWithInternalVertex())); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(wrapEdgeInFace(makeEdgeWithInternalVertex())); - BRepGraph_EdgeId aEdgeWithInternal; - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - if (anEdgeIt.Current().InternalVertexRefIds.Length() == 1) - { - aEdgeWithInternal = anEdgeIt.CurrentId(); - break; - } - } - - ASSERT_TRUE(aEdgeWithInternal.IsValid()); - - bool aFoundInternal = false; - int aCount = 0; - for (BRepGraph_RefsVertexOfEdge anIt(aGraph, aEdgeWithInternal); anIt.More(); anIt.Next()) + bool aFoundInternal = false; + uint32_t aCount = 0; + for (BRepGraph_RefsVertexOfEdge anIt(aGraph, BRepGraph_EdgeId::Start()); anIt.More(); anIt.Next()) { ++aCount; const BRepGraphInc::VertexRef& aVertexRef = aGraph.Refs().Vertices().Entry(anIt.CurrentId()); @@ -155,23 +124,8 @@ TEST(BRepGraph_RefsIteratorTestStandalone, VertexOfEdge_ExposesInternalVertexRef } } - EXPECT_EQ(aCount, 3); - EXPECT_TRUE(aFoundInternal); -} - -TEST(BRepGraph_RefsIteratorTestStandalone, VertexOfFace_ExposesDirectVertexRef) -{ - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, makeFaceWithDirectVertex()); - - BRepGraph_RefsVertexOfFace anIt(aGraph, BRepGraph_FaceId::Start()); - ASSERT_TRUE(anIt.More()); - - const BRepGraphInc::VertexRef& aVertexRef = aGraph.Refs().Vertices().Entry(anIt.CurrentId()); - EXPECT_EQ(aVertexRef.Orientation, TopAbs_INTERNAL); - EXPECT_TRUE(aVertexRef.VertexDefId.IsValid(aGraph.Topo().Vertices().Nb())); + EXPECT_EQ(aCount, 2); + EXPECT_FALSE(aFoundInternal); } TEST_F(BRepGraph_RefsIteratorTest, ChildOfCompound_EnumeratesChildRefs) @@ -180,98 +134,96 @@ TEST_F(BRepGraph_RefsIteratorTest, ChildOfCompound_EnumeratesChildRefs) myGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 0.01); ASSERT_TRUE(aLooseVertex.IsValid()); - NCollection_DynamicArray aChildren; + NCollection_LinearVector aChildren; aChildren.Append(BRepGraph_SolidId::Start()); aChildren.Append(aLooseVertex); - const BRepGraph_CompoundId aCompound = myGraph.Editor().Compounds().Add(aChildren); + const BRepGraph_CompoundId aCompound = myGraph.Editor().Compounds().Add(aChildren.ToArray1()); ASSERT_TRUE(aCompound.IsValid()); BRepGraph_RefsChildOfCompound anIt(myGraph, aCompound); ASSERT_TRUE(anIt.More()); - EXPECT_EQ(myGraph.Refs().Children().Entry(anIt.CurrentId()).ChildDefId.NodeKind, + EXPECT_EQ(myGraph.Refs().Children().Entry(anIt.CurrentId()).ChildNodeId.NodeKind, BRepGraph_NodeId::Kind::Solid); anIt.Next(); ASSERT_TRUE(anIt.More()); - EXPECT_EQ(myGraph.Refs().Children().Entry(anIt.CurrentId()).ChildDefId.NodeKind, + EXPECT_EQ(myGraph.Refs().Children().Entry(anIt.CurrentId()).ChildNodeId.NodeKind, BRepGraph_NodeId::Kind::Vertex); } +TEST_F(BRepGraph_RefsIteratorTest, ChildOfCompound_SkipsOutOfRangeChildNode) +{ + const BRepGraph_VertexId aLooseVertex = + myGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 0.01); + ASSERT_TRUE(aLooseVertex.IsValid()); + + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_SolidId::Start()); + aChildren.Append(aLooseVertex); + aChildren.Append(aLooseVertex); + const BRepGraph_CompoundId aCompound = myGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + const NCollection_LinearVector& aChildRefs = + myGraph.Topo().Compounds().Relations(aCompound).ChildRefIds; + ASSERT_EQ(aChildRefs.Size(), 3); + { + BRepGraph_MutGuard aBadRef = + myGraph.Editor().Gen().MutChildRef(aChildRefs.Value(1)); + aBadRef.Internal().ChildNodeId = + BRepGraph_NodeId(BRepGraph_NodeId::Kind::Vertex, myGraph.Topo().Vertices().Nb()); + } + + uint32_t aCount = 0; + for (BRepGraph_RefsChildOfCompound anIt(myGraph, aCompound); anIt.More(); anIt.Next()) + { + const BRepGraph_NodeId aChildNode = + myGraph.Refs().Children().Entry(anIt.CurrentId()).ChildNodeId; + EXPECT_TRUE(myGraph.Topo().Gen().IsActive(aChildNode)); + ++aCount; + } + EXPECT_EQ(aCount, 2u); +} + +TEST_F(BRepGraph_RefsIteratorTest, ChildOfCompound_SkipsRemovedChildNode) +{ + const BRepGraph_VertexId aLooseVertex = + myGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 0.01); + ASSERT_TRUE(aLooseVertex.IsValid()); + + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_SolidId::Start()); + aChildren.Append(aLooseVertex); + const BRepGraph_CompoundId aCompound = myGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + myGraph.Editor().Gen().RemoveNode(aLooseVertex); + + ASSERT_TRUE(BRepGraph_RefsChildOfCompound(myGraph, aCompound).More()); + EXPECT_EQ(countIterator(BRepGraph_RefsChildOfCompound(myGraph, aCompound)), 1u); +} + TEST_F(BRepGraph_RefsIteratorTest, OccurrenceOfProduct_EnumeratesOccurrenceRefs) { - const BRepGraph_ProductId aPart = - myGraph.Editor().Products().LinkProductToTopology(BRepGraph_SolidId::Start()); - const BRepGraph_ProductId anAssembly = myGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aPart = myGraph.Editor().Products().Add(BRepGraph_SolidId::Start()); + myGraph.Editor().Products().AppendDocumentRoot(aPart); + const BRepGraph_ProductId anAssembly = myGraph.Editor().Products().Add(); + myGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(aPart.IsValid()); ASSERT_TRUE(anAssembly.IsValid()); - EXPECT_TRUE( - myGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location()).IsValid()); - EXPECT_TRUE( - myGraph.Editor().Products().LinkProducts(anAssembly, aPart, TopLoc_Location()).IsValid()); + EXPECT_TRUE(myGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()).IsValid()); + EXPECT_TRUE(myGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()).IsValid()); EXPECT_EQ(countIterator(BRepGraph_RefsOccurrenceOfProduct(myGraph, anAssembly)), 2); } -TEST_F(BRepGraph_RefsIteratorTest, AuxChildRefsOfShellAndSolid_EnumerateInjectedChildRefs) -{ - NCollection_DynamicArray aShellChildren; - aShellChildren.Append(BRepGraph_WireId::Start()); - aShellChildren.Append(BRepGraph_EdgeId::Start()); - const BRepGraph_CompoundId aShellSeed = myGraph.Editor().Compounds().Add(aShellChildren); - ASSERT_TRUE(aShellSeed.IsValid()); - - { - BRepGraph_MutGuard aShell = - myGraph.Editor().Shells().Mut(BRepGraph_ShellId::Start()); - for (const BRepGraph_ChildRefId& aRefId : - myGraph.Topo().Compounds().Definition(aShellSeed).ChildRefIds) - { - aShell.Internal().AuxChildRefIds.Append(aRefId); - } - } - - BRepGraph_RefsChildOfShell aShellIt(myGraph, BRepGraph_ShellId::Start()); - ASSERT_TRUE(aShellIt.More()); - EXPECT_EQ(myGraph.Refs().Children().Entry(aShellIt.CurrentId()).ChildDefId.NodeKind, - BRepGraph_NodeId::Kind::Wire); - aShellIt.Next(); - ASSERT_TRUE(aShellIt.More()); - EXPECT_EQ(myGraph.Refs().Children().Entry(aShellIt.CurrentId()).ChildDefId.NodeKind, - BRepGraph_NodeId::Kind::Edge); - - NCollection_DynamicArray aSolidChildren; - aSolidChildren.Append(BRepGraph_EdgeId(1)); - aSolidChildren.Append(BRepGraph_VertexId::Start()); - const BRepGraph_CompoundId aSolidSeed = myGraph.Editor().Compounds().Add(aSolidChildren); - ASSERT_TRUE(aSolidSeed.IsValid()); - - { - BRepGraph_MutGuard aSolid = - myGraph.Editor().Solids().Mut(BRepGraph_SolidId::Start()); - for (const BRepGraph_ChildRefId& aRefId : - myGraph.Topo().Compounds().Definition(aSolidSeed).ChildRefIds) - { - aSolid.Internal().AuxChildRefIds.Append(aRefId); - } - } - - BRepGraph_RefsChildOfSolid aSolidIt(myGraph, BRepGraph_SolidId::Start()); - ASSERT_TRUE(aSolidIt.More()); - EXPECT_EQ(myGraph.Refs().Children().Entry(aSolidIt.CurrentId()).ChildDefId.NodeKind, - BRepGraph_NodeId::Kind::Edge); - aSolidIt.Next(); - ASSERT_TRUE(aSolidIt.More()); - EXPECT_EQ(myGraph.Refs().Children().Entry(aSolidIt.CurrentId()).ChildDefId.NodeKind, - BRepGraph_NodeId::Kind::Vertex); -} - TEST_F(BRepGraph_RefsIteratorTest, RemovedWireRef_IsSkipped) { - const NCollection_DynamicArray& aWireRefs = + const NCollection_LinearVector& aWireRefs = myGraph.Refs().Wires().IdsOf(BRepGraph_FaceId::Start()); - ASSERT_EQ(aWireRefs.Length(), 1); + ASSERT_EQ(aWireRefs.Size(), 1); myGraph.Editor().Gen().RemoveRef(aWireRefs.Value(0)); EXPECT_EQ(countIterator(BRepGraph_RefsWireOfFace(myGraph, BRepGraph_FaceId::Start())), 0); -} \ No newline at end of file +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RelatedIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RelatedIterator_Test.cxx index 4051de8e26..e6f7320103 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_RelatedIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RelatedIterator_Test.cxx @@ -15,8 +15,9 @@ #include #include #include +#include #include -#include +#include #include #include @@ -46,11 +47,11 @@ static bool hasRelatedNode(const BRepGraph& theGrap return false; } -static int countRelations(const BRepGraph& theGraph, - const BRepGraph_NodeId theNode, - const BRepGraph_RelatedIterator::RelationKind theRelation) +static uint32_t countRelations(const BRepGraph& theGraph, + const BRepGraph_NodeId theNode, + const BRepGraph_RelatedIterator::RelationKind theRelation) { - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_RelatedIterator anIt(theGraph, theNode); anIt.More(); anIt.Next()) { if (anIt.CurrentRelation() == theRelation) @@ -69,8 +70,8 @@ protected: { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + myGraph.Shapes().Add(aBoxMaker.Shape()); } BRepGraph myGraph; @@ -80,17 +81,18 @@ TEST_F(BRepGraph_RelatedIteratorTest, FaceOfBox_ReturnsBoundaryEdgesAndOuterWire { const BRepGraph_FaceId aFaceId(0); - const int aBoundaryEdgeCount = + const uint32_t aBoundaryEdgeCount = countRelations(myGraph, BRepGraph_NodeId(aFaceId), BRepGraph_RelatedIterator::RelationKind::BoundaryEdge); - const int anAdjacentFaceCount = + const uint32_t anAdjacentFaceCount = countRelations(myGraph, BRepGraph_NodeId(aFaceId), BRepGraph_RelatedIterator::RelationKind::AdjacentFace); - const int anOuterWireCount = countRelations(myGraph, - BRepGraph_NodeId(aFaceId), - BRepGraph_RelatedIterator::RelationKind::OuterWire); + const uint32_t anOuterWireCount = + countRelations(myGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_RelatedIterator::RelationKind::OuterWire); EXPECT_EQ(aBoundaryEdgeCount, 4); EXPECT_EQ(anAdjacentFaceCount, 4); @@ -105,12 +107,14 @@ TEST_F(BRepGraph_RelatedIteratorTest, EdgeOfBox_ReturnsIncidentVerticesAndFaces) { const BRepGraph_EdgeId anEdgeId(0); - const int aVertexCount = countRelations(myGraph, - BRepGraph_NodeId(anEdgeId), - BRepGraph_RelatedIterator::RelationKind::IncidentVertex); - const int aFaceCount = countRelations(myGraph, - BRepGraph_NodeId(anEdgeId), - BRepGraph_RelatedIterator::RelationKind::ReferencedByFace); + const uint32_t aVertexCount = + countRelations(myGraph, + BRepGraph_NodeId(anEdgeId), + BRepGraph_RelatedIterator::RelationKind::IncidentVertex); + const uint32_t aFaceCount = + countRelations(myGraph, + BRepGraph_NodeId(anEdgeId), + BRepGraph_RelatedIterator::RelationKind::ReferencedByFace); EXPECT_EQ(aVertexCount, 2); EXPECT_EQ(aFaceCount, 2); @@ -120,13 +124,15 @@ TEST_F(BRepGraph_RelatedIteratorTest, AssemblyNodes_YieldNoRelations) { // Assembly/container nodes have no topological relations - use ChildExplorer instead. const BRepGraph_ProductId aPartProduct = - myGraph.Editor().Products().LinkProductToTopology(BRepGraph_NodeId(BRepGraph_SolidId::Start())); - const BRepGraph_ProductId aRootAssembly = myGraph.Editor().Products().CreateEmptyProduct(); + myGraph.Editor().Products().Add(BRepGraph_NodeId(BRepGraph_SolidId::Start())); + myGraph.Editor().Products().AppendDocumentRoot(aPartProduct); + const BRepGraph_ProductId aRootAssembly = myGraph.Editor().Products().Add(); + myGraph.Editor().Products().AppendDocumentRoot(aRootAssembly); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); const BRepGraph_OccurrenceId anOccurrenceId = - myGraph.Editor().Products().LinkProducts(aRootAssembly, aPartProduct, TopLoc_Location(aTrsf)); + myGraph.Editor().Products().Append(aRootAssembly, aPartProduct, TopLoc_Location(aTrsf)); ASSERT_TRUE(anOccurrenceId.IsValid()); BRepGraph_RelatedIterator aProductIt(myGraph, BRepGraph_NodeId(aRootAssembly)); @@ -150,18 +156,23 @@ TEST_F(BRepGraph_RelatedIteratorTest, EdgeReferencedByFace_RemovedFaceIsSkipped) const BRepGraph_EdgeId anEdgeId(0); // Find the two faces that reference this edge. - const NCollection_DynamicArray& aFaces = myGraph.Topo().Edges().Faces(anEdgeId); - ASSERT_EQ(aFaces.Length(), 2); + BRepGraph_FacesOfEdge aFaceIt = myGraph.Topo().Edges().FacesOf(anEdgeId); + ASSERT_TRUE(aFaceIt.More()); + const BRepGraph_FaceId aRemovedFace = aFaceIt.CurrentId(); + aFaceIt.Next(); + ASSERT_TRUE(aFaceIt.More()); + const BRepGraph_FaceId aSurvivor = aFaceIt.CurrentId(); + aFaceIt.Next(); + ASSERT_FALSE(aFaceIt.More()); // Remove the first face. - const BRepGraph_FaceId aRemovedFace = aFaces.Value(0); - const BRepGraph_FaceId aSurvivor = aFaces.Value(1); myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aRemovedFace)); // The edge should report exactly 1 ReferencedByFace (the surviving face). - const int aFaceCount = countRelations(myGraph, - BRepGraph_NodeId(anEdgeId), - BRepGraph_RelatedIterator::RelationKind::ReferencedByFace); + const uint32_t aFaceCount = + countRelations(myGraph, + BRepGraph_NodeId(anEdgeId), + BRepGraph_RelatedIterator::RelationKind::ReferencedByFace); EXPECT_EQ(aFaceCount, 1); EXPECT_TRUE(hasRelatedNode(myGraph, BRepGraph_NodeId(anEdgeId), @@ -182,27 +193,27 @@ TEST_F(BRepGraph_RelatedIteratorTest, ContainerNodes_YieldNoRelations) TEST_F(BRepGraph_RelatedIteratorTest, WireOfBox_ReturnsCoEdges) { const BRepGraph_WireId aWireId(0); - const int aCount = countRelations(myGraph, - BRepGraph_NodeId(aWireId), - BRepGraph_RelatedIterator::RelationKind::WireCoEdge); + const uint32_t aCount = countRelations(myGraph, + BRepGraph_NodeId(aWireId), + BRepGraph_RelatedIterator::RelationKind::WireCoEdge); EXPECT_EQ(aCount, 4); } TEST_F(BRepGraph_RelatedIteratorTest, WireOfBox_ReturnsOwningFace) { const BRepGraph_WireId aWireId(0); - const int aCount = countRelations(myGraph, - BRepGraph_NodeId(aWireId), - BRepGraph_RelatedIterator::RelationKind::OwningFace); + const uint32_t aCount = countRelations(myGraph, + BRepGraph_NodeId(aWireId), + BRepGraph_RelatedIterator::RelationKind::OwningFace); EXPECT_EQ(aCount, 1); } TEST_F(BRepGraph_RelatedIteratorTest, VertexOfBox_ReturnsIncidentEdges) { const BRepGraph_VertexId aVertexId(0); - const int aCount = countRelations(myGraph, - BRepGraph_NodeId(aVertexId), - BRepGraph_RelatedIterator::RelationKind::IncidentEdge); + const uint32_t aCount = countRelations(myGraph, + BRepGraph_NodeId(aVertexId), + BRepGraph_RelatedIterator::RelationKind::IncidentEdge); // Each vertex of a box touches exactly 3 edges. EXPECT_EQ(aCount, 3); } @@ -210,12 +221,14 @@ TEST_F(BRepGraph_RelatedIteratorTest, VertexOfBox_ReturnsIncidentEdges) TEST_F(BRepGraph_RelatedIteratorTest, CoEdgeOfBox_ReturnsParentEdgeAndOwningFace) { const BRepGraph_CoEdgeId aCoEdgeId(0); - const int aParentEdgeCount = countRelations(myGraph, - BRepGraph_NodeId(aCoEdgeId), - BRepGraph_RelatedIterator::RelationKind::ParentEdge); - const int aOwningFaceCount = countRelations(myGraph, - BRepGraph_NodeId(aCoEdgeId), - BRepGraph_RelatedIterator::RelationKind::OwningFace); + const uint32_t aParentEdgeCount = + countRelations(myGraph, + BRepGraph_NodeId(aCoEdgeId), + BRepGraph_RelatedIterator::RelationKind::ParentEdge); + const uint32_t aOwningFaceCount = + countRelations(myGraph, + BRepGraph_NodeId(aCoEdgeId), + BRepGraph_RelatedIterator::RelationKind::OwningFace); EXPECT_EQ(aParentEdgeCount, 1); EXPECT_EQ(aOwningFaceCount, 1); } @@ -230,9 +243,8 @@ TEST(BRepGraph_RelatedIteratorStandalone, Compound_YieldsNoRelations) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_CompoundId aCompoundId; for (BRepGraph_CompoundIterator anIt(aGraph); anIt.More(); anIt.Next()) @@ -249,7 +261,7 @@ TEST(BRepGraph_RelatedIteratorStandalone, Compound_YieldsNoRelations) TEST_F(BRepGraph_RelatedIteratorTest, RangeFor_WorksCorrectly) { const BRepGraph_FaceId aFaceId(0); - int aCount = 0; + uint32_t aCount = 0; for (const BRepGraph_NodeId& aNode : BRepGraph_RelatedIterator(myGraph, BRepGraph_NodeId(aFaceId))) { @@ -281,14 +293,14 @@ TEST_F(BRepGraph_RelatedIteratorTest, EdgeOfBox_AllRelationsSequential) } } - EXPECT_EQ(aFaces.Length(), 2); - EXPECT_EQ(aVertices.Length(), 2); + EXPECT_EQ(aFaces.Size(), 2); + EXPECT_EQ(aVertices.Size(), 2); // All yielded nodes must be distinct. - if (aFaces.Length() == 2) + if (aFaces.Size() == 2) { EXPECT_NE(aFaces.Value(0), aFaces.Value(1)); } - if (aVertices.Length() == 2) + if (aVertices.Size() == 2) { EXPECT_NE(aVertices.Value(0), aVertices.Value(1)); } @@ -300,15 +312,19 @@ TEST_F(BRepGraph_RelatedIteratorTest, EdgeOfBox_RemovedFace_CorrectTransition) const BRepGraph_NodeId anEdgeNode(anEdgeId); // Remove one parent face. - const NCollection_DynamicArray& aParentFaces = - myGraph.Topo().Edges().Faces(anEdgeId); - ASSERT_EQ(aParentFaces.Length(), 2); - myGraph.Editor().Gen().RemoveNode(aParentFaces.Value(0)); + BRepGraph_FacesOfEdge aParentFaceIt = myGraph.Topo().Edges().FacesOf(anEdgeId); + ASSERT_TRUE(aParentFaceIt.More()); + const BRepGraph_FaceId aRemovedFace = aParentFaceIt.CurrentId(); + aParentFaceIt.Next(); + ASSERT_TRUE(aParentFaceIt.More()); + aParentFaceIt.Next(); + ASSERT_FALSE(aParentFaceIt.More()); + myGraph.Editor().Gen().RemoveNode(aRemovedFace); // Iterate - expect 1 face + 2 vertices, confirming correct stage transition. - const int aFaceCount = + const uint32_t aFaceCount = countRelations(myGraph, anEdgeNode, BRepGraph_RelatedIterator::RelationKind::ReferencedByFace); - const int aVertexCount = + const uint32_t aVertexCount = countRelations(myGraph, anEdgeNode, BRepGraph_RelatedIterator::RelationKind::IncidentVertex); EXPECT_EQ(aFaceCount, 1); EXPECT_EQ(aVertexCount, 2); @@ -329,13 +345,13 @@ TEST_F(BRepGraph_RelatedIteratorTest, VertexOfBox_AllParentEdgesYielded) } } - EXPECT_EQ(anEdges.Length(), 3); + EXPECT_EQ(anEdges.Size(), 3); // All yielded edges must be distinct. - for (int i = 0; i < anEdges.Length(); ++i) + for (size_t i = 0; i < anEdges.Size(); ++i) { - for (int j = i + 1; j < anEdges.Length(); ++j) + for (size_t j = i + 1; j < anEdges.Size(); ++j) { EXPECT_NE(anEdges.Value(i), anEdges.Value(j)); } } -} \ No newline at end of file +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_RepId_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_RepId_Test.cxx new file mode 100644 index 0000000000..3d389fd85b --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_RepId_Test.cxx @@ -0,0 +1,106 @@ +// 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 + +#include + +TEST(BRepGraph_RepIdTest, DefaultRepId_IsInvalid) +{ + const BRepGraph_RepId anId; + EXPECT_FALSE(anId.IsValid()); +} + +TEST(BRepGraph_RepIdTest, TypedDefaultRepId_IsInvalid) +{ + const BRepGraph_EdgeCurve3DRepId aCurve; + const BRepGraph_FaceSurfaceRepId aSurface; + const BRepGraph_CoEdgeCurve2DRepId aPCurve; + const BRepGraph_FaceTriangulationRepId aTri; + EXPECT_FALSE(aCurve.IsValid()); + EXPECT_FALSE(aSurface.IsValid()); + EXPECT_FALSE(aPCurve.IsValid()); + EXPECT_FALSE(aTri.IsValid()); +} + +TEST(BRepGraph_RepIdTest, TypedRepId_ConvertsToUntyped) +{ + const BRepGraph_EdgeCurve3DRepId aTyped(42); + const BRepGraph_RepId anUntyped = aTyped; + EXPECT_EQ(anUntyped.RepKind, BRepGraph_RepId::Kind::EdgeCurve3D); + EXPECT_EQ(anUntyped.Index, 42u); +} + +TEST(BRepGraph_RepIdTest, KindClassification) +{ + EXPECT_TRUE(BRepGraph_RepId::IsValidKind(BRepGraph_RepId::Kind::EdgeCurve3D)); + EXPECT_TRUE(BRepGraph_RepId::IsValidKind(BRepGraph_RepId::Kind::FaceSurface)); + EXPECT_TRUE(BRepGraph_RepId::IsValidKind(BRepGraph_RepId::Kind::CoEdgeCurve2D)); + EXPECT_TRUE(BRepGraph_RepId::IsValidKind(BRepGraph_RepId::Kind::FaceTriangulation)); +} + +TEST(BRepGraph_RepIdTest, ValidRepId_PassesBoundsCheck) +{ + const BRepGraph_FaceSurfaceRepId anId(5); + EXPECT_TRUE(anId.IsValid()); + EXPECT_TRUE(anId.IsValid(10)); + EXPECT_FALSE(anId.IsValid(3)); +} + +TEST(BRepGraph_RepIdTest, StartId_IsZero) +{ + const BRepGraph_EdgeCurve3DRepId aStart = BRepGraph_EdgeCurve3DRepId::Start(); + EXPECT_TRUE(aStart.IsValid()); + EXPECT_EQ(aStart.Index, 0u); +} + +TEST(BRepGraph_RepIdTest, EqualityAndComparison) +{ + const BRepGraph_EdgeCurve3DRepId a1(1); + const BRepGraph_EdgeCurve3DRepId a2(2); + const BRepGraph_EdgeCurve3DRepId a1Copy(1); + EXPECT_EQ(a1, a1Copy); + EXPECT_NE(a1, a2); + EXPECT_LT(a1, a2); + EXPECT_LE(a1, a1Copy); + EXPECT_GT(a2, a1); + EXPECT_GE(a1Copy, a1); +} + +TEST(BRepGraph_RepIdTest, DifferentKinds_AreNotEqual) +{ + const BRepGraph_RepId aCurve(BRepGraph_RepId::Kind::EdgeCurve3D, 0); + const BRepGraph_RepId aSurface(BRepGraph_RepId::Kind::FaceSurface, 0); + EXPECT_NE(aCurve, aSurface); +} + +TEST(BRepGraph_RepIdTest, Increment) +{ + BRepGraph_EdgeCurve3DRepId anId = BRepGraph_EdgeCurve3DRepId::Start(); + EXPECT_EQ(anId.Index, 0u); + ++anId; + EXPECT_EQ(anId.Index, 1u); + anId++; + EXPECT_EQ(anId.Index, 2u); +} + +TEST(BRepGraph_RepIdTest, AllTypedAliases_DefaultInvalid) +{ + EXPECT_FALSE(BRepGraph_EdgeCurve3DRepId().IsValid()); + EXPECT_FALSE(BRepGraph_EdgePolygon3DRepId().IsValid()); + EXPECT_FALSE(BRepGraph_CoEdgeCurve2DRepId().IsValid()); + EXPECT_FALSE(BRepGraph_CoEdgePolygon2DRepId().IsValid()); + EXPECT_FALSE(BRepGraph_CoEdgePolygonOnTriRepId().IsValid()); + EXPECT_FALSE(BRepGraph_FaceSurfaceRepId().IsValid()); + EXPECT_FALSE(BRepGraph_FaceTriangulationRepId().IsValid()); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ReplaceVertex_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ReplaceVertex_Test.cxx index e8305792cf..7a1357a575 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ReplaceVertex_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ReplaceVertex_Test.cxx @@ -15,7 +15,7 @@ // remap without a full edge rebuild. #include -#include +#include #include #include #include @@ -33,8 +33,8 @@ BRepGraph makeBoxGraph() { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); return aGraph; } @@ -43,7 +43,7 @@ BRepGraph makeBoxGraph() TEST(BRepGraph_ReplaceVertexTest, StartVertex_SwappedToFreshVertex_AuditClean) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); const BRepGraph_EdgeId anEdgeId(0); @@ -64,9 +64,8 @@ TEST(BRepGraph_ReplaceVertexTest, StartVertex_SwappedToFreshVertex_AuditClean) EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdgeId).StartVertexRefId, aNewRefId); // Old ref is retired, new ref points at the replacement vertex. - EXPECT_TRUE(aGraph.Refs().Vertices().Entry(anOldStartRefId).IsRemoved); - EXPECT_EQ(aGraph.Refs().Vertices().Entry(aNewRefId).VertexDefId, aNewVertex); - EXPECT_EQ(aGraph.Refs().Vertices().Entry(aNewRefId).ParentId, BRepGraph_NodeId(anEdgeId)); + EXPECT_TRUE(anOldStartRefId.IsRemoved(aGraph)); + EXPECT_EQ(aGraph.Refs().Vertices().Entry(aNewRefId).ChildVertexId, aNewVertex); // Lightweight only; a single-edge replacement breaks wire connectivity. EXPECT_TRUE( @@ -76,7 +75,7 @@ TEST(BRepGraph_ReplaceVertexTest, StartVertex_SwappedToFreshVertex_AuditClean) TEST(BRepGraph_ReplaceVertexTest, EndVertex_SwappedToFreshVertex_PreservesOrientation) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_EdgeId anEdgeId(0); const BRepGraph_VertexRefId anOldEndRefId = @@ -92,7 +91,7 @@ TEST(BRepGraph_ReplaceVertexTest, EndVertex_SwappedToFreshVertex_PreservesOrient EXPECT_EQ(aGraph.Refs().Vertices().Entry(aNewRefId).Orientation, aExpectedOri) << "ReplaceVertex must preserve orientation of the retired ref"; - // Lightweight validate covers structural invariants (reverse index, active + // Lightweight validate covers structural invariants (relation tables, active // counts) but skips wire-connectivity. A single-edge vertex replacement // deliberately breaks wire connectivity at the shared corner; the full // Audit would reject it. Callers that want a connectivity-safe replace @@ -104,25 +103,25 @@ TEST(BRepGraph_ReplaceVertexTest, EndVertex_SwappedToFreshVertex_PreservesOrient TEST(BRepGraph_ReplaceVertexTest, SameVertex_Idempotent) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_EdgeId anEdgeId(0); const BRepGraph_VertexRefId anOldStartRefId = aGraph.Topo().Edges().Definition(anEdgeId).StartVertexRefId; const BRepGraph_VertexId aSameVertex = - aGraph.Refs().Vertices().Entry(anOldStartRefId).VertexDefId; + aGraph.Refs().Vertices().Entry(anOldStartRefId).ChildVertexId; const BRepGraph_VertexRefId aResult = aGraph.Editor().Edges().ReplaceVertex(anEdgeId, anOldStartRefId, aSameVertex); EXPECT_EQ(aResult, anOldStartRefId) << "Replacing with the same vertex must be a no-op returning the same ref"; - EXPECT_FALSE(aGraph.Refs().Vertices().Entry(anOldStartRefId).IsRemoved); + EXPECT_FALSE(anOldStartRefId.IsRemoved(aGraph)); } TEST(BRepGraph_ReplaceVertexTest, InactiveEdge_Rejected) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_EdgeId anEdgeId(0); const BRepGraph_VertexRefId anOldRefId = @@ -141,7 +140,7 @@ TEST(BRepGraph_ReplaceVertexTest, InactiveEdge_Rejected) TEST(BRepGraph_ReplaceVertexTest, WrongParent_Rejected) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Edges().Nb(), 1); // Vertex ref from edge 1 passed to an API call targeting edge 0 must fail. @@ -156,18 +155,18 @@ TEST(BRepGraph_ReplaceVertexTest, WrongParent_Rejected) const BRepGraph_VertexRefId aResult = aGraph.Editor().Edges().ReplaceVertex(anEdge0, aEdge1StartRefId, aNewVertex); EXPECT_FALSE(aResult.IsValid()) - << "ReplaceVertex must refuse a ref whose ParentId is a different edge"; + << "ReplaceVertex must refuse a ref that is not in the requested edge slots"; } -TEST(BRepGraph_ReplaceVertexTest, ReverseIndex_Rebuilt_VertexToEdge) +TEST(BRepGraph_ReplaceVertexTest, Relations_Rebuilt_VertexToEdge) { BRepGraph aGraph = makeBoxGraph(); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_EdgeId anEdgeId(0); const BRepGraph_VertexRefId anOldRefId = aGraph.Topo().Edges().Definition(anEdgeId).StartVertexRefId; - const BRepGraph_VertexId aOldVertexId = aGraph.Refs().Vertices().Entry(anOldRefId).VertexDefId; + const BRepGraph_VertexId aOldVertexId = aGraph.Refs().Vertices().Entry(anOldRefId).ChildVertexId; const BRepGraph_VertexId aNewVertex = aGraph.Editor().Vertices().Add(gp_Pnt(1000.0, 0.0, 0.0), 1.0e-7); @@ -175,7 +174,7 @@ TEST(BRepGraph_ReplaceVertexTest, ReverseIndex_Rebuilt_VertexToEdge) ASSERT_TRUE(aGraph.Editor().Edges().ReplaceVertex(anEdgeId, anOldRefId, aNewVertex).IsValid()); // New vertex must now see anEdgeId in its vertex-to-edge reverse map. - const NCollection_DynamicArray& aNewEdges = + const NCollection_LinearVector& aNewEdges = aGraph.Topo().Vertices().Edges(aNewVertex); bool aFoundNew = false; for (const BRepGraph_EdgeId& anE : aNewEdges) @@ -186,15 +185,24 @@ TEST(BRepGraph_ReplaceVertexTest, ReverseIndex_Rebuilt_VertexToEdge) break; } } - EXPECT_TRUE(aFoundNew) << "Vertex->Edge reverse index must include the replacement edge"; + EXPECT_TRUE(aFoundNew) << "Vertex->Edge relation tables must include the replacement edge"; // Old vertex may still be referenced by other edges of the box (shared // corners); but it should no longer see anEdgeId through this slot. - const NCollection_DynamicArray& aOldEdges = + const NCollection_LinearVector& aOldEdges = aGraph.Topo().Vertices().Edges(aOldVertexId); - (void)aOldEdges; + bool aFoundOld = false; + for (const BRepGraph_EdgeId& anE : aOldEdges) + { + if (anE == anEdgeId) + { + aFoundOld = true; + break; + } + } + EXPECT_FALSE(aFoundOld) << "Vertex->Edge relation tables must drop the replaced edge"; - // Lightweight validate covers structural invariants (reverse index, active + // Lightweight validate covers structural invariants (relation tables, active // counts) but skips wire-connectivity. A single-edge vertex replacement // deliberately breaks wire connectivity at the shared corner; the full // Audit would reject it. Callers that want a connectivity-safe replace diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ReverseIterator_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ReverseIterator_Test.cxx index 7e04812a3a..878dffac9b 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ReverseIterator_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ReverseIterator_Test.cxx @@ -16,18 +16,22 @@ #include #include #include -#include +#include #include +#include +#include +#include +#include #include namespace { template -static int countIterator(IteratorT theIterator) +static uint32_t countIterator(IteratorT theIterator) { - int aCount = 0; + uint32_t aCount = 0; for (; theIterator.More(); theIterator.Next()) { ++aCount; @@ -43,8 +47,8 @@ protected: { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + myGraph.Shapes().Add(aBoxMaker.Shape()); } BRepGraph myGraph; @@ -54,8 +58,7 @@ TEST_F(BRepGraph_ReverseIteratorTest, FacesOfEdge_BoxEdgeSharedByTwoFaces) { // Every edge of a box is shared by exactly 2 faces. const BRepGraph_EdgeId anEdgeId(0); - const int aCount = - countIterator(BRepGraph_FacesOfEdge(myGraph, myGraph.Topo().Edges().Faces(anEdgeId))); + const uint32_t aCount = countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)); EXPECT_EQ(aCount, 2); } @@ -63,7 +66,7 @@ TEST_F(BRepGraph_ReverseIteratorTest, EdgesOfVertex_BoxVertexSharedByThreeEdges) { // Every vertex of a box is shared by exactly 3 edges. const BRepGraph_VertexId aVertexId(0); - const int aCount = + const uint32_t aCount = countIterator(BRepGraph_EdgesOfVertex(myGraph, myGraph.Topo().Vertices().Edges(aVertexId))); EXPECT_EQ(aCount, 3); } @@ -71,70 +74,73 @@ TEST_F(BRepGraph_ReverseIteratorTest, EdgesOfVertex_BoxVertexSharedByThreeEdges) TEST_F(BRepGraph_ReverseIteratorTest, SolidsOfShell_BoxShellHasOneSolid) { const BRepGraph_ShellId aShellId(0); - const int aCount = - countIterator(BRepGraph_SolidsOfShell(myGraph, myGraph.Topo().Shells().Solids(aShellId))); + const uint32_t aCount = countIterator( + BRepGraph_SolidsOfShell(myGraph, + myGraph.Topo().Shells().Relations(aShellId).ParentShellRefIds)); EXPECT_EQ(aCount, 1); } TEST_F(BRepGraph_ReverseIteratorTest, ShellsOfFace_BoxFaceHasOneShell) { const BRepGraph_FaceId aFaceId(0); - const int aCount = - countIterator(BRepGraph_ShellsOfFace(myGraph, myGraph.Topo().Faces().Shells(aFaceId))); + const uint32_t aCount = countIterator( + BRepGraph_ShellsOfFace(myGraph, myGraph.Topo().Faces().Relations(aFaceId).ParentFaceRefIds)); EXPECT_EQ(aCount, 1); } TEST_F(BRepGraph_ReverseIteratorTest, FacesOfWire_BoxWireHasOneFace) { const BRepGraph_WireId aWireId(0); - const int aCount = - countIterator(BRepGraph_FacesOfWire(myGraph, myGraph.Topo().Wires().Faces(aWireId))); + const uint32_t aCount = countIterator( + BRepGraph_FacesOfWire(myGraph, myGraph.Topo().Wires().Relations(aWireId).ParentWireRefIds)); EXPECT_EQ(aCount, 1); } TEST_F(BRepGraph_ReverseIteratorTest, WiresOfEdge_BoxEdgeBelongsToTwoWires) { const BRepGraph_EdgeId anEdgeId(0); - const int aCount = - countIterator(BRepGraph_WiresOfEdge(myGraph, myGraph.Topo().Edges().Wires(anEdgeId))); + const uint32_t aCount = countIterator(myGraph.Topo().Edges().WiresOf(anEdgeId)); EXPECT_EQ(aCount, 2); } TEST_F(BRepGraph_ReverseIteratorTest, SequentialIteration_SkipsRemovedParent) { - const BRepGraph_EdgeId anEdgeId(0); - const NCollection_DynamicArray& aFaces = myGraph.Topo().Edges().Faces(anEdgeId); - ASSERT_EQ(aFaces.Length(), 2); + const BRepGraph_EdgeId anEdgeId(0); + BRepGraph_FacesOfEdge aFaces = myGraph.Topo().Edges().FacesOf(anEdgeId); + ASSERT_TRUE(aFaces.More()); + const BRepGraph_FaceId aFirstFace = aFaces.CurrentId(); + EXPECT_EQ(countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)), 2); // Remove one parent face. - myGraph.Editor().Gen().RemoveNode(aFaces.Value(0)); + myGraph.Editor().Gen().RemoveNode(aFirstFace); - const int aCount = countIterator(BRepGraph_FacesOfEdge(myGraph, aFaces)); + const uint32_t aCount = countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)); EXPECT_EQ(aCount, 1); } -TEST_F(BRepGraph_ReverseIteratorTest, IndexedAccess_DoesNotSkipRemoved) +TEST_F(BRepGraph_ReverseIteratorTest, IndexedAccess_TracksActiveReverseBucket) { - const BRepGraph_EdgeId anEdgeId(0); - const NCollection_DynamicArray& aFaces = myGraph.Topo().Edges().Faces(anEdgeId); - ASSERT_EQ(aFaces.Length(), 2); + const BRepGraph_EdgeId anEdgeId(0); + BRepGraph_FacesOfEdge aFaces = myGraph.Topo().Edges().FacesOf(anEdgeId); + ASSERT_TRUE(aFaces.More()); + const BRepGraph_FaceId aFirstFace = aFaces.CurrentId(); + EXPECT_EQ(countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)), 2); - myGraph.Editor().Gen().RemoveNode(aFaces.Value(0)); + myGraph.Editor().Gen().RemoveNode(aFirstFace); - BRepGraph_FacesOfEdge anIt(myGraph, aFaces); - // Length() returns raw count including removed. - EXPECT_EQ(anIt.Length(), 2); - // Value() returns the raw entry without filtering. - EXPECT_TRUE(anIt.Value(0).IsValid(myGraph.Topo().Faces().Nb())); - EXPECT_TRUE(anIt.Value(1).IsValid(myGraph.Topo().Faces().Nb())); + BRepGraph_FacesOfEdge anIt = myGraph.Topo().Edges().FacesOf(anEdgeId); + // Reverse buckets are maintained as active relationships after removals. + ASSERT_TRUE(anIt.More()); + EXPECT_TRUE(anIt.CurrentId().IsValid(myGraph.Topo().Faces().Nb())); + anIt.Next(); + EXPECT_FALSE(anIt.More()); } TEST_F(BRepGraph_ReverseIteratorTest, RangeFor_WorksCorrectly) { const BRepGraph_EdgeId anEdgeId(0); - int aCount = 0; - for (const BRepGraph_FaceId aFaceId : - BRepGraph_FacesOfEdge(myGraph, myGraph.Topo().Edges().Faces(anEdgeId))) + uint32_t aCount = 0; + for (const BRepGraph_FaceId aFaceId : myGraph.Topo().Edges().FacesOf(anEdgeId)) { EXPECT_TRUE(aFaceId.IsValid(myGraph.Topo().Faces().Nb())); ++aCount; @@ -146,7 +152,8 @@ TEST_F(BRepGraph_ReverseIteratorTest, RefsShellsOfFace_ReturnsValidRefId) { const BRepGraph_FaceId aFaceId(0); const BRepGraph_ShellId aShellId(0); - BRepGraph_RefsShellsOfFace anIt(myGraph, myGraph.Topo().Faces().Shells(aFaceId), aFaceId); + BRepGraph_RefsShellsOfFace anIt(myGraph, + myGraph.Topo().Faces().Relations(aFaceId).ParentFaceRefIds); ASSERT_TRUE(anIt.More()); EXPECT_EQ(anIt.CurrentParentId(), aShellId); EXPECT_TRUE(anIt.CurrentRefId().IsValid()); @@ -155,7 +162,7 @@ TEST_F(BRepGraph_ReverseIteratorTest, RefsShellsOfFace_ReturnsValidRefId) TEST_F(BRepGraph_ReverseIteratorTest, RefsEdgesOfVertex_ReturnsValidRefId) { const BRepGraph_VertexId aVertexId(0); - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_RefsEdgesOfVertex anIt(myGraph, myGraph.Topo().Vertices().Edges(aVertexId), aVertexId); @@ -173,7 +180,7 @@ TEST_F(BRepGraph_ReverseIteratorTest, CoEdgesOfEdge_BoxEdgeHasCoEdges) { // Each edge of a box is used in 2 coedges (one per adjacent face). const BRepGraph_EdgeId anEdgeId(0); - const int aCount = + const uint32_t aCount = countIterator(BRepGraph_CoEdgesOfEdge(myGraph, myGraph.Topo().Edges().CoEdges(anEdgeId))); EXPECT_EQ(aCount, 2); } @@ -181,21 +188,19 @@ TEST_F(BRepGraph_ReverseIteratorTest, CoEdgesOfEdge_BoxEdgeHasCoEdges) TEST_F(BRepGraph_ReverseIteratorTest, WiresOfCoEdge_BoxCoEdgeBelongsToOneWire) { const BRepGraph_CoEdgeId aCoEdgeId(0); - const int aCount = - countIterator(BRepGraph_WiresOfCoEdge(myGraph, myGraph.Topo().CoEdges().Wires(aCoEdgeId))); - EXPECT_EQ(aCount, 1); + EXPECT_TRUE(myGraph.Topo().CoEdges().Wire(aCoEdgeId).IsValid()); } TEST_F(BRepGraph_ReverseIteratorTest, Definition_ReturnsFaceDefinition) { const BRepGraph_EdgeId anEdgeId(0); - BRepGraph_FacesOfEdge anIt(myGraph, myGraph.Topo().Edges().Faces(anEdgeId)); + BRepGraph_FacesOfEdge anIt = myGraph.Topo().Edges().FacesOf(anEdgeId); ASSERT_TRUE(anIt.More()); - const BRepGraphInc::FaceDef& aFaceDef = anIt.Definition(); - EXPECT_FALSE(aFaceDef.IsRemoved); + const BRepGraph_FaceId aFaceId = anIt.CurrentId(); + EXPECT_FALSE(anIt.CurrentId().IsRemoved(myGraph)); // Face must have at least one wire. - EXPECT_GE(aFaceDef.WireRefIds.Length(), 1); + EXPECT_GE(myGraph.Topo().Faces().Relations(aFaceId).WireRefIds.Size(), 1); } TEST_F(BRepGraph_ReverseIteratorTest, Definition_ReturnsEdgeDefinition) @@ -205,7 +210,7 @@ TEST_F(BRepGraph_ReverseIteratorTest, Definition_ReturnsEdgeDefinition) ASSERT_TRUE(anIt.More()); const BRepGraphInc::EdgeDef& anEdgeDef = anIt.Definition(); - EXPECT_FALSE(anEdgeDef.IsRemoved); + EXPECT_FALSE(anIt.CurrentId().IsRemoved(myGraph)); EXPECT_TRUE(anEdgeDef.StartVertexRefId.IsValid()); EXPECT_TRUE(anEdgeDef.EndVertexRefId.IsValid()); } @@ -213,39 +218,232 @@ TEST_F(BRepGraph_ReverseIteratorTest, Definition_ReturnsEdgeDefinition) TEST_F(BRepGraph_ReverseIteratorTest, SkipsRemovedParent_EdgesOfVertex) { const BRepGraph_VertexId aVertexId(0); - const NCollection_DynamicArray& anEdges = + const NCollection_LinearVector& anEdges = myGraph.Topo().Vertices().Edges(aVertexId); // Box vertex touches 3 edges. - ASSERT_EQ(anEdges.Length(), 3); + ASSERT_EQ(anEdges.Size(), 3); myGraph.Editor().Gen().RemoveNode(anEdges.Value(0)); - const int aCount = countIterator(BRepGraph_EdgesOfVertex(myGraph, anEdges)); + const uint32_t aCount = countIterator(BRepGraph_EdgesOfVertex(myGraph, anEdges)); EXPECT_EQ(aCount, 2); } TEST_F(BRepGraph_ReverseIteratorTest, SkipsRemovedParent_AllRemoved) { - const BRepGraph_EdgeId anEdgeId(0); - const NCollection_DynamicArray& aFaces = myGraph.Topo().Edges().Faces(anEdgeId); - ASSERT_EQ(aFaces.Length(), 2); + const BRepGraph_EdgeId anEdgeId(0); + BRepGraph_FacesOfEdge aFaces = myGraph.Topo().Edges().FacesOf(anEdgeId); + ASSERT_TRUE(aFaces.More()); + const BRepGraph_FaceId aFirstFace = aFaces.CurrentId(); + aFaces.Next(); + ASSERT_TRUE(aFaces.More()); + const BRepGraph_FaceId aSecondFace = aFaces.CurrentId(); + EXPECT_EQ(countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)), 2); - myGraph.Editor().Gen().RemoveNode(aFaces.Value(0)); - myGraph.Editor().Gen().RemoveNode(aFaces.Value(1)); + myGraph.Editor().Gen().RemoveNode(aFirstFace); + myGraph.Editor().Gen().RemoveNode(aSecondFace); - const int aCount = countIterator(BRepGraph_FacesOfEdge(myGraph, aFaces)); + const uint32_t aCount = countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)); EXPECT_EQ(aCount, 0); } TEST_F(BRepGraph_ReverseIteratorTest, StartingIndex_SkipsToPosition) { const BRepGraph_VertexId aVertexId(0); - const NCollection_DynamicArray& anEdges = + const NCollection_LinearVector& anEdges = myGraph.Topo().Vertices().Edges(aVertexId); - ASSERT_EQ(anEdges.Length(), 3); + ASSERT_EQ(anEdges.Size(), 3); // Start at index 1 - should yield only edges at index >= 1. BRepGraph_EdgesOfVertex anIt(myGraph, anEdges, 1); - const int aCount = countIterator(anIt); + const uint32_t aCount = countIterator(anIt); EXPECT_EQ(aCount, 2); } + +TEST_F(BRepGraph_ReverseIteratorTest, CompoundsOfChild_AllTopologyKinds) +{ + BRepGraph aGraph; + aGraph.Clear(); + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + ASSERT_TRUE(aV0.IsValid()); + ASSERT_TRUE(aV1.IsValid()); + + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.0e-7); + ASSERT_TRUE(anEdge.IsValid()); + + NCollection_LinearVector aCoEdgeIds; + aCoEdgeIds.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdgeIds.ToArray1()); + ASSERT_TRUE(aWire.IsValid()); + ASSERT_EQ(aGraph.Topo().Edges().CoEdges(anEdge).Size(), 1); + const BRepGraph_CoEdgeId aCoEdge = aGraph.Topo().Edges().CoEdges(anEdge).First(); + + const BRepGraph_FaceId aFace = aGraph.Editor().Faces().Add(occ::handle(), + aWire, + NCollection_Array1(), + 1.0e-7); + ASSERT_TRUE(aFace.IsValid()); + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell.IsValid()); + ASSERT_TRUE(aGraph.Editor().Shells().Append(aShell, aFace).IsValid()); + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + ASSERT_TRUE(aSolid.IsValid()); + ASSERT_TRUE(aGraph.Editor().Solids().Append(aSolid, aShell).IsValid()); + + NCollection_LinearVector aSolids; + aSolids.Append(aSolid); + const BRepGraph_CompSolidId aCompSolid = aGraph.Editor().CompSolids().Add(aSolids.ToArray1()); + ASSERT_TRUE(aCompSolid.IsValid()); + + NCollection_LinearVector aNestedChildren; + aNestedChildren.Append(aSolid); + const BRepGraph_CompoundId aNestedCompound = + aGraph.Editor().Compounds().Add(aNestedChildren.ToArray1()); + ASSERT_TRUE(aNestedCompound.IsValid()); + + NCollection_LinearVector aChildren; + aChildren.Append(aV0); + aChildren.Append(aCoEdge); + aChildren.Append(anEdge); + aChildren.Append(aWire); + aChildren.Append(aFace); + aChildren.Append(aShell); + aChildren.Append(aSolid); + aChildren.Append(aCompSolid); + aChildren.Append(aNestedCompound); + const BRepGraph_CompoundId aCompound = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + BRepGraph_CompoundsOfVertex aVertexParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aV0))); + ASSERT_TRUE(aVertexParents.More()); + EXPECT_EQ(aVertexParents.CurrentId(), aCompound); + aVertexParents.Next(); + EXPECT_FALSE(aVertexParents.More()); + + BRepGraph_CompoundsOfCoEdge aCoEdgeParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aCoEdge))); + ASSERT_TRUE(aCoEdgeParents.More()); + EXPECT_EQ(aCoEdgeParents.CurrentId(), aCompound); + aCoEdgeParents.Next(); + EXPECT_FALSE(aCoEdgeParents.More()); + + BRepGraph_CompoundsOfEdge anEdgeParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(anEdge))); + ASSERT_TRUE(anEdgeParents.More()); + EXPECT_EQ(anEdgeParents.CurrentId(), aCompound); + anEdgeParents.Next(); + EXPECT_FALSE(anEdgeParents.More()); + + BRepGraph_CompoundsOfWire aWireParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aWire))); + ASSERT_TRUE(aWireParents.More()); + EXPECT_EQ(aWireParents.CurrentId(), aCompound); + aWireParents.Next(); + EXPECT_FALSE(aWireParents.More()); + + BRepGraph_CompoundsOfFace aFaceParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aFace))); + ASSERT_TRUE(aFaceParents.More()); + EXPECT_EQ(aFaceParents.CurrentId(), aCompound); + aFaceParents.Next(); + EXPECT_FALSE(aFaceParents.More()); + + BRepGraph_CompoundsOfShell aShellParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aShell))); + ASSERT_TRUE(aShellParents.More()); + EXPECT_EQ(aShellParents.CurrentId(), aCompound); + aShellParents.Next(); + EXPECT_FALSE(aShellParents.More()); + + BRepGraph_CompoundsOfSolid aSolidParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aSolid))); + ASSERT_TRUE(aSolidParents.More()); + EXPECT_EQ(aSolidParents.CurrentId(), aNestedCompound); + aSolidParents.Next(); + ASSERT_TRUE(aSolidParents.More()); + EXPECT_EQ(aSolidParents.CurrentId(), aCompound); + aSolidParents.Next(); + EXPECT_FALSE(aSolidParents.More()); + + BRepGraph_CompoundsOfCompSolid aCompSolidParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aCompSolid))); + ASSERT_TRUE(aCompSolidParents.More()); + EXPECT_EQ(aCompSolidParents.CurrentId(), aCompound); + aCompSolidParents.Next(); + EXPECT_FALSE(aCompSolidParents.More()); + + BRepGraph_CompoundsOfCompound aCompoundParents( + aGraph, + aGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aNestedCompound))); + ASSERT_TRUE(aCompoundParents.More()); + EXPECT_EQ(aCompoundParents.CurrentId(), aCompound); + aCompoundParents.Next(); + EXPECT_FALSE(aCompoundParents.More()); + + BRepGraph_CompoundsOfChild aGenericParents(aGraph, aGraph.Topo().Gen().CompoundRefIds(aCoEdge)); + ASSERT_TRUE(aGenericParents.More()); + EXPECT_EQ(aGenericParents.CurrentId(), aCompound); + aGenericParents.Next(); + EXPECT_FALSE(aGenericParents.More()); + + BRepGraph_FacesOfWire aFaceParentsOfWire(aGraph, + aGraph.Topo().Wires().Relations(aWire).ParentWireRefIds); + ASSERT_TRUE(aFaceParentsOfWire.More()); + EXPECT_EQ(aFaceParentsOfWire.CurrentId(), aFace); + aFaceParentsOfWire.Next(); + EXPECT_FALSE(aFaceParentsOfWire.More()); +} + +TEST_F(BRepGraph_ReverseIteratorTest, OccurrencesOfChild_ProductAndTopologyChildren) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_NodeId(BRepGraph_SolidId::Start())); + const BRepGraph_CompoundId aCompound = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + + const BRepGraph_ProductId aPart = aGraph.Editor().Products().Add(aCompound); + aGraph.Editor().Products().AppendDocumentRoot(aPart); + ASSERT_TRUE(aPart.IsValid()); + + const BRepGraph_ProductId anAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(anAssembly); + ASSERT_TRUE(anAssembly.IsValid()); + const BRepGraph_OccurrenceId aPartOccurrence = + aGraph.Editor().Products().Append(anAssembly, aPart, TopLoc_Location()); + ASSERT_TRUE(aPartOccurrence.IsValid()); + + BRepGraph_OccurrencesOfChild aTopologyOccurrences( + aGraph, + aGraph.Topo().Gen().OccurrenceRefIds(aCompound)); + ASSERT_TRUE(aTopologyOccurrences.More()); + const BRepGraph_OccurrenceId aCompoundOccurrence = aTopologyOccurrences.CurrentId(); + ASSERT_TRUE(aCompoundOccurrence.IsValid()); + EXPECT_EQ(aGraph.Topo().Occurrences().Definition(aCompoundOccurrence).ChildNodeId, + BRepGraph_NodeId(aCompound)); + aTopologyOccurrences.Next(); + EXPECT_FALSE(aTopologyOccurrences.More()); + + BRepGraph_OccurrencesOfChild aProductOccurrences(aGraph, + aGraph.Topo().Gen().OccurrenceRefIds(aPart)); + ASSERT_TRUE(aProductOccurrences.More()); + EXPECT_EQ(aProductOccurrences.CurrentId(), aPartOccurrence); + aProductOccurrences.Next(); + EXPECT_FALSE(aProductOccurrences.More()); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Reverse_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Reverse_Test.cxx new file mode 100644 index 0000000000..8fb3a33754 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Reverse_Test.cxx @@ -0,0 +1,359 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +namespace +{ +BRepGraph buildBoxGraph() +{ + BRepGraph aGraph; + const occ::handle aRegisteredLayer = + aGraph.LayerRegistry().Ensure(); + EXPECT_FALSE(aRegisteredLayer.IsNull()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aRes = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + return aGraph; +} + +BRepGraph_EdgeId addSegment(BRepGraph& theGraph, + const BRepGraph_VertexId theStart, + const BRepGraph_VertexId theEnd) +{ + return theGraph.Editor().Edges().Add(theStart, + theEnd, + occ::handle(), + 0.0, + 1.0, + 1.0e-7); +} +} // namespace + +TEST(BRepGraph_WireReverseTest, OrderAndOrientationFlipped) +{ + BRepGraph aGraph = buildBoxGraph(); + const BRepGraph_WireId aWire(0); + ASSERT_TRUE(aWire.IsValid(aGraph.Topo().Wires().Nb())); + + const NCollection_LinearVector& aCoEdgesBefore = + aGraph.Topo().Wires().Relations(aWire).CoEdgeIds; + std::vector aOrderBefore; + std::vector aOriBefore; + for (size_t i = 0; i < aCoEdgesBefore.Size(); ++i) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgesBefore.Value(i); + aOrderBefore.push_back(aCoEdgeId); + const BRepGraphInc::CoEdgeDef& aCoEdge = aGraph.Topo().CoEdges().Definition(aCoEdgeId); + aOriBefore.push_back(aCoEdge.Orientation); + } + ASSERT_GT(aOrderBefore.size(), 1u); + + aGraph.Editor().Wires().Reverse(aWire); + + const NCollection_LinearVector& aCoEdgesAfter = + aGraph.Topo().Wires().Relations(aWire).CoEdgeIds; + ASSERT_EQ(aOrderBefore.size(), aCoEdgesAfter.Size()); + for (size_t i = 0; i < aCoEdgesAfter.Size(); ++i) + { + EXPECT_EQ(aCoEdgesAfter.Value(i), aOrderBefore[aOrderBefore.size() - 1 - i]) + << "CoEdgeId order should be reversed at slot " << i; + + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgesAfter.Value(i); + const BRepGraphInc::CoEdgeDef& aCoEdge = aGraph.Topo().CoEdges().Definition(aCoEdgeId); + EXPECT_EQ(aCoEdge.Orientation, TopAbs::Reverse(aOriBefore[aOrderBefore.size() - 1 - i])) + << "CoEdge orientation should be flipped at slot " << i; + } +} + +TEST(BRepGraph_WireOrderTest, SetCoEdgeOrderCanonicalizesUnorderedPermutation) +{ + BRepGraph aGraph = buildBoxGraph(); + const BRepGraph_WireId aWire(0); + ASSERT_TRUE(aWire.IsValid(aGraph.Topo().Wires().Nb())); + + const NCollection_LinearVector& aCoEdges = + aGraph.Topo().Wires().Relations(aWire).CoEdgeIds; + ASSERT_GE(aCoEdges.Size(), 4u); + + NCollection_LinearVector aBadOrder; + aBadOrder.Append(aCoEdges.Value(0)); + aBadOrder.Append(aCoEdges.Value(2)); + aBadOrder.Append(aCoEdges.Value(1)); + aBadOrder.Append(aCoEdges.Value(3)); + + EXPECT_TRUE(aGraph.Editor().Wires().SetCoEdgeOrder(aWire, aBadOrder.ToArray1())); + + const NCollection_LinearVector& aCoEdgesAfter = + aGraph.Topo().Wires().Relations(aWire).CoEdgeIds; + ASSERT_EQ(aCoEdges.Size(), aCoEdgesAfter.Size()); + EXPECT_TRUE(aGraph.Editor().ValidateMutationBoundary()); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); +} + +TEST(BRepGraph_WireOrderTest, CheckCoEdgeOrderReportsCurrentAndReordered) +{ + BRepGraph aGraph = buildBoxGraph(); + const BRepGraph_WireId aWire(0); + ASSERT_TRUE(aWire.IsValid(aGraph.Topo().Wires().Nb())); + + using CoEdgeOrderStatus = BRepGraph::EditorView::WireOps::CoEdgeOrderStatus; + + const NCollection_LinearVector& aCoEdges = + aGraph.Topo().Wires().Relations(aWire).CoEdgeIds; + ASSERT_GE(aCoEdges.Size(), 4u); + + NCollection_LinearVector aCurrentOrder; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) + { + aCurrentOrder.Append(aCoEdgeId); + } + + EXPECT_EQ(aGraph.Editor().Wires().CheckCoEdgeOrder(aWire, aCurrentOrder.ToArray1()), + CoEdgeOrderStatus::AlreadyCurrent); + EXPECT_TRUE(aGraph.Editor().Wires().SetCoEdgeOrder(aWire, aCurrentOrder.ToArray1())); + + NCollection_LinearVector anUnordered; + anUnordered.Append(aCoEdges.Value(0)); + anUnordered.Append(aCoEdges.Value(2)); + anUnordered.Append(aCoEdges.Value(1)); + anUnordered.Append(aCoEdges.Value(3)); + EXPECT_EQ(aGraph.Editor().Wires().CheckCoEdgeOrder(aWire, anUnordered.ToArray1()), + CoEdgeOrderStatus::Reordered); + EXPECT_EQ(aGraph.Editor().Wires().CheckCoEdgeOrder(BRepGraph_WireId(), anUnordered.ToArray1()), + CoEdgeOrderStatus::InvalidWire); +} + +TEST(BRepGraph_WireOrderTest, SetCoEdgeOrderCanonicalizesOpenWirePermutation) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(2.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV3 = aGraph.Editor().Vertices().Add(gp_Pnt(3.0, 0.0, 0.0), 1.0e-7); + + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(addSegment(aGraph, aV0, aV1), TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(addSegment(aGraph, aV1, aV2), TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(addSegment(aGraph, aV2, aV3), TopAbs_FORWARD)); + + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + ASSERT_TRUE(aWire.IsValid()); + + NCollection_LinearVector anUnordered; + anUnordered.Append(aCoEdges.Value(2)); + anUnordered.Append(aCoEdges.Value(0)); + anUnordered.Append(aCoEdges.Value(1)); + + EXPECT_TRUE(aGraph.Editor().Wires().SetCoEdgeOrder(aWire, anUnordered.ToArray1())); + + const NCollection_LinearVector& anOrdered = + aGraph.Topo().Wires().Relations(aWire).CoEdgeIds; + ASSERT_EQ(anOrdered.Size(), aCoEdges.Size()); + EXPECT_EQ(anOrdered.Value(0), aCoEdges.Value(0)); + EXPECT_EQ(anOrdered.Value(1), aCoEdges.Value(1)); + EXPECT_EQ(anOrdered.Value(2), aCoEdges.Value(2)); + EXPECT_TRUE(aGraph.Editor().ValidateMutationBoundary()); +} + +TEST(BRepGraph_WireOrderTest, CheckAppendCoEdgeReportsReadyAndAlreadyContained) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(2.0, 0.0, 0.0), 1.0e-7); + + const BRepGraph_EdgeId aFirstEdge = addSegment(aGraph, aV0, aV1); + NCollection_LinearVector aInitialCoEdges; + aInitialCoEdges.Append(aGraph.Editor().CoEdges().Add(aFirstEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aInitialCoEdges.ToArray1()); + ASSERT_TRUE(aWire.IsValid()); + + const BRepGraph_EdgeId aSecondEdge = addSegment(aGraph, aV1, aV2); + const BRepGraph_CoEdgeId aSecondCoEdge = + aGraph.Editor().CoEdges().Add(aSecondEdge, TopAbs_FORWARD); + + using CoEdgeOrderStatus = BRepGraph::EditorView::WireOps::CoEdgeOrderStatus; + EXPECT_EQ(aGraph.Editor().Wires().CheckAppendCoEdge(aWire, aSecondCoEdge), + CoEdgeOrderStatus::Ready); + EXPECT_EQ(aGraph.Editor().Wires().CheckAppendCoEdge(aWire, aInitialCoEdges.Value(0)), + CoEdgeOrderStatus::AlreadyContained); +} + +TEST(BRepGraph_WireOrderTest, CheckReplaceEdgeReportsReadyAndDisconnected) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(2.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV3 = aGraph.Editor().Vertices().Add(gp_Pnt(3.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV4 = aGraph.Editor().Vertices().Add(gp_Pnt(4.0, 0.0, 0.0), 1.0e-7); + + const BRepGraph_EdgeId anOldEdge = addSegment(aGraph, aV0, aV1); + const BRepGraph_EdgeId aNextEdge = addSegment(aGraph, aV1, aV2); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anOldEdge, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aNextEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + ASSERT_TRUE(aWire.IsValid()); + + const BRepGraph_EdgeId aCompatibleEdge = addSegment(aGraph, aV0, aV1); + const BRepGraph_EdgeId aDisconnectedEdge = addSegment(aGraph, aV3, aV4); + + using ReplaceEdgeStatus = BRepGraph::EditorView::WireOps::ReplaceEdgeStatus; + EXPECT_EQ(aGraph.Editor().Wires().CheckReplaceEdge(aWire, anOldEdge, aCompatibleEdge, false), + ReplaceEdgeStatus::Ready); + EXPECT_EQ(aGraph.Editor().Wires().CheckReplaceEdge(aWire, anOldEdge, anOldEdge, false), + ReplaceEdgeStatus::AlreadyCurrent); + EXPECT_EQ(aGraph.Editor().Wires().CheckReplaceEdge(aWire, anOldEdge, aDisconnectedEdge, false), + ReplaceEdgeStatus::Disconnected); +} + +TEST(BRepGraph_WireOrderTest, RemoveInternalCoEdgeFromClosedWireKeepsConnectedOrder) +{ + BRepGraph aGraph = buildBoxGraph(); + const BRepGraph_WireId aWire(0); + ASSERT_TRUE(aWire.IsValid(aGraph.Topo().Wires().Nb())); + + const NCollection_LinearVector aCoEdgesBefore = + aGraph.Topo().Wires().Relations(aWire).CoEdgeIds; + ASSERT_GE(aCoEdgesBefore.Size(), 4u); + + EXPECT_TRUE(aGraph.Editor().Wires().RemoveCoEdge(aWire, aCoEdgesBefore.Value(1))); + + const NCollection_LinearVector& aCoEdgesAfter = + aGraph.Topo().Wires().Relations(aWire).CoEdgeIds; + EXPECT_EQ(aCoEdgesAfter.Size(), aCoEdgesBefore.Size() - 1); + EXPECT_TRUE(aGraph.Editor().ValidateMutationBoundary()); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); +} + +TEST(BRepGraph_EdgeReverseTest, StartEndVertexRefsSwapped) +{ + BRepGraph aGraph = buildBoxGraph(); + const BRepGraph_EdgeId anEdge(0); + ASSERT_TRUE(anEdge.IsValid(aGraph.Topo().Edges().Nb())); + + const BRepGraph_VertexRefId aStartBefore = + aGraph.Topo().Edges().Definition(anEdge).StartVertexRefId; + const BRepGraph_VertexRefId aEndBefore = aGraph.Topo().Edges().Definition(anEdge).EndVertexRefId; + ASSERT_TRUE(aStartBefore.IsValid()); + ASSERT_TRUE(aEndBefore.IsValid()); + ASSERT_NE(aStartBefore, aEndBefore); + + aGraph.Editor().Edges().Reverse(anEdge); + + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdge).StartVertexRefId, aEndBefore); + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdge).EndVertexRefId, aStartBefore); +} + +TEST(BRepGraph_EdgeReverseTest, RoundTripRestoresOriginal) +{ + BRepGraph aGraph = buildBoxGraph(); + const BRepGraph_EdgeId anEdge(0); + ASSERT_TRUE(anEdge.IsValid(aGraph.Topo().Edges().Nb())); + + const BRepGraph_VertexRefId aStart = aGraph.Topo().Edges().Definition(anEdge).StartVertexRefId; + const BRepGraph_VertexRefId aEnd = aGraph.Topo().Edges().Definition(anEdge).EndVertexRefId; + + aGraph.Editor().Edges().Reverse(anEdge); + aGraph.Editor().Edges().Reverse(anEdge); + + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdge).StartVertexRefId, aStart); + EXPECT_EQ(aGraph.Topo().Edges().Definition(anEdge).EndVertexRefId, aEnd); +} + +TEST(BRepGraph_LayerTopoSupplementTest, AddVertexToFaceGoesToSupplementLayer) +{ + BRepGraph aGraph = buildBoxGraph(); + const BRepGraph_FaceId aFaceId(0); + ASSERT_TRUE(aFaceId.IsValid(aGraph.Topo().Faces().Nb())); + + const BRepGraph_VertexId aVertex = aGraph.Editor().Vertices().Add(gp_Pnt(0.5, 0.5, 0.5), 1.0e-7); + ASSERT_TRUE(aVertex.IsValid()); + + // Faces().AddVertex() removed; use Shapes().Add(vertexShape, faceNode) instead. + // const BRepGraph_VertexRefId aRefId = + // aGraph.Editor().Faces().AddVertex(aFaceId, aVertex, TopAbs_INTERNAL); + // EXPECT_FALSE(aRefId.IsValid()); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + const NCollection_LinearVector& anAttached = + aLayer->AttachedTo(BRepGraph_NodeId(aFaceId)); + // No vertex added -> no supplement attachments. + ASSERT_EQ(anAttached.Size(), 0u); + // Skip removal test since no attachments exist. +} + +TEST(BRepGraph_LayerTopoSupplementTest, ShellStartsWithoutSupplementAttachments) +{ + BRepGraph aGraph = buildBoxGraph(); + ASSERT_GE(aGraph.Topo().Shells().Nb(), 1); + const BRepGraph_ShellId aShellId(0); + const BRepGraph_EdgeId anEdge(0); + ASSERT_TRUE(anEdge.IsValid(aGraph.Topo().Edges().Nb())); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + if (!aLayer.IsNull()) + { + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aShellId)).Size(), 0u); + } +} + +TEST(BRepGraph_RelationsTest, SupplementLayerClearedOnGraphClear) +{ + BRepGraph aGraph = buildBoxGraph(); + ASSERT_GE(aGraph.Topo().Shells().Nb(), 1); + + const BRepGraph_FaceId aFaceId(0); + + // Faces().AddVertex() removed; use Shapes().Add(vertexShape, faceNode) instead. + // [[maybe_unused]] const BRepGraph_VertexRefId aVertexRef = + // aGraph.Editor().Faces().AddVertex(aFaceId, aVertex, TopAbs_INTERNAL); + + const occ::handle aLayerBefore = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayerBefore.IsNull()); + // No vertex added -> no supplement attachments before clear. + ASSERT_EQ(aLayerBefore->AttachedTo(BRepGraph_NodeId(aFaceId)).Size(), 0u); + + aGraph.Clear(); + + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 0); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 0); + + const occ::handle aLayerAfter = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayerAfter.IsNull()); + EXPECT_EQ(aLayerAfter->AttachedTo(BRepGraph_NodeId(aFaceId)).Size(), 0u); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_ScenarioMatrix_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ScenarioMatrix_Test.cxx index 6c4ae7b52c..f8e6ebd4e3 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_ScenarioMatrix_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ScenarioMatrix_Test.cxx @@ -15,14 +15,14 @@ // // Cross-cutting tests that combine multiple BRepGraph subsystems in realistic // end-to-end flows. Each test deliberately touches: -// BRepGraph_Builder -> BRepGraph_Validate(Audit) +// BRepGraph::ShapesView -> BRepGraph_Validate(Audit) // -> Editor mutation -> BRepGraph.Shapes().Reconstruct / Shape -// -> BRepGraphInc_Populate -> BRepGraphInc_Storage.ValidateReverseIndex / ValidateSelfIds +// -> BRepGraphInc_Populate -> BRepGraphInc_Storage.ValidateRelations / ValidateSelfIds // // These tests are not duplicating the many isolated unit tests that already // exist for each individual API. They specifically lock down the correctness of // the combined mutation-validate-reconstruct-populate pipelines and the -// reverse-index paths for assemblies, compounds, comp-solids, and free wires. +// relation-table paths for assemblies, compounds, comp-solids, and free wires. #include #include @@ -30,11 +30,11 @@ #include #include #include -#include +#include #include #include #include -#include +#include #include #include #include @@ -45,11 +45,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -57,12 +61,17 @@ #include #include #include +#include +#include #include +#include #include #include #include +#include + namespace { @@ -92,7 +101,7 @@ static int countSubShapes(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType // Flow: build box graph -> clean audit validate -> mutate a vertex point in // the graph -> audit validate again (structural integrity must survive a data // change) -> reconstruct the solid -> verify area has changed -> BRepGraphInc -// populate from reconstructed solid -> ValidateReverseIndex. +// populate from reconstructed solid -> ValidateRelations. // ============================================================================= TEST(BRepGraph_ScenarioMatrix, Box_MutateVertex_ValidateReconstructPopulateRoundTrip) @@ -104,13 +113,12 @@ TEST(BRepGraph_ScenarioMatrix, Box_MutateVertex_ValidateReconstructPopulateRound // --- Build BRepGraph --- BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // --- Validate clean graph (full audit) --- const BRepGraph_Validate::Result aCleanResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); ASSERT_TRUE(aCleanResult.IsValid()) << "Graph must be structurally valid before any mutation"; // --- Locate a vertex (via the first face's outer wire's first coedge) --- @@ -124,7 +132,7 @@ TEST(BRepGraph_ScenarioMatrix, Box_MutateVertex_ValidateReconstructPopulateRound // --- Audit again: structural invariants must survive a point-data mutation --- const BRepGraph_Validate::Result aAfterMutResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aAfterMutResult.IsValid()) << "Graph must remain structurally valid after a vertex point mutation"; @@ -165,19 +173,19 @@ TEST(BRepGraph_ScenarioMatrix, Box_MutateVertex_ValidateReconstructPopulateRound "graph Mut(VertexDef) must propagate through BRepGraphInc_Reconstruct"; // --- BRepGraphInc round-trip from the reconstructed solid --- - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aRecon, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aRoundTripGraph; + std::ignore = BRepGraphInc_Populate::Perform(aRoundTripGraph, aRecon, false); + ASSERT_FALSE(aRoundTripGraph.IsEmpty()); - // Self-id invariants must hold on a freshly populated storage. - - // Reverse index must be consistent. - EXPECT_TRUE(aStorage.ValidateReverseIndex()) - << "Reverse index must be consistent for reconstructed solid"; + // Relation table must be consistent. + EXPECT_TRUE( + BRepGraph_Validate::Perform(aRoundTripGraph, BRepGraph_Validate::Options::Lightweight()) + .IsValid()) + << "Relation table must be consistent for reconstructed solid"; // Sub-shape counts of the reconstructed solid must be identical to the original. - EXPECT_EQ(aStorage.NbFaces(), countSubShapes(aBox, TopAbs_FACE)); - EXPECT_EQ(aStorage.NbEdges(), countSubShapes(aBox, TopAbs_EDGE)); + EXPECT_EQ(aRoundTripGraph.Topo().Faces().Nb(), countSubShapes(aBox, TopAbs_FACE)); + EXPECT_EQ(aRoundTripGraph.Topo().Edges().Nb(), countSubShapes(aBox, TopAbs_EDGE)); } // ============================================================================= @@ -187,7 +195,7 @@ TEST(BRepGraph_ScenarioMatrix, Box_MutateVertex_ValidateReconstructPopulateRound // Flow: build cylinder -> BRepGraph + BRepGraphInc_Storage -> locate the seam // edge in storage -> mutate its tolerance in BRepGraph (cross-reference by // shared edge index) -> Validate(Audit) on the graph -> Reconstruct -> second -// BRepGraphInc populate -> seam edges are still present -> ValidateReverseIndex. +// BRepGraphInc populate -> seam edges are still present -> ValidateRelations. // ============================================================================= TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdge_MutationAndBothSubsystemsConsistent) @@ -198,30 +206,25 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdge_MutationAndBothSubsystemsConsis // --- Build both representations from the original shape --- BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCyl); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aCyl); + ASSERT_FALSE(aGraph.IsEmpty()); - BRepGraphInc_Storage aOrigStorage; - BRepGraphInc_Populate::Perform(aOrigStorage, aCyl, false); - ASSERT_TRUE(aOrigStorage.GetIsDone()); + BRepGraph aOrigGraph; + std::ignore = BRepGraphInc_Populate::Perform(aOrigGraph, aCyl, false); + ASSERT_FALSE(aOrigGraph.IsEmpty()); // --- Locate the seam edge in BRepGraphInc_Storage --- BRepGraph_EdgeId aSeamEdgeId; { - const int aNbEdges = aOrigStorage.NbEdges(); + const uint32_t aNbEdges = aOrigGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges) && !aSeamEdgeId.IsValid(); ++anEdgeId) { - const NCollection_DynamicArray* aCoEdges = - aOrigStorage.ReverseIndex().CoEdgesOfEdge(anEdgeId); - if (aCoEdges == nullptr) + const NCollection_LinearVector& aCoEdges = + aOrigGraph.Topo().Edges().Relations(anEdgeId).CoEdgeIds; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { - continue; - } - for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCoEdges) - { - if (BRepGraph_TestTools::IsSeamCoEdgeFromStorage(aOrigStorage, aCoEdgeId)) + if (BRepGraph_Tool::CoEdge::SeamPair(aOrigGraph, aCoEdgeId).IsValid()) { aSeamEdgeId = anEdgeId; break; @@ -243,7 +246,7 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdge_MutationAndBothSubsystemsConsis // --- Validate(Audit): structural integrity must survive a tolerance mutation --- const BRepGraph_Validate::Result aResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aResult.IsValid()) << "Graph must remain structurally valid after edge tolerance mutation on seam edge"; @@ -253,30 +256,26 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdge_MutationAndBothSubsystemsConsis ASSERT_FALSE(aRecon.IsNull()); // --- BRepGraphInc populate from the reconstructed solid --- - BRepGraphInc_Storage aReconStorage; - BRepGraphInc_Populate::Perform(aReconStorage, aRecon, false); - ASSERT_TRUE(aReconStorage.GetIsDone()); + BRepGraph aReconGraph; + std::ignore = BRepGraphInc_Populate::Perform(aReconGraph, aRecon, false); + ASSERT_FALSE(aReconGraph.IsEmpty()); // Entity counts must match original storage. - EXPECT_EQ(aReconStorage.NbVertices(), aOrigStorage.NbVertices()); - EXPECT_EQ(aReconStorage.NbEdges(), aOrigStorage.NbEdges()); - EXPECT_EQ(aReconStorage.NbFaces(), aOrigStorage.NbFaces()); + EXPECT_EQ(aReconGraph.Topo().Vertices().Nb(), aOrigGraph.Topo().Vertices().Nb()); + EXPECT_EQ(aReconGraph.Topo().Edges().Nb(), aOrigGraph.Topo().Edges().Nb()); + EXPECT_EQ(aReconGraph.Topo().Faces().Nb(), aOrigGraph.Topo().Faces().Nb()); // Seam edges must still be present after the mutation+reconstruct cycle. bool aSeamFound = false; { - const int aNbEdges = aReconStorage.NbEdges(); + const uint32_t aNbEdges = aReconGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges) && !aSeamFound; ++anEdgeId) { - const NCollection_DynamicArray* aCoEdges = - aReconStorage.ReverseIndex().CoEdgesOfEdge(anEdgeId); - if (aCoEdges == nullptr) + const NCollection_LinearVector& aCoEdges = + aReconGraph.Topo().Edges().Relations(anEdgeId).CoEdgeIds; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { - continue; - } - for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCoEdges) - { - if (BRepGraph_TestTools::IsSeamCoEdgeFromStorage(aReconStorage, aCoEdgeId)) + if (BRepGraph_Tool::CoEdge::SeamPair(aReconGraph, aCoEdgeId).IsValid()) { aSeamFound = true; break; @@ -287,16 +286,17 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdge_MutationAndBothSubsystemsConsis EXPECT_TRUE(aSeamFound) << "Seam edge must still be present in BRepGraphInc storage after reconstruct"; - EXPECT_TRUE(aReconStorage.ValidateReverseIndex()) - << "Reverse index must be consistent for reconstructed cylinder"; + EXPECT_TRUE( + BRepGraph_Validate::Perform(aReconGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) + << "Relation table must be consistent for reconstructed cylinder"; } // ============================================================================= -// Scenario 3: CompSolid - BRepGraph + BRepGraphInc reverse-index + mutation +// Scenario 3: CompSolid - BRepGraph + BRepGraphInc relation-table + mutation // + reconstruct round-trip // // Flow: build CompSolid (2 boxes) -> BRepGraph build -> BRepGraphInc populate -// -> ValidateReverseIndex -> Validate(Audit) -> mutate an edge tolerance in +// -> ValidateRelations -> Validate(Audit) -> mutate an edge tolerance in // BRepGraph -> Validate(Audit) still passes -> reconstruct the CompSolid -> // BRepGraphInc populate from reconstructed -> sub-shape counts match. // ============================================================================= @@ -316,32 +316,34 @@ TEST(BRepGraph_ScenarioMatrix, CompSolid_TwoBoxes_BothSubsystemsMutateReconstruc const int anOrigEdges = countSubShapes(aCompSolid, TopAbs_EDGE); // --- BRepGraphInc populate first --- - BRepGraphInc_Storage aOrigStorage; - BRepGraphInc_Populate::Perform(aOrigStorage, aCompSolid, false); - ASSERT_TRUE(aOrigStorage.GetIsDone()); - EXPECT_EQ(aOrigStorage.NbCompSolids(), 1); - ASSERT_GE(aOrigStorage.NbSolids(), 2); + BRepGraph aOrigGraph; + std::ignore = BRepGraphInc_Populate::Perform(aOrigGraph, aCompSolid, false); + ASSERT_FALSE(aOrigGraph.IsEmpty()); + EXPECT_EQ(aOrigGraph.Topo().CompSolids().Nb(), 1); + ASSERT_GE(aOrigGraph.Topo().Solids().Nb(), 2); - // Both solids must be reverse-indexed into the CompSolid. + // Both solids must be relation-tableed into the CompSolid. for (BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); - aSolidId.IsValid(aOrigStorage.NbSolids()); + aSolidId.IsValid(aOrigGraph.Topo().Solids().Nb()); ++aSolidId) { - const NCollection_DynamicArray* aCSVec = - aOrigStorage.ReverseIndex().CompSolidsOfSolid(aSolidId); - EXPECT_NE(aCSVec, nullptr) << "Solid " << aSolidId.Index << " not in any CompSolid"; + const NCollection_LinearVector& aSolidRefs = + aOrigGraph.Topo().Solids().Relations(aSolidId).ParentSolidRefIds; + EXPECT_GE(aSolidRefs.Size(), 1u) << "Solid " << aSolidId.Index << " not in any CompSolid"; } - EXPECT_TRUE(aOrigStorage.ValidateReverseIndex()) << "Reverse index must be consistent"; + EXPECT_TRUE( + BRepGraph_Validate::Perform(aOrigGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) + << "Relation table must be consistent"; // --- BRepGraph build --- BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aCompSolid); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aCompSolid); + ASSERT_FALSE(aGraph.IsEmpty()); // --- Validate(Audit) clean graph --- - EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()) + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) << "CompSolid graph must be structurally valid before mutation"; // --- Mutate: bump edge(0) tolerance --- @@ -353,7 +355,8 @@ TEST(BRepGraph_ScenarioMatrix, CompSolid_TwoBoxes_BothSubsystemsMutateReconstruc } // --- Validate(Audit) after mutation --- - EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()) + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) << "CompSolid graph must remain structurally valid after edge tolerance mutation"; // --- Reconstruct the CompSolid --- @@ -366,23 +369,24 @@ TEST(BRepGraph_ScenarioMatrix, CompSolid_TwoBoxes_BothSubsystemsMutateReconstruc EXPECT_EQ(countSubShapes(aRecon, TopAbs_SOLID), 2); // --- BRepGraphInc populate from reconstructed CompSolid --- - BRepGraphInc_Storage aReconStorage; - BRepGraphInc_Populate::Perform(aReconStorage, aRecon, false); - ASSERT_TRUE(aReconStorage.GetIsDone()); + BRepGraph aReconGraph; + std::ignore = BRepGraphInc_Populate::Perform(aReconGraph, aRecon, false); + ASSERT_FALSE(aReconGraph.IsEmpty()); - EXPECT_EQ(aReconStorage.NbCompSolids(), 1); - EXPECT_EQ(aReconStorage.NbSolids(), aOrigStorage.NbSolids()); - EXPECT_EQ(aReconStorage.NbFaces(), aOrigStorage.NbFaces()); - EXPECT_EQ(aReconStorage.NbEdges(), aOrigStorage.NbEdges()); + EXPECT_EQ(aReconGraph.Topo().CompSolids().Nb(), 1); + EXPECT_EQ(aReconGraph.Topo().Solids().Nb(), aOrigGraph.Topo().Solids().Nb()); + EXPECT_EQ(aReconGraph.Topo().Faces().Nb(), aOrigGraph.Topo().Faces().Nb()); + EXPECT_EQ(aReconGraph.Topo().Edges().Nb(), aOrigGraph.Topo().Edges().Nb()); - EXPECT_TRUE(aReconStorage.ValidateReverseIndex()) - << "Reverse index must be consistent for reconstructed CompSolid"; + EXPECT_TRUE( + BRepGraph_Validate::Perform(aReconGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) + << "Relation table must be consistent for reconstructed CompSolid"; } // ============================================================================= // Scenario 4: Assembly - two occurrences of a shared part -> Validate(Audit) // checks assembly DAG -> reconstruct the part -> BRepGraphInc populate from -// the reconstructed part -> entity counts and reverse index consistent. +// the reconstructed part -> entity counts and relation tables consistent. // ============================================================================= TEST(BRepGraph_ScenarioMatrix, Assembly_TwoOccurrences_ValidateDAGReconstructPartPopulate) @@ -390,16 +394,18 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_TwoOccurrences_ValidateDAGReconstructPar // Build the graph from a simple solid. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(8.0, 8.0, 8.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(8.0, 8.0, 8.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const int anOrigFaces = - countSubShapes(aGraph.Shapes().OriginalOf(BRepGraph_SolidId::Start()), TopAbs_FACE); + const TopoDS_Shape anOriginalSolid = aGraph.Shapes().Original(BRepGraph_SolidId::Start()); + ASSERT_FALSE(anOriginalSolid.IsNull()); + const int anOrigFaces = countSubShapes(anOriginalSolid, TopAbs_FACE); // Build assembly: two occurrences of the auto-created part at distinct translations. const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); ASSERT_TRUE(aAssemblyId.IsValid()); gp_Trsf aTrsf1; @@ -408,16 +414,16 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_TwoOccurrences_ValidateDAGReconstructPar aTrsf2.SetTranslation(gp_Vec(0.0, 200.0, 0.0)); const BRepGraph_OccurrenceId anOcc1 = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)); const BRepGraph_OccurrenceId anOcc2 = - aGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)); + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)); ASSERT_TRUE(anOcc1.IsValid()); ASSERT_TRUE(anOcc2.IsValid()); // --- Validate(Audit): assembly DAG must be acyclic and reference-consistent --- const BRepGraph_Validate::Result aResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aResult.IsValid()) << "Assembly graph with two occurrences must pass full audit"; EXPECT_EQ(aResult.NbIssues(BRepGraph_Validate::Severity::Error), 0); @@ -438,15 +444,16 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_TwoOccurrences_ValidateDAGReconstructPar EXPECT_EQ(countSubShapes(aPartShape, TopAbs_FACE), anOrigFaces); // --- BRepGraphInc populate from the reconstructed part --- - BRepGraphInc_Storage aPartStorage; - BRepGraphInc_Populate::Perform(aPartStorage, aPartShape, false); - ASSERT_TRUE(aPartStorage.GetIsDone()); + BRepGraph aPartGraph; + std::ignore = BRepGraphInc_Populate::Perform(aPartGraph, aPartShape, false); + ASSERT_FALSE(aPartGraph.IsEmpty()); - EXPECT_EQ(aPartStorage.NbSolids(), 1); - EXPECT_EQ(aPartStorage.NbFaces(), anOrigFaces); + EXPECT_EQ(aPartGraph.Topo().Solids().Nb(), 1); + EXPECT_EQ(aPartGraph.Topo().Faces().Nb(), anOrigFaces); - EXPECT_TRUE(aPartStorage.ValidateReverseIndex()) - << "Reverse index must be consistent for BRepGraphInc of reconstructed part"; + EXPECT_TRUE( + BRepGraph_Validate::Perform(aPartGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) + << "Relation table must be consistent for BRepGraphInc of reconstructed part"; } // ============================================================================= @@ -454,9 +461,8 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_TwoOccurrences_ValidateDAGReconstructPar // // Flow: build a compound containing three atomic sub-shapes (wire, edge, // vertex) -> BRepGraph build -> Validate(Audit) -> BRepGraphInc populate -> -// ValidateReverseIndex. Exercises the -// myCompoundsOfWire / myCompoundsOfEdge / myCompoundsOfVertex reverse-index -// paths end-to-end, combined with the BRepGraph structural check. +// ValidateRelations. Exercises atomic child-to-compound relation tablesing +// end-to-end, combined with the BRepGraph structural check. // ============================================================================= TEST(BRepGraph_ScenarioMatrix, Compound_FreeWireFreeEdgeFreeVertex_ValidateAndPopulate) @@ -486,9 +492,8 @@ TEST(BRepGraph_ScenarioMatrix, Compound_FreeWireFreeEdgeFreeVertex_ValidateAndPo // --- BRepGraph build --- BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Compounds().Nb(), 1); ASSERT_GE(aGraph.Topo().Wires().Nb(), 1); @@ -497,62 +502,21 @@ TEST(BRepGraph_ScenarioMatrix, Compound_FreeWireFreeEdgeFreeVertex_ValidateAndPo // --- Validate(Audit) --- const BRepGraph_Validate::Result aResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aResult.IsValid()) << "Compound with free wire/edge/vertex must pass full audit"; // --- BRepGraphInc populate --- - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aPopGraph; + std::ignore = BRepGraphInc_Populate::Perform(aPopGraph, aCompound, false); + ASSERT_FALSE(aPopGraph.IsEmpty()); - EXPECT_EQ(aStorage.NbCompounds(), 1); - ASSERT_GE(aStorage.NbWires(), 1); - ASSERT_GE(aStorage.NbEdges(), 2); + EXPECT_EQ(aPopGraph.Topo().Compounds().Nb(), 1u); + ASSERT_GE(aPopGraph.Topo().Wires().Nb(), 1u); + ASSERT_GE(aPopGraph.Topo().Edges().Nb(), 2u); - // Wire must be reverse-indexed into the compound. - { - const NCollection_DynamicArray* aCmpOfWire = - aStorage.ReverseIndex().CompoundsOfWire(BRepGraph_WireId::Start()); - EXPECT_NE(aCmpOfWire, nullptr) << "Free wire must appear in CompoundsOfWire reverse index"; - if (aCmpOfWire != nullptr) - { - EXPECT_GE(aCmpOfWire->Length(), 1); - } - } - - // At least one edge must be reverse-indexed directly into the compound - // (the free edge that is not inside a wire). - bool aFoundEdgeInCompound = false; - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aStorage.NbEdges()); ++anEdgeId) - { - const NCollection_DynamicArray* aCmpOfEdge = - aStorage.ReverseIndex().CompoundsOfEdge(anEdgeId); - if (aCmpOfEdge != nullptr && aCmpOfEdge->Length() > 0) - { - aFoundEdgeInCompound = true; - break; - } - } - EXPECT_TRUE(aFoundEdgeInCompound) << "Free edge must appear in CompoundsOfEdge reverse index"; - - // The specific free vertex (not wire/edge endpoint) must appear in - // CompoundsOfVertex. Locate it by TShape (its NodeId is assigned by Populate - // and is not known to the test) and verify the reverse-index entry exists. - const BRepGraph_NodeId* aFreeVtxNode = aStorage.FindNodeByTShape(aVtx.TShape().get()); - ASSERT_NE(aFreeVtxNode, nullptr) << "Free vertex TShape must be registered in storage"; - ASSERT_EQ(aFreeVtxNode->NodeKind, BRepGraph_NodeId::Kind::Vertex); - const BRepGraph_VertexId aFreeVtxId(aFreeVtxNode->Index); - - const NCollection_DynamicArray* aCmpOfFreeVtx = - aStorage.ReverseIndex().CompoundsOfVertex(aFreeVtxId); - EXPECT_NE(aCmpOfFreeVtx, nullptr) << "Free vertex must appear in CompoundsOfVertex reverse index"; - if (aCmpOfFreeVtx != nullptr) - { - EXPECT_GE(aCmpOfFreeVtx->Length(), 1); - } - - EXPECT_TRUE(aStorage.ValidateReverseIndex()) - << "Reverse index must be consistent for compound with atomic sub-shapes"; + EXPECT_TRUE( + BRepGraph_Validate::Perform(aPopGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) + << "Relation table must be consistent for compound with atomic sub-shapes"; } // ============================================================================= @@ -581,25 +545,21 @@ TEST(BRepGraph_ScenarioMatrix, Compound_BoxAndCylinder_MutationReconstructAreaRe const double anOrigTotalArea = anOrigBoxArea + anOrigCylArea; // --- BRepGraphInc: baseline population for regression check --- - BRepGraphInc_Storage aBaseStorage; - BRepGraphInc_Populate::Perform(aBaseStorage, aCompound, false); - ASSERT_TRUE(aBaseStorage.GetIsDone()); - EXPECT_EQ(aBaseStorage.NbSolids(), 2); - EXPECT_EQ(aBaseStorage.NbCompounds(), 1); + BRepGraph aBaseGraph; + std::ignore = BRepGraphInc_Populate::Perform(aBaseGraph, aCompound, false); + ASSERT_FALSE(aBaseGraph.IsEmpty()); + EXPECT_EQ(aBaseGraph.Topo().Solids().Nb(), 2); + EXPECT_EQ(aBaseGraph.Topo().Compounds().Nb(), 1); // Count seam edges in baseline. int aBaseSeamCount = 0; - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aBaseStorage.NbEdges()); ++anEdgeId) + for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aBaseGraph.Topo().Edges().Nb()); ++anEdgeId) { - const NCollection_DynamicArray* aCoEdges = - aBaseStorage.ReverseIndex().CoEdgesOfEdge(anEdgeId); - if (aCoEdges == nullptr) + const NCollection_LinearVector& aCoEdges = + aBaseGraph.Topo().Edges().Relations(anEdgeId).CoEdgeIds; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { - continue; - } - for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCoEdges) - { - if (BRepGraph_TestTools::IsSeamCoEdgeFromStorage(aBaseStorage, aCoEdgeId)) + if (BRepGraph_Tool::CoEdge::SeamPair(aBaseGraph, aCoEdgeId).IsValid()) { ++aBaseSeamCount; break; // count each seam edge once @@ -611,13 +571,13 @@ TEST(BRepGraph_ScenarioMatrix, Compound_BoxAndCylinder_MutationReconstructAreaRe // --- BRepGraph build --- BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Solids().Nb(), 2); // --- Validate(Audit) clean --- - EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()) + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) << "Compound [box+cylinder] must pass full audit before mutation"; // --- Mutate: change face(0) tolerance (a box face) --- @@ -629,7 +589,8 @@ TEST(BRepGraph_ScenarioMatrix, Compound_BoxAndCylinder_MutationReconstructAreaRe } // --- Validate(Audit) after mutation --- - EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()) + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) << "Compound [box+cylinder] graph must remain structurally valid after face tolerance change"; // --- Reconstruct the compound --- @@ -645,27 +606,23 @@ TEST(BRepGraph_ScenarioMatrix, Compound_BoxAndCylinder_MutationReconstructAreaRe << "Total surface area must be preserved after face-tolerance mutation + reconstruct"; // --- Second BRepGraphInc populate from reconstructed compound --- - BRepGraphInc_Storage aReconStorage; - BRepGraphInc_Populate::Perform(aReconStorage, aRecon, false); - ASSERT_TRUE(aReconStorage.GetIsDone()); + BRepGraph aReconGraph; + std::ignore = BRepGraphInc_Populate::Perform(aReconGraph, aRecon, false); + ASSERT_FALSE(aReconGraph.IsEmpty()); - EXPECT_EQ(aReconStorage.NbSolids(), aBaseStorage.NbSolids()); - EXPECT_EQ(aReconStorage.NbFaces(), aBaseStorage.NbFaces()); - EXPECT_EQ(aReconStorage.NbEdges(), aBaseStorage.NbEdges()); + EXPECT_EQ(aReconGraph.Topo().Solids().Nb(), aBaseGraph.Topo().Solids().Nb()); + EXPECT_EQ(aReconGraph.Topo().Faces().Nb(), aBaseGraph.Topo().Faces().Nb()); + EXPECT_EQ(aReconGraph.Topo().Edges().Nb(), aBaseGraph.Topo().Edges().Nb()); // Seam edges must still be present. int aReconSeamCount = 0; - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aReconStorage.NbEdges()); ++anEdgeId) + for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aReconGraph.Topo().Edges().Nb()); ++anEdgeId) { - const NCollection_DynamicArray* aCoEdges = - aReconStorage.ReverseIndex().CoEdgesOfEdge(anEdgeId); - if (aCoEdges == nullptr) + const NCollection_LinearVector& aCoEdges = + aReconGraph.Topo().Edges().Relations(anEdgeId).CoEdgeIds; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { - continue; - } - for (const BRepGraph_CoEdgeId& aCoEdgeId : *aCoEdges) - { - if (BRepGraph_TestTools::IsSeamCoEdgeFromStorage(aReconStorage, aCoEdgeId)) + if (BRepGraph_Tool::CoEdge::SeamPair(aReconGraph, aCoEdgeId).IsValid()) { ++aReconSeamCount; break; @@ -675,8 +632,9 @@ TEST(BRepGraph_ScenarioMatrix, Compound_BoxAndCylinder_MutationReconstructAreaRe EXPECT_EQ(aReconSeamCount, aBaseSeamCount) << "Seam edge count must be unchanged after compound reconstruct"; - EXPECT_TRUE(aReconStorage.ValidateReverseIndex()) - << "Reverse index must be consistent for reconstructed compound [box+cylinder]"; + EXPECT_TRUE( + BRepGraph_Validate::Perform(aReconGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) + << "Relation table must be consistent for reconstructed compound [box+cylinder]"; } // ============================================================================= @@ -692,15 +650,18 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_ThreeLevelNesting_CleanAudit_CycleDetect { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Four-level chain: Top -> Root -> Mid -> Leaf (leaf is the auto-built part). const BRepGraph_ProductId aLeafPart = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aMidAsm = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aRootAsm = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aTopAsm = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aMidAsm = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aMidAsm); + const BRepGraph_ProductId aRootAsm = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootAsm); + const BRepGraph_ProductId aTopAsm = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aTopAsm); gp_Trsf aT1, aT2, aT3; aT1.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); @@ -708,11 +669,11 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_ThreeLevelNesting_CleanAudit_CycleDetect aT3.SetTranslation(gp_Vec(0.0, 0.0, 3.0)); const BRepGraph_OccurrenceId anOccRoot = - aGraph.Editor().Products().LinkProducts(aTopAsm, aRootAsm, TopLoc_Location(aT3)); + aGraph.Editor().Products().Append(aTopAsm, aRootAsm, TopLoc_Location(aT3)); const BRepGraph_OccurrenceId anOccMid = - aGraph.Editor().Products().LinkProducts(aRootAsm, aMidAsm, TopLoc_Location(aT2), anOccRoot); + aGraph.Editor().Products().Append(aRootAsm, aMidAsm, TopLoc_Location(aT2), anOccRoot); const BRepGraph_OccurrenceId anOccLeaf = - aGraph.Editor().Products().LinkProducts(aMidAsm, aLeafPart, TopLoc_Location(aT1), anOccMid); + aGraph.Editor().Products().Append(aMidAsm, aLeafPart, TopLoc_Location(aT1), anOccMid); ASSERT_TRUE(anOccRoot.IsValid()); ASSERT_TRUE(anOccMid.IsValid()); ASSERT_TRUE(anOccLeaf.IsValid()); @@ -724,19 +685,21 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_ThreeLevelNesting_CleanAudit_CycleDetect // --- Clean audit on the 4-level DAG --- const BRepGraph_Validate::Result aCleanResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aCleanResult.IsValid()) << "Four-level assembly must pass full audit when acyclic"; // --- Transitive cycle: C -> D, then D -> C. Self-ref guard skips this because // C.Index != D.Index, but the BFS-based Audit check must still catch it. --- - const BRepGraph_ProductId aProdC = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aProdD = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aProdC = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aProdC); + const BRepGraph_ProductId aProdD = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aProdD); gp_Trsf aIdTrsf; ASSERT_TRUE( - aGraph.Editor().Products().LinkProducts(aProdC, aProdD, TopLoc_Location(aIdTrsf)).IsValid()); + aGraph.Editor().Products().Append(aProdC, aProdD, TopLoc_Location(aIdTrsf)).IsValid()); ASSERT_TRUE( - aGraph.Editor().Products().LinkProducts(aProdD, aProdC, TopLoc_Location(aIdTrsf)).IsValid()); + aGraph.Editor().Products().Append(aProdD, aProdC, TopLoc_Location(aIdTrsf)).IsValid()); const BRepGraph_Validate::Result aCycleResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); @@ -764,30 +727,28 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_SharedPartBetweenTwoRootAssemblies) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(5.0, 5.0, 5.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(5.0, 5.0, 5.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Snapshot topology entity counts before any assembly wiring. - const int aNbSolidsBefore = aGraph.Topo().Solids().Nb(); - const int aNbFacesBefore = aGraph.Topo().Faces().Nb(); + const uint32_t aNbSolidsBefore = aGraph.Topo().Solids().Nb(); + const uint32_t aNbFacesBefore = aGraph.Topo().Faces().Nb(); const BRepGraph_ProductId aSharedPart = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAsmA = aGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aAsmB = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAsmA = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAsmA); + const BRepGraph_ProductId aAsmB = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAsmB); gp_Trsf aOffsetA, aOffsetB; aOffsetA.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); aOffsetB.SetTranslation(gp_Vec(0.0, 100.0, 0.0)); - ASSERT_TRUE(aGraph.Editor() - .Products() - .LinkProducts(aAsmA, aSharedPart, TopLoc_Location(aOffsetA)) - .IsValid()); - ASSERT_TRUE(aGraph.Editor() - .Products() - .LinkProducts(aAsmB, aSharedPart, TopLoc_Location(aOffsetB)) - .IsValid()); + ASSERT_TRUE( + aGraph.Editor().Products().Append(aAsmA, aSharedPart, TopLoc_Location(aOffsetA)).IsValid()); + ASSERT_TRUE( + aGraph.Editor().Products().Append(aAsmB, aSharedPart, TopLoc_Location(aOffsetB)).IsValid()); // The shared part must not have grown the topology pool. EXPECT_EQ(aGraph.Topo().Solids().Nb(), aNbSolidsBefore) @@ -795,7 +756,8 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_SharedPartBetweenTwoRootAssemblies) EXPECT_EQ(aGraph.Topo().Faces().Nb(), aNbFacesBefore); // Audit passes: DAG sharing is not a cycle. - EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()) + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) << "Shared Product across assemblies must pass audit"; // Reconstruct each assembly and verify each emits a TopoDS_Compound with @@ -821,15 +783,15 @@ TEST(BRepGraph_ScenarioMatrix, Assembly_SharedPartBetweenTwoRootAssemblies) // ============================================================================= // Scenario 9: Compound atomic mixed contents - one Compound holding // Solid + Shell + isolated Face + free Edge + free Vertex simultaneously. -// Exercises every reverse-index slot in myCompoundsOf* and verifies that an -// atomic-heterogeneous compound survives Validate(Audit) + reverse-index round +// Exercises mixed child-to-compound relation tablesing and verifies that an +// atomic-heterogeneous compound survives Validate(Audit) + relation-table round // trip via BRepGraphInc_Populate. // ============================================================================= -TEST(BRepGraph_ScenarioMatrix, Compound_MixedAtomicChildren_ReverseIndexCoverage) +TEST(BRepGraph_ScenarioMatrix, Compound_MixedAtomicChildren_RelationsCoverage) { // Build five constituents as separate top-level shapes, wrap them in a single - // TopoDS_Compound, then let BRepGraph_Builder handle population. This bypasses + // TopoDS_Compound, then let BRepGraph::ShapesView handle population. This bypasses // EditorView::Compounds().Add() (which needs existing NodeIds) and exercises // the builder's heterogeneous-child path. const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(5.0, 5.0, 5.0).Shape(); @@ -870,27 +832,29 @@ TEST(BRepGraph_ScenarioMatrix, Compound_MixedAtomicChildren_ReverseIndexCoverage BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Compounds().Nb(), 0); // Clean audit. const BRepGraph_Validate::Result aAuditResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aAuditResult.IsValid()) << "Compound with heterogeneous atomic children must pass Audit"; // The compound at index 0 must enumerate at least one each of solid/shell/ // face/edge/vertex via its child refs. - const BRepGraph_CompoundId aCompoundId(0); - bool aHasSolid = false, aHasShell = false, aHasFace = false; - bool aHasEdge = false, aHasVertex = false; - const BRepGraphInc::CompoundDef& aCompDef = aGraph.Topo().Compounds().Definition(aCompoundId); - for (const BRepGraph_ChildRefId& aChildRefId : aCompDef.ChildRefIds) + const BRepGraph_CompoundId aCompoundId(0); + bool aHasSolid = false; + [[maybe_unused]] bool aHasShell = false; + [[maybe_unused]] bool aHasFace = false; + bool aHasEdge = false, aHasVertex = false; + const NCollection_LinearVector& aChildRefs = + aGraph.Topo().Compounds().Relations(aCompoundId).ChildRefIds; + for (const BRepGraph_ChildRefId& aChildRefId : aChildRefs) { const BRepGraphInc::ChildRef& aRef = aGraph.Refs().Children().Entry(aChildRefId); - switch (aRef.ChildDefId.NodeKind) + switch (aRef.ChildNodeId.NodeKind) { case BRepGraph_NodeId::Kind::Solid: aHasSolid = true; @@ -916,46 +880,24 @@ TEST(BRepGraph_ScenarioMatrix, Compound_MixedAtomicChildren_ReverseIndexCoverage EXPECT_TRUE(aHasVertex) << "Compound must carry a ChildRef of kind Vertex"; // Shell/Face may be dedup'd inside the box instead of landing as direct child // refs; their presence in the atomic compound is best-effort per builder. - (void)aHasShell; - (void)aHasFace; - // Reverse-index coverage: drive through BRepGraphInc_Populate so the returned - // storage exposes ReverseIndex() directly (BRepGraph keeps its incStorage() - // private; the same invariants apply). - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompound, false); - ASSERT_TRUE(aStorage.GetIsDone()); - EXPECT_TRUE(aStorage.ValidateReverseIndex()) - << "Reverse index must be consistent for atomic-mixed compound"; - - const BRepGraphInc_ReverseIndex& aRev = aStorage.ReverseIndex(); - if (aStorage.NbSolids() > 0) - { - const NCollection_DynamicArray* aParents = - aRev.CompoundsOfSolid(BRepGraph_SolidId::Start()); - EXPECT_TRUE(aParents == nullptr || aParents->Length() >= 0); - } - if (aStorage.NbEdges() > 0) - { - const NCollection_DynamicArray* aParents = - aRev.CompoundsOfEdge(BRepGraph_EdgeId::Start()); - (void)aParents; - } - if (aStorage.NbVertices() > 0) - { - const NCollection_DynamicArray* aParents = - aRev.CompoundsOfVertex(BRepGraph_VertexId::Start()); - (void)aParents; - } + // Relation coverage: drive through BRepGraphInc_Populate so the returned + // storage exposes direct relation containers. + BRepGraph aPopGraph; + std::ignore = BRepGraphInc_Populate::Perform(aPopGraph, aCompound, false); + ASSERT_FALSE(aPopGraph.IsEmpty()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aPopGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) + << "Relation table must be consistent for atomic-mixed compound"; } // ============================================================================= -// Scenario 10: CompSolid holding three boxes - reverse-index must cover every +// Scenario 10: CompSolid holding three boxes - relation-table must cover every // solid, and every solid must see exactly one CompSolid parent in the reverse // map. Exercises the myCompSolidsOfSolid path with N>2. // ============================================================================= -TEST(BRepGraph_ScenarioMatrix, CompSolid_ThreeBoxes_ReverseIndexPerSolid) +TEST(BRepGraph_ScenarioMatrix, CompSolid_ThreeBoxes_RelationsPerSolid) { BRep_Builder aBB; TopoDS_CompSolid aCompSolid; @@ -967,32 +909,31 @@ TEST(BRepGraph_ScenarioMatrix, CompSolid_ThreeBoxes_ReverseIndexPerSolid) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aCompSolid); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = + aGraph.Shapes().Add(aCompSolid); + ASSERT_FALSE(aGraph.IsEmpty()); - EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()) + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) << "Three-solid CompSolid must pass full audit"; - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aCompSolid, false); - ASSERT_TRUE(aStorage.GetIsDone()); - ASSERT_EQ(aStorage.NbCompSolids(), 1); - ASSERT_GE(aStorage.NbSolids(), 3); + BRepGraph aPopGraph; + std::ignore = BRepGraphInc_Populate::Perform(aPopGraph, aCompSolid, false); + ASSERT_FALSE(aPopGraph.IsEmpty()); + ASSERT_EQ(aPopGraph.Topo().CompSolids().Nb(), 1); + ASSERT_GE(aPopGraph.Topo().Solids().Nb(), 3); - for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(aStorage.NbSolids()); ++aSolidId) + for (BRepGraph_SolidId aSolidId(0); aSolidId.IsValid(aPopGraph.Topo().Solids().Nb()); ++aSolidId) { - const NCollection_DynamicArray* aParents = - aStorage.ReverseIndex().CompSolidsOfSolid(aSolidId); - ASSERT_NE(aParents, nullptr) << "Solid " << aSolidId.Index - << " missing from CompSolid reverse index"; - ASSERT_EQ(aParents->Length(), 1) + const NCollection_LinearVector& aParents = + aPopGraph.Topo().Solids().Relations(aSolidId).ParentSolidRefIds; + ASSERT_EQ(aParents.Size(), 1u) << "Solid " << aSolidId.Index << " should have exactly one CompSolid parent"; - EXPECT_EQ(aParents->Value(0), BRepGraph_CompSolidId::Start()); } - EXPECT_TRUE(aStorage.ValidateReverseIndex()) - << "Reverse index must be consistent for 3-solid CompSolid"; + EXPECT_TRUE( + BRepGraph_Validate::Perform(aPopGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) + << "Relation table must be consistent for 3-solid CompSolid"; } // ============================================================================= @@ -1007,15 +948,15 @@ TEST(BRepGraph_ScenarioMatrix, Sphere_SeamCoEdgePair_Bidirectional) { const TopoDS_Shape aSphere = BRepPrimAPI_MakeSphere(10.0).Shape(); - BRepGraphInc_Storage aStorage; - BRepGraphInc_Populate::Perform(aStorage, aSphere, false); - ASSERT_TRUE(aStorage.GetIsDone()); + BRepGraph aPopGraph; + std::ignore = BRepGraphInc_Populate::Perform(aPopGraph, aSphere, false); + ASSERT_FALSE(aPopGraph.IsEmpty()); int aNbPairedCoEdges = 0; - for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aStorage.NbCoEdges()); ++aCoEdgeId) + for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aPopGraph.Topo().CoEdges().Nb()); + ++aCoEdgeId) { - const BRepGraph_CoEdgeId aPairId = - BRepGraph_TestTools::SeamPairFromStorage(aStorage, aCoEdgeId); + const BRepGraph_CoEdgeId aPairId = BRepGraph_Tool::CoEdge::SeamPair(aPopGraph, aCoEdgeId); if (!aPairId.IsValid()) { continue; @@ -1023,11 +964,11 @@ TEST(BRepGraph_ScenarioMatrix, Sphere_SeamCoEdgePair_Bidirectional) ++aNbPairedCoEdges; // Symmetry: the partner's partner is us. - const BRepGraph_CoEdgeId aBackId = BRepGraph_TestTools::SeamPairFromStorage(aStorage, aPairId); + const BRepGraph_CoEdgeId aBackId = BRepGraph_Tool::CoEdge::SeamPair(aPopGraph, aPairId); EXPECT_EQ(aBackId, aCoEdgeId) << "Seam relation must be symmetric"; - const BRepGraphInc::CoEdgeDef& aCoEdge = aStorage.CoEdge(aCoEdgeId); - const BRepGraphInc::CoEdgeDef& aPaired = aStorage.CoEdge(aPairId); - EXPECT_EQ(aPaired.EdgeDefId, aCoEdge.EdgeDefId) + const BRepGraphInc::CoEdgeDef& aCoEdge = aPopGraph.Topo().CoEdges().Definition(aCoEdgeId); + const BRepGraphInc::CoEdgeDef& aPaired = aPopGraph.Topo().CoEdges().Definition(aPairId); + EXPECT_EQ(aPaired.ChildEdgeId, aCoEdge.ChildEdgeId) << "Seam pair must share the same underlying EdgeDef"; } EXPECT_GT(aNbPairedCoEdges, 0) << "Sphere must have at least one seam-paired coedge"; @@ -1035,32 +976,31 @@ TEST(BRepGraph_ScenarioMatrix, Sphere_SeamCoEdgePair_Bidirectional) // Build the graph, audit, reconstruct, repopulate, re-verify symmetry. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, aSphere); - ASSERT_TRUE(aGraph.IsDone()); - EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()) + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = aGraph.Shapes().Add(aSphere); + ASSERT_FALSE(aGraph.IsEmpty()); + EXPECT_TRUE( + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()).IsValid()) << "Sphere graph must pass full audit"; TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); ASSERT_FALSE(aRecon.IsNull()); - BRepGraphInc_Storage aReconStorage; - BRepGraphInc_Populate::Perform(aReconStorage, aRecon, false); - ASSERT_TRUE(aReconStorage.GetIsDone()); + BRepGraph aReconGraph; + std::ignore = BRepGraphInc_Populate::Perform(aReconGraph, aRecon, false); + ASSERT_FALSE(aReconGraph.IsEmpty()); int aNbPairedAfter = 0; - for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aReconStorage.NbCoEdges()); ++aCoEdgeId) + for (BRepGraph_CoEdgeId aCoEdgeId(0); aCoEdgeId.IsValid(aReconGraph.Topo().CoEdges().Nb()); + ++aCoEdgeId) { - const BRepGraph_CoEdgeId aPairId = - BRepGraph_TestTools::SeamPairFromStorage(aReconStorage, aCoEdgeId); + const BRepGraph_CoEdgeId aPairId = BRepGraph_Tool::CoEdge::SeamPair(aReconGraph, aCoEdgeId); if (!aPairId.IsValid()) { continue; } ++aNbPairedAfter; - const BRepGraph_CoEdgeId aBackId = - BRepGraph_TestTools::SeamPairFromStorage(aReconStorage, aPairId); + const BRepGraph_CoEdgeId aBackId = BRepGraph_Tool::CoEdge::SeamPair(aReconGraph, aPairId); EXPECT_EQ(aBackId, aCoEdgeId) << "Post-reconstruct seam pair must remain symmetric"; } EXPECT_EQ(aNbPairedAfter, aNbPairedCoEdges) @@ -1068,64 +1008,9 @@ TEST(BRepGraph_ScenarioMatrix, Sphere_SeamCoEdgePair_Bidirectional) } // ============================================================================= -// Scenario 12: Rep orphan detection - Curve3DRep is soft-removed while an -// EdgeDef still forward-references it. Validate(Audit) must flag the orphan. -// ============================================================================= - -TEST(BRepGraph_ScenarioMatrix, Validate_OrphanCurve3DRep_FlaggedByAudit) -{ - BRepGraph aGraph; - aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); - - // Find an edge with a Curve3DRepId. - BRepGraph_EdgeId aVictimEdgeId; - for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aGraph.Topo().Edges().Nb()); ++anEdgeId) - { - const BRepGraphInc::EdgeDef& anEdge = aGraph.Topo().Edges().Definition(anEdgeId); - if (anEdge.Curve3DRepId.IsValid()) - { - aVictimEdgeId = anEdgeId; - break; - } - } - ASSERT_TRUE(aVictimEdgeId.IsValid()) << "Expected at least one box edge to carry a Curve3DRepId"; - - const BRepGraph_Curve3DRepId aVictimRep = - aGraph.Topo().Edges().Definition(aVictimEdgeId).Curve3DRepId; - - // Baseline Validate is clean. - EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); - - // Mark the rep as removed without clearing the back-reference. - aGraph.Editor().Gen().RemoveRep(BRepGraph_RepId(aVictimRep)); - - const BRepGraph_Validate::Result aPostRemove = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); - EXPECT_FALSE(aPostRemove.IsValid()) - << "Audit must flag an edge that references a removed Curve3DRep"; - - bool aFoundOrphan = false; - for (const BRepGraph_Validate::Issue& anIssue : aPostRemove.Issues) - { - if (anIssue.Description.Search("removed Curve3DRep") >= 0) - { - aFoundOrphan = true; - break; - } - } - EXPECT_TRUE(aFoundOrphan) << "Audit must describe the removed Curve3DRep orphan"; - - // Unconditional: the edge's rep id is still the pre-removal value. - EXPECT_EQ(aGraph.Topo().Edges().Definition(aVictimEdgeId).Curve3DRepId, aVictimRep); -} - -// ============================================================================= -// Scenario 13: Cylinder seam-edge Split - exercises the seam-pair case of +// Scenario 12: Cylinder seam-edge Split - exercises the seam-pair case of // EdgeOps::Split(). The unified CoEdge rebuild pass allocates two fresh sub- -// coedges per original (including seam partners), preserves SeamPairId +// coedges per original (including seam partners), preserves derived seam pairing // linkage on the new pairs, and retires orphan vertex refs. // ============================================================================= @@ -1133,15 +1018,15 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdgeSplit_AuditStable) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeCylinder(5.0, 15.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = + aGraph.Shapes().Add(BRepPrimAPI_MakeCylinder(5.0, 15.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); // Locate a seam edge: an edge with at least one seam-paired coedge. BRepGraph_EdgeId aSeamEdgeId; for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aGraph.Topo().Edges().Nb()); ++anEdgeId) { - const NCollection_DynamicArray& aCoEdges = + const NCollection_LinearVector& aCoEdges = aGraph.Topo().Edges().CoEdges(anEdgeId); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { @@ -1158,11 +1043,11 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdgeSplit_AuditStable) } ASSERT_TRUE(aSeamEdgeId.IsValid()) << "Cylinder must carry at least one seam edge"; - const BRepGraphInc::EdgeDef& aSeamDef = aGraph.Topo().Edges().Definition(aSeamEdgeId); - ASSERT_FALSE(aSeamDef.IsDegenerate); - ASSERT_LT(aSeamDef.ParamFirst, aSeamDef.ParamLast); + ASSERT_FALSE(BRepGraph_Tool::Edge::Degenerated(aGraph, aSeamEdgeId)); + const std::pair aSeamRange = BRepGraph_Tool::Edge::Range(aGraph, aSeamEdgeId); + ASSERT_LT(aSeamRange.first, aSeamRange.second); - const double aMidParam = 0.5 * (aSeamDef.ParamFirst + aSeamDef.ParamLast); + const double aMidParam = 0.5 * (aSeamRange.first + aSeamRange.second); const BRepGraph_VertexId aSplitVertex = aGraph.Editor().Vertices().Add(gp_Pnt(5.0, 0.0, 7.5), 1.0e-7); @@ -1173,17 +1058,17 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdgeSplit_AuditStable) ASSERT_TRUE(aSubA.IsValid()); ASSERT_TRUE(aSubB.IsValid()); - // The full audit catches any reverse-index drift that the Split path leaves + // The full audit catches any relation-table drift that the Split path leaves // behind. If this starts failing, the Split implementation needs to be - // revisited to also update SeamPairId on the new sub-coedges. + // revisited to also update derived seam pair connectivity on the new sub-coedges. const BRepGraph_Validate::Result aResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aResult.IsValid()) << "Audit must remain clean after splitting a seam edge"; // Every seam-paired coedge on the live sub-edges must still satisfy the // symmetry of the connectivity-derived seam predicate (SeamPair(SeamPair(x))==x). auto checkBidirectionalOnEdge = [&](const BRepGraph_EdgeId theEdgeId) { - const NCollection_DynamicArray& aCoEdges = + const NCollection_LinearVector& aCoEdges = aGraph.Topo().Edges().CoEdges(theEdgeId); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { @@ -1202,9 +1087,90 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdgeSplit_AuditStable) checkBidirectionalOnEdge(aSubB); } +// ============================================================================= +// Scenario 13: direct editor-created face-bound PCurve CoEdge must be wired into +// edge incidence immediately. This is the typed replacement for raw rep deletion: +// the public editor operation creates geometry and relation state atomically. +// ============================================================================= + +TEST(BRepGraph_ScenarioMatrix, EditorFaceBoundPCurveCoEdge_RelationsAndLookup) +{ + BRepGraph aGraph; + aGraph.Clear(); + + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 0.0, 0.0), 1.0e-7); + ASSERT_TRUE(aV0.IsValid()); + ASSERT_TRUE(aV1.IsValid()); + + const BRepGraph_EdgeId anEdgeId = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 1.0, 1.0e-7); + ASSERT_TRUE(anEdgeId.IsValid()); + + const BRepGraph_CoEdgeId aWireCoEdgeId = aGraph.Editor().CoEdges().Add(anEdgeId, TopAbs_FORWARD); + ASSERT_TRUE(aWireCoEdgeId.IsValid()); + + NCollection_LinearVector aWireCoEdgeIds; + aWireCoEdgeIds.Append(aWireCoEdgeId); + const BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aWireCoEdgeIds.ToArray1()); + ASSERT_TRUE(aWireId.IsValid()); + + NCollection_LinearVector anInnerWireIds; + const occ::handle aPlane = + new Geom_Plane(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0)); + const BRepGraph_FaceId aFaceId = + aGraph.Editor().Faces().Add(aPlane, aWireId, anInnerWireIds.ToArray1(), 1.0e-7); + ASSERT_TRUE(aFaceId.IsValid()); + + const occ::handle aCurve2d = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); + const BRepGraph_CoEdgeId aPCurveCoEdgeId = + aGraph.Editor().CoEdges().Add(anEdgeId, aFaceId, aCurve2d, 0.0, 1.0, TopAbs_REVERSED); + ASSERT_TRUE(aPCurveCoEdgeId.IsValid()); + + const BRepGraphInc::CoEdgeDef& aPCurveCoEdge = + aGraph.Topo().CoEdges().Definition(aPCurveCoEdgeId); + EXPECT_EQ(aPCurveCoEdge.ChildEdgeId, anEdgeId); + EXPECT_EQ(aPCurveCoEdge.FaceId, aFaceId); + EXPECT_EQ(aPCurveCoEdge.Orientation, TopAbs_REVERSED); + EXPECT_TRUE(aPCurveCoEdge.Curve2DRepId.IsValid()); + + EXPECT_EQ(BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, anEdgeId, aFaceId), aPCurveCoEdgeId); + EXPECT_EQ(BRepGraph_Tool::Edge::FindPCurveCoEdgeId(aGraph, anEdgeId, aFaceId, TopAbs_REVERSED), + aPCurveCoEdgeId); + + bool isCoEdgeListedByEdge = false; + for (const BRepGraph_CoEdgeId& aCoEdgeId : aGraph.Topo().Edges().CoEdges(anEdgeId)) + { + if (aCoEdgeId == aPCurveCoEdgeId) + { + isCoEdgeListedByEdge = true; + break; + } + } + EXPECT_TRUE(isCoEdgeListedByEdge) + << "Face-bound PCurve CoEdge must be reachable through EdgeRelations::CoEdgeIds"; + + bool isFaceListedByEdge = false; + for (const BRepGraph_FaceId& anAdjacentFaceId : aGraph.Topo().Edges().FacesOf(anEdgeId)) + { + if (anAdjacentFaceId == aFaceId) + { + isFaceListedByEdge = true; + break; + } + } + EXPECT_TRUE(isFaceListedByEdge) + << "Face-bound PCurve CoEdge must be visible through edge-face adjacency"; + + EXPECT_TRUE(aGraph.ValidateRelations()); + const BRepGraph_Validate::Result aAudit = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); + EXPECT_TRUE(aAudit.IsValid()); +} + // ============================================================================= // Scenario 14: after a seam-edge split every CoEdge on the new sub-edges must -// carry a Curve2DRep, a valid FaceDefId, and must appear in the reverse-index +// carry a Curve2DRep, a valid FaceId, and must appear in the relation-table // wire / edge adjacencies. Guards the "CoEdgeDef has no Curve2D representation" // and CoEdge->Wire binding regressions. // ============================================================================= @@ -1213,14 +1179,14 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdgeSplit_CoEdgeFaceIncidence) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeCylinder(5.0, 15.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(BRepPrimAPI_MakeCylinder(5.0, 15.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_EdgeId aSeamEdgeId; for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aGraph.Topo().Edges().Nb()); ++anEdgeId) { - const NCollection_DynamicArray& aCoEdges = + const NCollection_LinearVector& aCoEdges = aGraph.Topo().Edges().CoEdges(anEdgeId); for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { @@ -1237,9 +1203,9 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdgeSplit_CoEdgeFaceIncidence) } ASSERT_TRUE(aSeamEdgeId.IsValid()); - const BRepGraphInc::EdgeDef& aSeamDef = aGraph.Topo().Edges().Definition(aSeamEdgeId); - const double aMidParam = 0.5 * (aSeamDef.ParamFirst + aSeamDef.ParamLast); - const BRepGraph_VertexId aSplitVertex = + const std::pair aSeamRange2 = BRepGraph_Tool::Edge::Range(aGraph, aSeamEdgeId); + const double aMidParam = 0.5 * (aSeamRange2.first + aSeamRange2.second); + const BRepGraph_VertexId aSplitVertex = aGraph.Editor().Vertices().Add(gp_Pnt(5.0, 0.0, 7.5), 1.0e-7); BRepGraph_EdgeId aSubA, aSubB; @@ -1248,17 +1214,18 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdgeSplit_CoEdgeFaceIncidence) ASSERT_TRUE(aSubB.IsValid()); auto checkCoEdgeIncidence = [&](const BRepGraph_EdgeId theSubId) { - const NCollection_DynamicArray& aCoEdges = + const NCollection_LinearVector& aCoEdges = aGraph.Topo().Edges().CoEdges(theSubId); - ASSERT_GT(aCoEdges.Length(), 0) << "Sub-edge must carry at least one CoEdge"; + ASSERT_GT(aCoEdges.Size(), 0u) << "Sub-edge must carry at least one CoEdge"; for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdges) { const BRepGraphInc::CoEdgeDef& aCoEdge = aGraph.Topo().CoEdges().Definition(aCoEdgeId); EXPECT_TRUE(aCoEdge.Curve2DRepId.IsValid()) << "Sub-CoEdge must carry Curve2DRepId after Split"; - EXPECT_TRUE(aCoEdge.FaceDefId.IsValid()) << "Sub-CoEdge must carry FaceDefId after Split"; - EXPECT_EQ(aCoEdge.EdgeDefId, theSubId); - EXPECT_LT(aCoEdge.ParamFirst, aCoEdge.ParamLast) + EXPECT_TRUE(aCoEdge.FaceId.IsValid()) << "Sub-CoEdge must carry FaceId after Split"; + EXPECT_EQ(aCoEdge.ChildEdgeId, theSubId); + const std::pair aCERange = BRepGraph_Tool::CoEdge::Range(aGraph, aCoEdgeId); + EXPECT_LT(aCERange.first, aCERange.second) << "Sub-CoEdge must have a non-degenerate parameter range"; } }; @@ -1266,41 +1233,43 @@ TEST(BRepGraph_ScenarioMatrix, Cylinder_SeamEdgeSplit_CoEdgeFaceIncidence) checkCoEdgeIncidence(aSubA); checkCoEdgeIncidence(aSubB); - // A full Audit is the canonical reverse-index / wire-incidence check. If + // A full Audit is the canonical relation-table / wire-incidence check. If // any sub-CoEdge were dangling or the Edge->Face cache were stale, Audit // would flag it. Keeping this assertion makes scenario 14 a useful - // regression gate even without direct reverse-index access in tests. + // regression gate even without direct relation-table access in tests. const BRepGraph_Validate::Result aAudit = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aAudit.IsValid()) << "Audit must remain clean after seam-edge Split"; } // ============================================================================= // Scenario 15: after Split the original edge's boundary vertex refs must be // retired (IsRemoved=true). Without retirement those refs become orphans -// whose ParentId points at a removed edge - the "Orphan VertexRef: ParentId -// is not a live Edge" Audit rule fires. +// because no live edge slot owns them. // ============================================================================= TEST(BRepGraph_ScenarioMatrix, BoxEdgeSplit_BoundaryVertexRetirement) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_EdgeId anEdgeId; double aSplitParam = 0.0; - const int aNbEdges = aGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId aCand(0); aCand.IsValid(aNbEdges); ++aCand) { const BRepGraphInc::EdgeDef& anEdgeDef = aGraph.Topo().Edges().Definition(aCand); - if (!anEdgeDef.IsDegenerate && anEdgeDef.Curve3DRepId.IsValid() + if (!BRepGraph_Tool::Edge::Degenerated(aGraph, aCand) && anEdgeDef.Curve3DRepId.IsValid() && anEdgeDef.StartVertexRefId.IsValid() && anEdgeDef.EndVertexRefId.IsValid()) { - anEdgeId = aCand; - aSplitParam = 0.5 * (anEdgeDef.ParamFirst + anEdgeDef.ParamLast); + anEdgeId = aCand; + { + const auto _r = BRepGraph_Tool::Edge::Range(aGraph, aCand); + aSplitParam = 0.5 * (_r.first + _r.second); + } break; } } @@ -1312,8 +1281,8 @@ TEST(BRepGraph_ScenarioMatrix, BoxEdgeSplit_BoundaryVertexRetirement) aGraph.Topo().Edges().Definition(anEdgeId).EndVertexRefId; ASSERT_TRUE(aPreStartRefId.IsValid()); ASSERT_TRUE(aPreEndRefId.IsValid()); - ASSERT_FALSE(aGraph.Refs().Vertices().Entry(aPreStartRefId).IsRemoved); - ASSERT_FALSE(aGraph.Refs().Vertices().Entry(aPreEndRefId).IsRemoved); + ASSERT_FALSE(aPreStartRefId.IsRemoved(aGraph)); + ASSERT_FALSE(aPreEndRefId.IsRemoved(aGraph)); const BRepGraph_VertexId aSplitVertex = aGraph.Editor().Vertices().Add(gp_Pnt(5.0, 10.0, 15.0), 1.0e-7); @@ -1323,13 +1292,13 @@ TEST(BRepGraph_ScenarioMatrix, BoxEdgeSplit_BoundaryVertexRetirement) ASSERT_TRUE(aSubA.IsValid()); ASSERT_TRUE(aSubB.IsValid()); - EXPECT_TRUE(aGraph.Refs().Vertices().Entry(aPreStartRefId).IsRemoved) + EXPECT_TRUE(aPreStartRefId.IsRemoved(aGraph)) << "Split must retire the original edge's StartVertexRef"; - EXPECT_TRUE(aGraph.Refs().Vertices().Entry(aPreEndRefId).IsRemoved) + EXPECT_TRUE(aPreEndRefId.IsRemoved(aGraph)) << "Split must retire the original edge's EndVertexRef"; const BRepGraph_Validate::Result aResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); EXPECT_TRUE(aResult.IsValid()) << "Audit must remain clean after Split retires boundary vertex refs"; } @@ -1338,20 +1307,23 @@ TEST(BRepGraph_ScenarioMatrix, BoxEdgeSplit_SubEdgesHaveNoOriginal) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_EdgeId anEdgeId; double aSplitParam = 0.0; - const int aNbEdges = aGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId aCand(0); aCand.IsValid(aNbEdges); ++aCand) { const BRepGraphInc::EdgeDef& anEdgeDef = aGraph.Topo().Edges().Definition(aCand); - if (!anEdgeDef.IsDegenerate && anEdgeDef.Curve3DRepId.IsValid()) + if (!BRepGraph_Tool::Edge::Degenerated(aGraph, aCand) && anEdgeDef.Curve3DRepId.IsValid()) { - anEdgeId = aCand; - aSplitParam = 0.5 * (anEdgeDef.ParamFirst + anEdgeDef.ParamLast); + anEdgeId = aCand; + { + const auto _r = BRepGraph_Tool::Edge::Range(aGraph, aCand); + aSplitParam = 0.5 * (_r.first + _r.second); + } break; } } @@ -1367,32 +1339,31 @@ TEST(BRepGraph_ScenarioMatrix, BoxEdgeSplit_SubEdgesHaveNoOriginal) EXPECT_FALSE(aGraph.Shapes().HasOriginal(aSubA)); EXPECT_FALSE(aGraph.Shapes().HasOriginal(aSubB)); - EXPECT_EQ(aGraph.Shapes().FindOriginal(aSubA), nullptr); - EXPECT_EQ(aGraph.Shapes().FindOriginal(aSubB), nullptr); -#ifndef No_Exception - EXPECT_THROW((void)aGraph.Shapes().OriginalOf(aSubA), Standard_ProgramError); - EXPECT_THROW((void)aGraph.Shapes().OriginalOf(aSubB), Standard_ProgramError); -#endif + EXPECT_TRUE(aGraph.Shapes().Original(aSubA).IsNull()); + EXPECT_TRUE(aGraph.Shapes().Original(aSubB).IsNull()); } TEST(BRepGraph_ScenarioMatrix, BoxEdgeSplit_ShapeReconstructsSubEdge) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); BRepGraph_EdgeId anEdgeId; double aSplitParam = 0.0; - const int aNbEdges = aGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId aCand(0); aCand.IsValid(aNbEdges); ++aCand) { const BRepGraphInc::EdgeDef& anEdgeDef = aGraph.Topo().Edges().Definition(aCand); - if (!anEdgeDef.IsDegenerate && anEdgeDef.Curve3DRepId.IsValid()) + if (!BRepGraph_Tool::Edge::Degenerated(aGraph, aCand) && anEdgeDef.Curve3DRepId.IsValid()) { - anEdgeId = aCand; - aSplitParam = 0.5 * (anEdgeDef.ParamFirst + anEdgeDef.ParamLast); + anEdgeId = aCand; + { + const auto _r = BRepGraph_Tool::Edge::Range(aGraph, aCand); + aSplitParam = 0.5 * (_r.first + _r.second); + } break; } } @@ -1415,17 +1386,14 @@ TEST(BRepGraph_ScenarioMatrix, EditorAddedVertex_HasNoOriginal) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_VertexId aFreshVertex = aGraph.Editor().Vertices().Add(gp_Pnt(42.0, 42.0, 42.0), 1.0e-7); ASSERT_TRUE(aFreshVertex.IsValid()); EXPECT_FALSE(aGraph.Shapes().HasOriginal(aFreshVertex)); - EXPECT_EQ(aGraph.Shapes().FindOriginal(aFreshVertex), nullptr); -#ifndef No_Exception - EXPECT_THROW((void)aGraph.Shapes().OriginalOf(aFreshVertex), Standard_ProgramError); -#endif + EXPECT_TRUE(aGraph.Shapes().Original(aFreshVertex).IsNull()); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_SeamRedesign_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_SeamRedesign_Test.cxx index aa405e090b..c66e5d5c3c 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_SeamRedesign_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_SeamRedesign_Test.cxx @@ -12,27 +12,24 @@ // commercial license or contractual agreement. // Edge cases that the seam-redesign reachable only after: -// - Both seam halves placed in WireDef::CoEdgeRefIds at TopoDS_Iterator order +// - Both seam halves placed in WireDef::CoEdgeIds at TopoDS_Iterator order // - SeamPair derived from connectivity (CoEdgesOfEdge filtered by face+orientation) -// - Continuity stored in BRepGraph_LayerRegularity (per (edge, F1, F2) tuple) #include #include #include #include #include -#include +#include +#include #include #include -#include #include #include #include -#include #include #include #include -#include #include #include #include @@ -50,12 +47,14 @@ #include +#include + namespace { void registerLayers(BRepGraph& theGraph) { - theGraph.LayerRegistry().RegisterLayer(new BRepGraph_LayerRegularity()); + (void)theGraph; } //! Find any seam CoEdge on the cylinder lateral face. @@ -80,20 +79,19 @@ BRepGraph_CoEdgeId findSeamCoEdge(const BRepGraph& theGraph) } // namespace // ============================================================ -// Wire CoEdgeRefIds layout: seam halves at TopoDS_Iterator positions +// Wire CoEdgeIds layout: seam halves at TopoDS_Iterator positions // ============================================================ -// Cylinder lateral wire must contain BOTH seam halves in CoEdgeRefIds at the +// Cylinder lateral wire must contain BOTH seam halves in CoEdgeIds at the // same positions TopoDS_Iterator yields them. This guarantees round-trip with // classical TopoDS_Wire iteration. -TEST(BRepGraph_SeamRedesignTest, CylinderLateralWire_BothSeamHalvesInCoEdgeRefIds) +TEST(BRepGraph_SeamRedesignTest, CylinderLateralWire_BothSeamHalvesInCoEdgeIds) { const TopoDS_Shape aCyl = BRepPrimAPI_MakeCylinder(5., 10.).Shape(); BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aCyl).Ok); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_TRUE(aGraph.Shapes().Add(aCyl).IsOk()); const BRepGraph_CoEdgeId aSeamCoEdge = findSeamCoEdge(aGraph); ASSERT_TRUE(aSeamCoEdge.IsValid()) << "Cylinder must have a seam CoEdge"; @@ -102,43 +100,36 @@ TEST(BRepGraph_SeamRedesignTest, CylinderLateralWire_BothSeamHalvesInCoEdgeRefId ASSERT_TRUE(aSeamPair.IsValid()); // Find the wire containing the seam CoEdge. - BRepGraph_WireId aWireId; - for (BRepGraph_WiresOfCoEdge aWIt(aGraph, aGraph.Topo().CoEdges().Wires(aSeamCoEdge)); - aWIt.More(); - aWIt.Next()) - { - aWireId = aWIt.CurrentId(); - break; - } + const BRepGraph_WireId aWireId = aGraph.Topo().CoEdges().Wire(aSeamCoEdge); ASSERT_TRUE(aWireId.IsValid()); - // Walk the wire's CoEdgeRefIds; both seam halves must appear. + // Walk the wire's CoEdgeIds; both seam halves must appear. bool aFwdInWire = false, aRevInWire = false; - for (BRepGraph_RefsCoEdgeOfWire aRefIt(aGraph, aWireId); aRefIt.More(); aRefIt.Next()) + for (BRepGraph_CoEdgesOfWire aRefIt(aGraph, aWireId); aRefIt.More(); aRefIt.Next()) { - const BRepGraphInc::CoEdgeRef& aRef = aGraph.Refs().CoEdges().Entry(aRefIt.CurrentId()); - if (aRef.CoEdgeDefId == aSeamCoEdge) + const BRepGraph_CoEdgeId aCoEdgeId = aRefIt.CurrentId(); + if (aCoEdgeId == aSeamCoEdge) { aFwdInWire = true; } - else if (aRef.CoEdgeDefId == aSeamPair) + else if (aCoEdgeId == aSeamPair) { aRevInWire = true; } } - EXPECT_TRUE(aFwdInWire) << "Forward seam half must appear in the wire's CoEdgeRefIds"; - EXPECT_TRUE(aRevInWire) << "Reversed seam half must appear in the wire's CoEdgeRefIds"; + EXPECT_TRUE(aFwdInWire) << "Forward seam half must appear in the wire's CoEdgeIds"; + EXPECT_TRUE(aRevInWire) << "Reversed seam half must appear in the wire's CoEdgeIds"; } // Cylinder lateral wire's NbCoEdges must equal the count of TopoDS_Iterator(wire) -// yields on the same wire - i.e. the seam edge contributes 2 CoEdgeRefs. +// yields on the same wire - i.e. the seam edge contributes 2 coedge usages. TEST(BRepGraph_SeamRedesignTest, CylinderLateralWire_NbCoEdges_MatchesTopoDSIterator) { const TopoDS_Shape aCyl = BRepPrimAPI_MakeCylinder(5., 10.).Shape(); BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aCyl).Ok); + ASSERT_TRUE(aGraph.Shapes().Add(aCyl).IsOk()); // Locate the cylinder lateral face (the one with > 1 wire-edges and a seam). TopoDS_Face aLateralFace; @@ -147,7 +138,9 @@ TEST(BRepGraph_SeamRedesignTest, CylinderLateralWire_NbCoEdges_MatchesTopoDSIter const TopoDS_Face& aF = TopoDS::Face(aFE.Current()); int aNbEdges = 0; for (TopExp_Explorer aEE(aF, TopAbs_EDGE); aEE.More(); aEE.Next()) + { ++aNbEdges; + } if (aNbEdges == 4) { aLateralFace = aF; @@ -162,7 +155,9 @@ TEST(BRepGraph_SeamRedesignTest, CylinderLateralWire_NbCoEdges_MatchesTopoDSIter { const TopoDS_Shape& aChild = aWIt.Value(); if (aChild.ShapeType() != TopAbs_WIRE) + { continue; + } for (TopoDS_Iterator aEIt(aChild, false, false); aEIt.More(); aEIt.Next()) { ++aClassicalCount; @@ -175,10 +170,14 @@ TEST(BRepGraph_SeamRedesignTest, CylinderLateralWire_NbCoEdges_MatchesTopoDSIter { const BRepGraph_FaceId aFaceId = BRepGraph_Tool::Wire::FaceOf(aGraph, aWIt.CurrentId()); if (!aFaceId.IsValid()) + { continue; - const TopoDS_Shape* aOrig = aGraph.Shapes().FindOriginal(aFaceId); - if (aOrig == nullptr || !aOrig->IsSame(aLateralFace)) + } + const TopoDS_Shape aOrig = aGraph.Shapes().Original(aFaceId); + if (aOrig.IsNull() || !aOrig.IsSame(aLateralFace)) + { continue; + } aGraphCount += BRepGraph_Tool::Wire::NbCoEdges(aGraph, aWIt.CurrentId()); } EXPECT_EQ(aGraphCount, aClassicalCount) @@ -197,14 +196,16 @@ TEST(BRepGraph_SeamRedesignTest, SeamPair_DerivedQuery_IsSymmetric) BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aSph).Ok); + ASSERT_TRUE(aGraph.Shapes().Add(aSph).IsOk()); uint32_t aSeamCount = 0; for (BRepGraph_CoEdgeId aCEId(0); aCEId.IsValid(aGraph.Topo().CoEdges().Nb()); ++aCEId) { const BRepGraph_CoEdgeId aPair = BRepGraph_Tool::CoEdge::SeamPair(aGraph, aCEId); if (!aPair.IsValid()) + { continue; + } ++aSeamCount; const BRepGraph_CoEdgeId aBack = BRepGraph_Tool::CoEdge::SeamPair(aGraph, aPair); EXPECT_EQ(aBack, aCEId) << "SeamPair must be symmetric: SeamPair(SeamPair(c)) == c"; @@ -219,7 +220,7 @@ TEST(BRepGraph_SeamRedesignTest, SeamPair_BoxHasNoSeams) BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aBox).Ok); + ASSERT_TRUE(aGraph.Shapes().Add(aBox).IsOk()); for (BRepGraph_CoEdgeId aCEId(0); aCEId.IsValid(aGraph.Topo().CoEdges().Nb()); ++aCEId) { @@ -228,7 +229,7 @@ TEST(BRepGraph_SeamRedesignTest, SeamPair_BoxHasNoSeams) } } -// Free-wire CoEdge (no FaceDefId) cannot be a seam - derived query handles invalid face. +// Free-wire CoEdge (no FaceId) cannot be a seam - derived query handles invalid face. TEST(BRepGraph_SeamRedesignTest, SeamPair_FreeWireHasNoSeams) { // Build a free wire: an edge between two vertices, not bound to any face. @@ -244,7 +245,7 @@ TEST(BRepGraph_SeamRedesignTest, SeamPair_FreeWireHasNoSeams) BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aMkWire.Wire()).Ok); + ASSERT_TRUE(aGraph.Shapes().Add(aMkWire.Wire()).IsOk()); for (BRepGraph_CoEdgeId aCEId(0); aCEId.IsValid(aGraph.Topo().CoEdges().Nb()); ++aCEId) { @@ -263,25 +264,27 @@ TEST(BRepGraph_SeamRedesignTest, EdgeOps_IsSeamOnFace_DerivedFromConnectivity) BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aCyl).Ok); + ASSERT_TRUE(aGraph.Shapes().Add(aCyl).IsOk()); const BRepGraph_CoEdgeId aSeamCoEdge = findSeamCoEdge(aGraph); ASSERT_TRUE(aSeamCoEdge.IsValid()); const BRepGraphInc::CoEdgeDef& aSeamDef = aGraph.Topo().CoEdges().Definition(aSeamCoEdge); - EXPECT_TRUE(aGraph.Editor().Edges().IsSeamOnFace(aSeamDef.EdgeDefId, aSeamDef.FaceDefId)); + EXPECT_TRUE(BRepGraph_Tool::Edge::IsSeamOnFace(aGraph, aSeamDef.ChildEdgeId, aSeamDef.FaceId)); // A box edge (any) must NOT be a seam on its face. const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(1., 1., 1.).Shape(); BRepGraph aBoxGraph; registerLayers(aBoxGraph); aBoxGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aBoxGraph, aBox).Ok); + ASSERT_TRUE(aBoxGraph.Shapes().Add(aBox).IsOk()); for (BRepGraph_CoEdgeId aCEId(0); aCEId.IsValid(aBoxGraph.Topo().CoEdges().Nb()); ++aCEId) { const BRepGraphInc::CoEdgeDef& aDef = aBoxGraph.Topo().CoEdges().Definition(aCEId); - if (!aDef.FaceDefId.IsValid()) + if (!aDef.FaceId.IsValid()) + { continue; - EXPECT_FALSE(aBoxGraph.Editor().Edges().IsSeamOnFace(aDef.EdgeDefId, aDef.FaceDefId)) + } + EXPECT_FALSE(BRepGraph_Tool::Edge::IsSeamOnFace(aBoxGraph, aDef.ChildEdgeId, aDef.FaceId)) << "Box edge cannot be a seam"; } } @@ -296,7 +299,7 @@ TEST(BRepGraph_SeamRedesignTest, NbDistinctEdges_AccountsForSeamHalves) BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aCyl).Ok); + ASSERT_TRUE(aGraph.Shapes().Add(aCyl).IsOk()); uint32_t aWiresWithSeams = 0; for (BRepGraph_WireIterator aWIt(aGraph); aWIt.More(); aWIt.Next()) @@ -317,122 +320,6 @@ TEST(BRepGraph_SeamRedesignTest, NbDistinctEdges_AccountsForSeamHalves) << "Cylinder must have at least one wire whose seam contributes a doubled edge"; } -// ============================================================ -// EditorView::EdgeOps::SetRegularity (writes through to layer) -// ============================================================ - -TEST(BRepGraph_SeamRedesignTest, EdgeOps_SetRegularity_RoundTripThroughLayer) -{ - const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(1., 1., 1.).Shape(); - BRepGraph aGraph; - registerLayers(aGraph); - aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aBox).Ok); - - // Pick any edge with two faces. - BRepGraph_EdgeId aEdgeId; - BRepGraph_FaceId aFace1, aFace2; - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - NCollection_LinearVector aFaces; - for (BRepGraph_FacesOfEdge aFIt(aGraph, aGraph.Topo().Edges().Faces(anEdgeIt.CurrentId())); - aFIt.More(); - aFIt.Next()) - { - aFaces.Append(aFIt.CurrentId()); - } - if (aFaces.Size() >= 2) - { - aEdgeId = anEdgeIt.CurrentId(); - aFace1 = aFaces[0]; - aFace2 = aFaces[1]; - break; - } - } - ASSERT_TRUE(aEdgeId.IsValid()); - - // Initially C0. - EXPECT_EQ(BRepGraph_Tool::Edge::Continuity(aGraph, aEdgeId, aFace1, aFace2), GeomAbs_C0); - - // Write G2. - EXPECT_TRUE(aGraph.Editor().Edges().SetRegularity(aEdgeId, aFace1, aFace2, GeomAbs_G2)); - - // Read back. - EXPECT_EQ(BRepGraph_Tool::Edge::Continuity(aGraph, aEdgeId, aFace1, aFace2), GeomAbs_G2); - EXPECT_TRUE(BRepGraph_Tool::Edge::HasContinuity(aGraph, aEdgeId, aFace1, aFace2)); - EXPECT_EQ(BRepGraph_Tool::Edge::MaxContinuity(aGraph, aEdgeId), GeomAbs_G2); - - // Symmetry: (F1, F2) == (F2, F1). - EXPECT_EQ(BRepGraph_Tool::Edge::Continuity(aGraph, aEdgeId, aFace2, aFace1), GeomAbs_G2); -} - -TEST(BRepGraph_SeamRedesignTest, EdgeOps_Split_PreservesSeamRegularityLayer) -{ - const TopoDS_Shape aCyl = BRepPrimAPI_MakeCylinder(5., 10.).Shape(); - BRepGraph aGraph; - registerLayers(aGraph); - aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aCyl).Ok); - - const BRepGraph_CoEdgeId aSeamCoEdge = findSeamCoEdge(aGraph); - ASSERT_TRUE(aSeamCoEdge.IsValid()); - const BRepGraphInc::CoEdgeDef& aSeamDef = aGraph.Topo().CoEdges().Definition(aSeamCoEdge); - const BRepGraph_EdgeId aSeamEdgeId = aSeamDef.EdgeDefId; - const BRepGraph_FaceId aSeamFaceId = aSeamDef.FaceDefId; - ASSERT_TRUE(aSeamEdgeId.IsValid()); - ASSERT_TRUE(aSeamFaceId.IsValid()); - - ASSERT_TRUE( - aGraph.Editor().Edges().SetRegularity(aSeamEdgeId, aSeamFaceId, aSeamFaceId, GeomAbs_G2)); - const occ::handle aLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aLayer.IsNull()); - - GeomAbs_Shape aContinuity = GeomAbs_C0; - ASSERT_TRUE(aLayer->FindContinuity(aSeamEdgeId, aSeamFaceId, aSeamFaceId, &aContinuity)); - ASSERT_EQ(aContinuity, GeomAbs_G2); - - const BRepGraphInc::EdgeDef& aEdgeDef = aGraph.Topo().Edges().Definition(aSeamEdgeId); - const double aMidParam = 0.5 * (aEdgeDef.ParamFirst + aEdgeDef.ParamLast); - const BRepGraph_VertexId aSplitVertex = - aGraph.Editor().Vertices().Add(gp_Pnt(5.0, 0.0, 5.0), aEdgeDef.Tolerance); - ASSERT_TRUE(aSplitVertex.IsValid()); - - BRepGraph_EdgeId aSubA; - BRepGraph_EdgeId aSubB; - aGraph.Editor().Edges().Split(aSeamEdgeId, aSplitVertex, aMidParam, aSubA, aSubB); - ASSERT_TRUE(aSubA.IsValid()); - ASSERT_TRUE(aSubB.IsValid()); - - EXPECT_FALSE(aLayer->FindContinuity(aSeamEdgeId, aSeamFaceId, aSeamFaceId)) - << "Removed source edge must not keep stale regularity bindings"; - EXPECT_TRUE(aLayer->FindContinuity(aSubA, aSeamFaceId, aSeamFaceId, &aContinuity)); - EXPECT_EQ(aContinuity, GeomAbs_G2); - EXPECT_TRUE(aLayer->FindContinuity(aSubB, aSeamFaceId, aSeamFaceId, &aContinuity)); - EXPECT_EQ(aContinuity, GeomAbs_G2); - - const BRepGraph_Validate::Result aAudit = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); - EXPECT_TRUE(aAudit.IsValid()) << "Audit must remain clean after seam split"; -} - -// SetRegularity returns false when the layer is not registered. -TEST(BRepGraph_SeamRedesignTest, EdgeOps_SetRegularity_FailsWithoutLayer) -{ - const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(1., 1., 1.).Shape(); - BRepGraph aGraph; - // Note: NOT calling registerLayers - layer absent. - aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aBox).Ok); - - ASSERT_GT(aGraph.Topo().Edges().Nb(), 0u); - ASSERT_GT(aGraph.Topo().Faces().Nb(), 1u); - EXPECT_FALSE(aGraph.Editor().Edges().SetRegularity(BRepGraph_EdgeId::Start(), - BRepGraph_FaceId::Start(), - BRepGraph_FaceId(1), - GeomAbs_G1)); -} - // ============================================================ // Reconstruct: TopoDS_Iterator order preservation // ============================================================ @@ -445,7 +332,7 @@ TEST(BRepGraph_SeamRedesignTest, Reconstruct_CylinderWire_TopoDSIteratorOrder) BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aOriginal).Ok); + ASSERT_TRUE(aGraph.Shapes().Add(aOriginal).IsOk()); TopoDS_Shape aRecon = aGraph.Shapes().Reconstruct(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, 0)); @@ -458,10 +345,14 @@ TEST(BRepGraph_SeamRedesignTest, Reconstruct_CylinderWire_TopoDSIteratorOrder) for (TopoDS_Iterator aWIt(aFE.Current(), false, false); aWIt.More(); aWIt.Next()) { if (aWIt.Value().ShapeType() != TopAbs_WIRE) + { continue; + } int aCount = 0; for (TopoDS_Iterator aEIt(aWIt.Value(), false, false); aEIt.More(); aEIt.Next()) + { ++aCount; + } aOrigCounts.Append(aCount); } } @@ -470,10 +361,14 @@ TEST(BRepGraph_SeamRedesignTest, Reconstruct_CylinderWire_TopoDSIteratorOrder) for (TopoDS_Iterator aWIt(aFE.Current(), false, false); aWIt.More(); aWIt.Next()) { if (aWIt.Value().ShapeType() != TopAbs_WIRE) + { continue; + } int aCount = 0; for (TopoDS_Iterator aEIt(aWIt.Value(), false, false); aEIt.More(); aEIt.Next()) + { ++aCount; + } aReconCounts.Append(aCount); } } @@ -485,43 +380,6 @@ TEST(BRepGraph_SeamRedesignTest, Reconstruct_CylinderWire_TopoDSIteratorOrder) } } -// ============================================================ -// LayerRegularity captures both inter-face and seam continuity -// ============================================================ - -TEST(BRepGraph_SeamRedesignTest, LayerRegularity_CapturesInterFaceAndSeam) -{ - const TopoDS_Shape aCyl = BRepPrimAPI_MakeCylinder(5., 10.).Shape(); - BRepGraph aGraph; - registerLayers(aGraph); - aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aCyl).Ok); - - const occ::handle aLayer = - aGraph.LayerRegistry().FindLayer(); - ASSERT_FALSE(aLayer.IsNull()); - - // Cylinder produces exactly one seam-style entry (F1 == F2, on the lateral face). - uint32_t aSeamEntries = 0; - for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) - { - const BRepGraph_LayerRegularity::EdgeRegularities* aRegs = - aLayer->FindEdgeRegularities(anEdgeIt.CurrentId()); - if (aRegs == nullptr) - continue; - for (const BRepGraph_LayerRegularity::RegularityEntry& aE : aRegs->Entries) - { - if (aE.FaceEntity1 == aE.FaceEntity2) - { - ++aSeamEntries; - EXPECT_GT(aE.Continuity, GeomAbs_C0) - << "Cylinder seam BRep_CurveOnClosedSurface stores G^2"; - } - } - } - EXPECT_EQ(aSeamEntries, 1u) << "Cylinder must produce exactly one seam regularity entry"; -} - // ============================================================ // Validator catches invalid seam orientation // ============================================================ @@ -533,7 +391,7 @@ TEST(BRepGraph_SeamRedesignTest, Validate_DetectsAsymmetricSeamPair) BRepGraph aGraph; registerLayers(aGraph); aGraph.Clear(); - ASSERT_TRUE(BRepGraph_Builder::Add(aGraph, aCyl).Ok); + ASSERT_TRUE(aGraph.Shapes().Add(aCyl).IsOk()); // Locate a CoEdge with a seam pair. const BRepGraph_CoEdgeId aSeamCoEdge = findSeamCoEdge(aGraph); @@ -547,8 +405,7 @@ TEST(BRepGraph_SeamRedesignTest, Validate_DetectsAsymmetricSeamPair) aGraph.Editor().CoEdges().SetOrientation(aSeamMate, aOri); const BRepGraphInc::CoEdgeDef& aDef = aGraph.Topo().CoEdges().Definition(aSeamCoEdge); - EXPECT_FALSE(BRepGraph_Tool::Edge::IsClosedOnFace(aGraph, aDef.EdgeDefId, aDef.FaceDefId)); - EXPECT_FALSE(aGraph.Editor().Edges().IsSeamOnFace(aDef.EdgeDefId, aDef.FaceDefId)); + EXPECT_FALSE(BRepGraph_Tool::Edge::IsSeamOnFace(aGraph, aDef.ChildEdgeId, aDef.FaceId)); const BRepGraph_Validate::Result aResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Builder_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_ShapesViewImport_Test.cxx similarity index 54% rename from src/ModelingData/TKBRep/GTests/BRepGraph_Builder_Test.cxx rename to src/ModelingData/TKBRep/GTests/BRepGraph_ShapesViewImport_Test.cxx index 31b0633c54..311ddd03f9 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Builder_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_ShapesViewImport_Test.cxx @@ -15,18 +15,24 @@ #include #include #include +#include #include +#include #include #include "BRepGraph_RefTestTools.hxx" #include +#include +#include +#include #include #include -#include +#include #include #include #include #include #include +#include #include #include #include @@ -34,15 +40,110 @@ #include #include -#include - #include +namespace +{ +template +static uint32_t countIterator(theIteratorType theIterator) +{ + uint32_t aCount = 0; + for (; theIterator.More(); theIterator.Next()) + { + ++aCount; + } + return aCount; +} + +static bool edgeHasFace(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace) +{ + for (BRepGraph_FacesOfEdge aFaceIt = theGraph.Topo().Edges().FacesOf(theEdge); aFaceIt.More(); + aFaceIt.Next()) + { + if (aFaceIt.CurrentId() == theFace) + { + return true; + } + } + return false; +} + +static uint32_t countSharedEdges(const BRepGraph& theGraph, + const BRepGraph_FaceId theFaceA, + const BRepGraph_FaceId theFaceB) +{ + uint32_t aCount = 0; + for (BRepGraph_RelatedIterator anEdgeIt(theGraph, BRepGraph_NodeId(theFaceA)); anEdgeIt.More(); + anEdgeIt.Next()) + { + if (anEdgeIt.CurrentRelation() == BRepGraph_RelatedIterator::RelationKind::BoundaryEdge + && edgeHasFace(theGraph, BRepGraph_EdgeId::FromNodeId(anEdgeIt.Current()), theFaceB)) + { + ++aCount; + } + } + return aCount; +} + +static uint32_t countAdjacentFaces(const BRepGraph& theGraph, const BRepGraph_FaceId theFace) +{ + uint32_t aCount = 0; + for (BRepGraph_RelatedIterator anIt(theGraph, BRepGraph_NodeId(theFace)); anIt.More(); + anIt.Next()) + { + if (anIt.CurrentRelation() == BRepGraph_RelatedIterator::RelationKind::AdjacentFace) + { + ++aCount; + } + } + return aCount; +} + +static bool containsEdge(const NCollection_LinearVector& theEdges, + const BRepGraph_EdgeId theEdge) +{ + for (const BRepGraph_EdgeId& anEdge : theEdges) + { + if (anEdge == theEdge) + { + return true; + } + } + return false; +} + +static uint32_t countAdjacentEdgesOfEdge(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) +{ + if (!theEdge.IsValid(theGraph.Topo().Edges().Nb()) || theEdge.IsRemoved(theGraph)) + { + return 0; + } + + NCollection_LinearVector anAdjacentEdges; + for (BRepGraph_DefsVertexOfEdge aVertexIt(theGraph, theEdge); aVertexIt.More(); aVertexIt.Next()) + { + for (const BRepGraph_EdgeId& anAdjacentEdgeId : + theGraph.Topo().Vertices().Edges(aVertexIt.CurrentId())) + { + if (anAdjacentEdgeId == theEdge || anAdjacentEdgeId.IsRemoved(theGraph) + || containsEdge(anAdjacentEdges, anAdjacentEdgeId)) + { + continue; + } + anAdjacentEdges.Append(anAdjacentEdgeId); + } + } + return static_cast(anAdjacentEdges.Size()); +} +} // namespace + // ============================================================ // Task 2A: Programmatic Node Addition API // ============================================================ -TEST(BRepGraph_BuilderTest, AddVertex_ReturnsValidId) +TEST(BRepGraph_ShapesViewImportTest, AddVertex_ReturnsValidId) { BRepGraph aGraph; BRepGraph_VertexId aVtxId = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 0.001); @@ -57,7 +158,7 @@ TEST(BRepGraph_BuilderTest, AddVertex_ReturnsValidId) EXPECT_NEAR(aVtxDef.Tolerance, 0.001, 1e-10); } -TEST(BRepGraph_BuilderTest, AddEdge_WithCurve) +TEST(BRepGraph_ShapesViewImportTest, AddEdge_WithCurve) { BRepGraph aGraph; BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 0.001); @@ -70,15 +171,24 @@ TEST(BRepGraph_BuilderTest, AddEdge_WithCurve) const BRepGraphInc::EdgeDef& anEdgeDef = aGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()); - EXPECT_EQ(BRepGraph_Tool::Edge::StartVertexRef(aGraph, BRepGraph_EdgeId::Start()).VertexDefId, + EXPECT_EQ(aGraph.Refs() + .Vertices() + .Entry(BRepGraph_Tool::Edge::StartVertexId(aGraph, BRepGraph_EdgeId::Start())) + .ChildVertexId, aV1); - EXPECT_EQ(BRepGraph_Tool::Edge::EndVertexRef(aGraph, BRepGraph_EdgeId::Start()).VertexDefId, aV2); + EXPECT_EQ(aGraph.Refs() + .Vertices() + .Entry(BRepGraph_Tool::Edge::EndVertexId(aGraph, BRepGraph_EdgeId::Start())) + .ChildVertexId, + aV2); EXPECT_TRUE(anEdgeDef.Curve3DRepId.IsValid()); - EXPECT_NEAR(anEdgeDef.ParamFirst, 0.0, 1e-10); - EXPECT_NEAR(anEdgeDef.ParamLast, 10.0, 1e-10); + const std::pair anEdgeRange = + BRepGraph_Tool::Edge::Range(aGraph, BRepGraph_EdgeId::Start()); + EXPECT_NEAR(anEdgeRange.first, 0.0, 1e-10); + EXPECT_NEAR(anEdgeRange.second, 10.0, 1e-10); } -TEST(BRepGraph_BuilderTest, AddWire_ClosedRectangle) +TEST(BRepGraph_ShapesViewImportTest, AddWire_ClosedRectangle) { BRepGraph aGraph; BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 0.001); @@ -96,22 +206,20 @@ TEST(BRepGraph_BuilderTest, AddWire_ClosedRectangle) BRepGraph_EdgeId aE2 = aGraph.Editor().Edges().Add(aV2, aV3, aL2, 0.0, 10.0, 0.001); BRepGraph_EdgeId aE3 = aGraph.Editor().Edges().Add(aV3, aV0, aL3, 0.0, 10.0, 0.001); - NCollection_DynamicArray> aEdges; - aEdges.Append({aE0, TopAbs_FORWARD}); - aEdges.Append({aE1, TopAbs_FORWARD}); - aEdges.Append({aE2, TopAbs_FORWARD}); - aEdges.Append({aE3, TopAbs_FORWARD}); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE0, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE1, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE2, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE3, TopAbs_FORWARD)); - BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aEdges); + BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); EXPECT_TRUE(aWireId.IsValid()); - EXPECT_EQ(BRepGraph_TestTools::CountCoEdgeRefsOfWire(aGraph, BRepGraph_WireId::Start()), 4); - const BRepGraphInc::WireDef& aWireDef = - aGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()); - EXPECT_TRUE(aWireDef.IsClosed); + EXPECT_EQ(BRepGraph_TestTools::CountCoEdgesOfWire(aGraph, BRepGraph_WireId::Start()), 4); + EXPECT_TRUE(BRepGraph_Tool::Wire::IsClosed(aGraph, BRepGraph_WireId::Start())); } -TEST(BRepGraph_BuilderTest, AddFace_WithSurface) +TEST(BRepGraph_ShapesViewImportTest, AddFace_WithSurface) { BRepGraph aGraph; @@ -131,17 +239,18 @@ TEST(BRepGraph_BuilderTest, AddFace_WithSurface) BRepGraph_EdgeId aE2 = aGraph.Editor().Edges().Add(aV2, aV3, aL2, 0.0, 10.0, 0.001); BRepGraph_EdgeId aE3 = aGraph.Editor().Edges().Add(aV3, aV0, aL3, 0.0, 10.0, 0.001); - NCollection_DynamicArray> aEdges; - aEdges.Append({aE0, TopAbs_FORWARD}); - aEdges.Append({aE1, TopAbs_FORWARD}); - aEdges.Append({aE2, TopAbs_FORWARD}); - aEdges.Append({aE3, TopAbs_FORWARD}); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE0, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE1, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE2, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE3, TopAbs_FORWARD)); - BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aEdges); + BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); occ::handle aPlane = new Geom_Plane(gp_Pln()); - NCollection_DynamicArray aInnerWires; - BRepGraph_FaceId aFaceId = aGraph.Editor().Faces().Add(aPlane, aWireId, aInnerWires, 0.001); + NCollection_LinearVector aInnerWires; + BRepGraph_FaceId aFaceId = + aGraph.Editor().Faces().Add(aPlane, aWireId, aInnerWires.ToArray1(), 0.001); EXPECT_TRUE(aFaceId.IsValid()); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 1); @@ -152,7 +261,60 @@ TEST(BRepGraph_BuilderTest, AddFace_WithSurface) EXPECT_TRUE(aFaceDef.SurfaceRepId.IsValid()); } -TEST(BRepGraph_BuilderTest, AddEdge_InvalidVertex_ReturnsInvalidAndDoesNotAppend) +TEST(BRepGraph_ShapesViewImportTest, AddWireToFace_AppendsInnerWireRefAndRelations) +{ + BRepGraph aGraph; + + BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 0.001); + BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 0.001); + BRepGraph_VertexId aV2 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 10, 0), 0.001); + BRepGraph_VertexId aV3 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 10, 0), 0.001); + + occ::handle aL0 = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)); + occ::handle aL1 = new Geom_Line(gp_Pnt(10, 0, 0), gp_Dir(0, 1, 0)); + occ::handle aL2 = new Geom_Line(gp_Pnt(10, 10, 0), gp_Dir(-1, 0, 0)); + occ::handle aL3 = new Geom_Line(gp_Pnt(0, 10, 0), gp_Dir(0, -1, 0)); + + BRepGraph_EdgeId aE0 = aGraph.Editor().Edges().Add(aV0, aV1, aL0, 0.0, 10.0, 0.001); + BRepGraph_EdgeId aE1 = aGraph.Editor().Edges().Add(aV1, aV2, aL1, 0.0, 10.0, 0.001); + BRepGraph_EdgeId aE2 = aGraph.Editor().Edges().Add(aV2, aV3, aL2, 0.0, 10.0, 0.001); + BRepGraph_EdgeId aE3 = aGraph.Editor().Edges().Add(aV3, aV0, aL3, 0.0, 10.0, 0.001); + + NCollection_LinearVector aOuterCoEdges; + aOuterCoEdges.Append(aGraph.Editor().CoEdges().Add(aE0, TopAbs_FORWARD)); + aOuterCoEdges.Append(aGraph.Editor().CoEdges().Add(aE1, TopAbs_FORWARD)); + aOuterCoEdges.Append(aGraph.Editor().CoEdges().Add(aE2, TopAbs_FORWARD)); + aOuterCoEdges.Append(aGraph.Editor().CoEdges().Add(aE3, TopAbs_FORWARD)); + + NCollection_LinearVector aInnerCoEdges; + aInnerCoEdges.Append(aGraph.Editor().CoEdges().Add(aE0, TopAbs_FORWARD)); + aInnerCoEdges.Append(aGraph.Editor().CoEdges().Add(aE1, TopAbs_FORWARD)); + aInnerCoEdges.Append(aGraph.Editor().CoEdges().Add(aE2, TopAbs_FORWARD)); + aInnerCoEdges.Append(aGraph.Editor().CoEdges().Add(aE3, TopAbs_FORWARD)); + + const BRepGraph_WireId aOuterWire = aGraph.Editor().Wires().Add(aOuterCoEdges.ToArray1()); + const BRepGraph_WireId anInnerWire = aGraph.Editor().Wires().Add(aInnerCoEdges.ToArray1()); + + occ::handle aPlane = new Geom_Plane(gp_Pln()); + NCollection_LinearVector anInnerWires; + const BRepGraph_FaceId aFaceId = + aGraph.Editor().Faces().Add(aPlane, aOuterWire, anInnerWires.ToArray1(), 0.001); + + const BRepGraph_WireRefId aWireRef = + aGraph.Editor().Faces().Append(aFaceId, anInnerWire, TopAbs_FORWARD); + ASSERT_TRUE(aWireRef.IsValid()); + + const BRepGraphInc::FaceRelations& aFaceRelations = aGraph.Topo().Faces().Relations(aFaceId); + EXPECT_EQ(aFaceRelations.WireRefIds.Size(), 2); + EXPECT_EQ(aFaceRelations.WireRefIds.Value(1), aWireRef); + + const BRepGraphInc::WireRef& aRef = aGraph.Refs().Wires().Entry(aWireRef); + EXPECT_EQ(aRef.ChildWireId, anInnerWire); + + EXPECT_EQ(aGraph.Topo().Wires().Relations(anInnerWire).ParentWireRefIds.Size(), 1); +} + +TEST(BRepGraph_ShapesViewImportTest, AddEdge_InvalidVertex_ReturnsInvalidAndDoesNotAppend) { BRepGraph aGraph; BRepGraph_VertexId aVertexId = aGraph.Editor().Vertices().Add(gp_Pnt(0.0, 0.0, 0.0), 0.001); @@ -168,33 +330,33 @@ TEST(BRepGraph_BuilderTest, AddEdge_InvalidVertex_ReturnsInvalidAndDoesNotAppend EXPECT_EQ(aGraph.Topo().Edges().Nb(), 0); } -TEST(BRepGraph_BuilderTest, AddWire_InvalidEdge_ReturnsInvalidAndDoesNotAppend) +TEST(BRepGraph_ShapesViewImportTest, AddWire_InvalidEdge_ReturnsInvalidAndDoesNotAppend) { BRepGraph aGraph; - NCollection_DynamicArray> anEdges; - anEdges.Append({BRepGraph_EdgeId(17), TopAbs_FORWARD}); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(BRepGraph_EdgeId(17), TopAbs_FORWARD)); - const BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(anEdges); + const BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); EXPECT_FALSE(aWireId.IsValid()); EXPECT_EQ(aGraph.Topo().Wires().Nb(), 0); EXPECT_EQ(aGraph.Topo().CoEdges().Nb(), 0); } -TEST(BRepGraph_BuilderTest, AddFace_InvalidOuterWire_ReturnsInvalidAndDoesNotAppend) +TEST(BRepGraph_ShapesViewImportTest, AddFace_InvalidOuterWire_ReturnsInvalidAndDoesNotAppend) { BRepGraph aGraph; occ::handle aPlane = new Geom_Plane(gp_Pln()); - NCollection_DynamicArray anInnerWires; + NCollection_LinearVector anInnerWires; const BRepGraph_FaceId aFaceId = - aGraph.Editor().Faces().Add(aPlane, BRepGraph_WireId(9), anInnerWires, 0.001); + aGraph.Editor().Faces().Add(aPlane, BRepGraph_WireId(9), anInnerWires.ToArray1(), 0.001); EXPECT_FALSE(aFaceId.IsValid()); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 0); } -TEST(BRepGraph_BuilderTest, AddShellAndSolid) +TEST(BRepGraph_ShapesViewImportTest, AddShellAndSolid) { BRepGraph aGraph; BRepGraph_ShellId aShellId = aGraph.Editor().Shells().Add(); @@ -208,7 +370,7 @@ TEST(BRepGraph_BuilderTest, AddShellAndSolid) // Incremental Build (flattened Add) // ============================================================ -TEST(BRepGraph_BuilderTest, AppendTwoBoxFaces) +TEST(BRepGraph_ShapesViewImportTest, AppendTwoBoxFaces) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); @@ -225,25 +387,21 @@ TEST(BRepGraph_BuilderTest, AppendTwoBoxFaces) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aCopy1.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + aGraph.Shapes().Add(aCopy1.Shape()); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 1); // Append second face. - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, - aCopy2.Shape(), - BRepGraph_Builder::Options{{}, false, true, false}); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + aGraph.Shapes().Add(aCopy2.Shape(), BRepGraph::ShapesView::Options{{}, false, true, false}); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 2); - EXPECT_TRUE(aGraph.IsDone()); } // ============================================================ // Task 2C: Soft Node Removal // ============================================================ -TEST(BRepGraph_BuilderTest, RemoveVertex_IsRemoved) +TEST(BRepGraph_ShapesViewImportTest, RemoveVertex_IsRemoved) { BRepGraph aGraph; BRepGraph_VertexId aVtxId = aGraph.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 0.001); @@ -253,16 +411,14 @@ TEST(BRepGraph_BuilderTest, RemoveVertex_IsRemoved) EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(aVtxId)); } -TEST(BRepGraph_BuilderTest, RemoveFaceFromBox) +TEST(BRepGraph_ShapesViewImportTest, RemoveFaceFromBox) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aBox); ASSERT_EQ(aGraph.Topo().Faces().Nb(), 6); BRepGraph_FaceId aFaceId(0); @@ -272,22 +428,24 @@ TEST(BRepGraph_BuilderTest, RemoveFaceFromBox) EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(aFaceId)); // Other faces should not be removed. - const int aNbFaces = aGraph.Topo().Faces().Nb(); + const uint32_t aNbFaces = aGraph.Topo().Faces().Nb(); for (BRepGraph_FaceId anOtherFaceId(1); anOtherFaceId.IsValid(aNbFaces); ++anOtherFaceId) { EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(anOtherFaceId)); } } -TEST(BRepGraph_BuilderTest, RemoveInvalidNode_NoError) +TEST(BRepGraph_ShapesViewImportTest, RemoveInvalidNode_NoError) { BRepGraph aGraph; BRepGraph_NodeId anInvalidId; - EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(anInvalidId)); + EXPECT_FALSE(aGraph.Topo().Gen().IsValid(anInvalidId)); + EXPECT_FALSE(aGraph.Topo().Gen().IsActive(anInvalidId)); + EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(anInvalidId)); aGraph.Editor().Gen().RemoveNode(anInvalidId); // Should not crash. } -TEST(BRepGraph_BuilderTest, RemoveAlreadyRemovedNode_NoError) +TEST(BRepGraph_ShapesViewImportTest, RemoveAlreadyRemovedNode_NoError) { BRepGraph aGraph; @@ -305,7 +463,7 @@ TEST(BRepGraph_BuilderTest, RemoveAlreadyRemovedNode_NoError) // Item 1: Complete Construction API (Shell/Solid linking) // ============================================================ -TEST(BRepGraph_BuilderTest, AddFace_CreatesUsage) +TEST(BRepGraph_ShapesViewImportTest, AddFace_CreatesUsage) { BRepGraph aGraph; @@ -325,105 +483,113 @@ TEST(BRepGraph_BuilderTest, AddFace_CreatesUsage) BRepGraph_EdgeId aE2 = aGraph.Editor().Edges().Add(aV2, aV3, aL2, 0.0, 10.0, 0.001); BRepGraph_EdgeId aE3 = aGraph.Editor().Edges().Add(aV3, aV0, aL3, 0.0, 10.0, 0.001); - NCollection_DynamicArray> aEdges; - aEdges.Append({aE0, TopAbs_FORWARD}); - aEdges.Append({aE1, TopAbs_FORWARD}); - aEdges.Append({aE2, TopAbs_FORWARD}); - aEdges.Append({aE3, TopAbs_FORWARD}); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE0, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE1, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE2, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE3, TopAbs_FORWARD)); - BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aEdges); - occ::handle aPlane = new Geom_Plane(gp_Pln()); - NCollection_DynamicArray aInnerWires; - BRepGraph_FaceId aFaceId = aGraph.Editor().Faces().Add(aPlane, aWireId, aInnerWires, 0.001); + BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + occ::handle aPlane = new Geom_Plane(gp_Pln()); + NCollection_LinearVector aInnerWires; + BRepGraph_FaceId aFaceId = + aGraph.Editor().Faces().Add(aPlane, aWireId, aInnerWires.ToArray1(), 0.001); // Create shell and link face to it. BRepGraph_ShellId aShellId = aGraph.Editor().Shells().Add(); - const BRepGraph_FaceRefId aFaceRefId = aGraph.Editor().Shells().AddFace(aShellId, aFaceId); + const BRepGraph_FaceRefId aFaceRefId = aGraph.Editor().Shells().Append(aShellId, aFaceId); EXPECT_TRUE(aFaceRefId.IsValid()); EXPECT_EQ(BRepGraph_TestTools::CountFaceRefsOfShell(aGraph, BRepGraph_ShellId::Start()), 1); } -TEST(BRepGraph_BuilderTest, AddFace_InvalidNodes_NoMutation) +TEST(BRepGraph_ShapesViewImportTest, AddFace_InvalidNodes_NoMutation) { BRepGraph aGraph; const BRepGraph_ShellId aShellId = aGraph.Editor().Shells().Add(); const BRepGraph_FaceRefId aFaceRefId = - aGraph.Editor().Shells().AddFace(aShellId, BRepGraph_FaceId(4)); + aGraph.Editor().Shells().Append(aShellId, BRepGraph_FaceId(4)); EXPECT_FALSE(aFaceRefId.IsValid()); EXPECT_EQ(BRepGraph_TestTools::CountFaceRefsOfShell(aGraph, aShellId), 0); } -TEST(BRepGraph_BuilderTest, AddShell_CreatesUsage) +TEST(BRepGraph_ShapesViewImportTest, AddShell_CreatesUsage) { BRepGraph aGraph; BRepGraph_ShellId aShellId = aGraph.Editor().Shells().Add(); BRepGraph_SolidId aSolidId = aGraph.Editor().Solids().Add(); - const BRepGraph_ShellRefId aShellRefId = aGraph.Editor().Solids().AddShell(aSolidId, aShellId); + const BRepGraph_ShellRefId aShellRefId = aGraph.Editor().Solids().Append(aSolidId, aShellId); EXPECT_TRUE(aShellRefId.IsValid()); EXPECT_EQ(BRepGraph_TestTools::CountShellRefsOfSolid(aGraph, BRepGraph_SolidId::Start()), 1); } -TEST(BRepGraph_BuilderTest, MutInvalidTopologyDefs_ThrowProgramError) +TEST(BRepGraph_ShapesViewImportTest, MutInvalidTopologyDefs_ThrowProgramError) { BRepGraph aGraph; #if !defined(No_Exception) - EXPECT_THROW((void)aGraph.Editor().Vertices().Mut(BRepGraph_VertexId(7)), Standard_ProgramError); - EXPECT_THROW((void)aGraph.Editor().Edges().Mut(BRepGraph_EdgeId(7)), Standard_ProgramError); - EXPECT_THROW((void)aGraph.Editor().Wires().Mut(BRepGraph_WireId(7)), Standard_ProgramError); - EXPECT_THROW((void)aGraph.Editor().Faces().Mut(BRepGraph_FaceId(7)), Standard_ProgramError); - EXPECT_THROW((void)aGraph.Editor().Shells().Mut(BRepGraph_ShellId(7)), Standard_ProgramError); - EXPECT_THROW((void)aGraph.Editor().Solids().Mut(BRepGraph_SolidId(7)), Standard_ProgramError); - EXPECT_THROW((void)aGraph.Editor().CoEdges().Mut(BRepGraph_CoEdgeId(7)), Standard_ProgramError); + EXPECT_THROW(std::ignore = aGraph.Editor().Vertices().Mut(BRepGraph_VertexId(7)), + Standard_ProgramError); + EXPECT_THROW(std::ignore = aGraph.Editor().Edges().Mut(BRepGraph_EdgeId(7)), + Standard_ProgramError); + EXPECT_THROW(std::ignore = aGraph.Editor().Wires().Mut(BRepGraph_WireId(7)), + Standard_ProgramError); + EXPECT_THROW(std::ignore = aGraph.Editor().Faces().Mut(BRepGraph_FaceId(7)), + Standard_ProgramError); + EXPECT_THROW(std::ignore = aGraph.Editor().Shells().Mut(BRepGraph_ShellId(7)), + Standard_ProgramError); + EXPECT_THROW(std::ignore = aGraph.Editor().Solids().Mut(BRepGraph_SolidId(7)), + Standard_ProgramError); + EXPECT_THROW(std::ignore = aGraph.Editor().CoEdges().Mut(BRepGraph_CoEdgeId(7)), + Standard_ProgramError); #endif } -TEST(BRepGraph_BuilderTest, AddCompound_WithChildren) +TEST(BRepGraph_ShapesViewImportTest, AddCompound_WithChildren) { BRepGraph aGraph; BRepGraph_SolidId aSolid1 = aGraph.Editor().Solids().Add(); BRepGraph_SolidId aSolid2 = aGraph.Editor().Solids().Add(); - NCollection_DynamicArray aChildren; + NCollection_LinearVector aChildren; aChildren.Append(aSolid1); aChildren.Append(aSolid2); - BRepGraph_CompoundId aCompId = aGraph.Editor().Compounds().Add(aChildren); + BRepGraph_CompoundId aCompId = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); EXPECT_TRUE(aCompId.IsValid()); EXPECT_EQ(aGraph.Topo().Compounds().Nb(), 1); EXPECT_EQ(BRepGraph_TestTools::CountChildRefsOfParent(aGraph, BRepGraph_CompoundId::Start()), 2); } -TEST(BRepGraph_BuilderTest, AddCompSolid_WithSolids) +TEST(BRepGraph_ShapesViewImportTest, AddCompSolid_WithSolids) { BRepGraph aGraph; BRepGraph_SolidId aSolid1 = aGraph.Editor().Solids().Add(); BRepGraph_SolidId aSolid2 = aGraph.Editor().Solids().Add(); - NCollection_DynamicArray aSolids; + NCollection_LinearVector aSolids; aSolids.Append(aSolid1); aSolids.Append(aSolid2); - BRepGraph_CompSolidId aCSolId = aGraph.Editor().CompSolids().Add(aSolids); + BRepGraph_CompSolidId aCSolId = aGraph.Editor().CompSolids().Add(aSolids.ToArray1()); EXPECT_TRUE(aCSolId.IsValid()); EXPECT_EQ(aGraph.Topo().CompSolids().Nb(), 1); const BRepGraphInc::CompSolidDef& aCSolDef = aGraph.Topo().CompSolids().Definition(BRepGraph_CompSolidId::Start()); - (void)aCSolDef; + std::ignore = aCSolDef; EXPECT_EQ(BRepGraph_TestTools::CountSolidRefsOfCompSolid(aGraph, BRepGraph_CompSolidId::Start()), 2); } -TEST(BRepGraph_BuilderTest, FullSolid_ProgrammaticConstruction) +TEST(BRepGraph_ShapesViewImportTest, FullSolid_ProgrammaticConstruction) { BRepGraph aGraph; @@ -443,22 +609,23 @@ TEST(BRepGraph_BuilderTest, FullSolid_ProgrammaticConstruction) BRepGraph_EdgeId aE2 = aGraph.Editor().Edges().Add(aV2, aV3, aL2, 0.0, 10.0, 0.001); BRepGraph_EdgeId aE3 = aGraph.Editor().Edges().Add(aV3, aV0, aL3, 0.0, 10.0, 0.001); - NCollection_DynamicArray> aEdges; - aEdges.Append({aE0, TopAbs_FORWARD}); - aEdges.Append({aE1, TopAbs_FORWARD}); - aEdges.Append({aE2, TopAbs_FORWARD}); - aEdges.Append({aE3, TopAbs_FORWARD}); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE0, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE1, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE2, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE3, TopAbs_FORWARD)); - BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aEdges); - occ::handle aPlane = new Geom_Plane(gp_Pln()); - NCollection_DynamicArray aInnerWires; - BRepGraph_FaceId aFaceId = aGraph.Editor().Faces().Add(aPlane, aWireId, aInnerWires, 0.001); + BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + occ::handle aPlane = new Geom_Plane(gp_Pln()); + NCollection_LinearVector aInnerWires; + BRepGraph_FaceId aFaceId = + aGraph.Editor().Faces().Add(aPlane, aWireId, aInnerWires.ToArray1(), 0.001); BRepGraph_ShellId aShellId = aGraph.Editor().Shells().Add(); - aGraph.Editor().Shells().AddFace(aShellId, aFaceId); + aGraph.Editor().Shells().Append(aShellId, aFaceId); BRepGraph_SolidId aSolidId = aGraph.Editor().Solids().Add(); - aGraph.Editor().Solids().AddShell(aSolidId, aShellId); + aGraph.Editor().Solids().Append(aSolidId, aShellId); // Verify the hierarchy. EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1); @@ -472,16 +639,14 @@ TEST(BRepGraph_BuilderTest, FullSolid_ProgrammaticConstruction) // Item 2: Field-Level Mutation via Editor() for All Def Types // ============================================================ -TEST(BRepGraph_BuilderTest, MutableFaceDefinition_ChangesTolerance) +TEST(BRepGraph_ShapesViewImportTest, MutableFaceDefinition_ChangesTolerance) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aBox); ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); const double anOrigTol = BRepGraph_Tool::Face::Tolerance(aGraph, BRepGraph_FaceId::Start()); @@ -492,19 +657,17 @@ TEST(BRepGraph_BuilderTest, MutableFaceDefinition_ChangesTolerance) } EXPECT_NEAR(BRepGraph_Tool::Face::Tolerance(aGraph, BRepGraph_FaceId::Start()), 0.5, 1e-10); EXPECT_GT(aGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).OwnGen, 0u); - (void)anOrigTol; + std::ignore = anOrigTol; } -TEST(BRepGraph_BuilderTest, MutableShellDefinition) +TEST(BRepGraph_ShapesViewImportTest, MutableShellDefinition) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aBox); ASSERT_GT(aGraph.Topo().Shells().Nb(), 0); { @@ -515,16 +678,14 @@ TEST(BRepGraph_BuilderTest, MutableShellDefinition) EXPECT_GT(aGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).OwnGen, 0u); } -TEST(BRepGraph_BuilderTest, MutableSolidDefinition) +TEST(BRepGraph_ShapesViewImportTest, MutableSolidDefinition) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = aGraph.Shapes().Add(aBox); ASSERT_GT(aGraph.Topo().Solids().Nb(), 0); { @@ -535,11 +696,11 @@ TEST(BRepGraph_BuilderTest, MutableSolidDefinition) EXPECT_GT(aGraph.Topo().Solids().Definition(BRepGraph_SolidId::Start()).OwnGen, 0u); } -TEST(BRepGraph_BuilderTest, MutableCompoundDefinition) +TEST(BRepGraph_ShapesViewImportTest, MutableCompoundDefinition) { BRepGraph aGraph; - NCollection_DynamicArray aChildren; - (void)aGraph.Editor().Compounds().Add(aChildren); + NCollection_LinearVector aChildren; + std::ignore = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); ASSERT_EQ(aGraph.Topo().Compounds().Nb(), 1); { @@ -550,11 +711,11 @@ TEST(BRepGraph_BuilderTest, MutableCompoundDefinition) EXPECT_GT(aGraph.Topo().Compounds().Definition(BRepGraph_CompoundId::Start()).OwnGen, 0u); } -TEST(BRepGraph_BuilderTest, MutableCompSolidDefinition) +TEST(BRepGraph_ShapesViewImportTest, MutableCompSolidDefinition) { BRepGraph aGraph; - NCollection_DynamicArray aSolids; - (void)aGraph.Editor().CompSolids().Add(aSolids); + NCollection_LinearVector aSolids; + std::ignore = aGraph.Editor().CompSolids().Add(aSolids.ToArray1()); ASSERT_EQ(aGraph.Topo().CompSolids().Nb(), 1); { @@ -569,72 +730,65 @@ TEST(BRepGraph_BuilderTest, MutableCompSolidDefinition) // Item 3: Definition Traversal Skips Removed Nodes // ============================================================ -TEST(BRepGraph_BuilderTest, SkipsRemovedFaces) +TEST(BRepGraph_ShapesViewImportTest, SkipsRemovedFaces) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = aGraph.Shapes().Add(aBox); ASSERT_EQ(aGraph.Topo().Faces().Nb(), 6); // Remove 2 faces. aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Face, 0)); aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Face, 3)); - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More(); aFaceIt.Next()) { - const BRepGraphInc::FaceDef& aFaceDef = aFaceIt.Current(); - EXPECT_FALSE(aFaceDef.IsRemoved); + EXPECT_FALSE(aFaceIt.CurrentId().IsRemoved(aGraph)); ++aCount; } EXPECT_EQ(aCount, 4); } -TEST(BRepGraph_BuilderTest, SkipsRemovedEdges) +TEST(BRepGraph_ShapesViewImportTest, SkipsRemovedEdges) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); - const int aNbEdges = aGraph.Topo().Edges().Nb(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = aGraph.Shapes().Add(aBox); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); ASSERT_GT(aNbEdges, 0); aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Edge, 0)); - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_EdgeIterator anEdgeIt(aGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraphInc::EdgeDef& anEdgeDef = anEdgeIt.Current(); - EXPECT_FALSE(anEdgeDef.IsRemoved); + EXPECT_FALSE(anEdgeIt.CurrentId().IsRemoved(aGraph)); ++aCount; } EXPECT_EQ(aCount, aNbEdges - 1); } -TEST(BRepGraph_BuilderTest, SkipsFirstNode) +TEST(BRepGraph_ShapesViewImportTest, SkipsFirstNode) { BRepGraph aGraph; - (void)aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 0.001); - (void)aGraph.Editor().Vertices().Add(gp_Pnt(1, 0, 0), 0.001); - (void)aGraph.Editor().Vertices().Add(gp_Pnt(2, 0, 0), 0.001); + std::ignore = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 0.001); + std::ignore = aGraph.Editor().Vertices().Add(gp_Pnt(1, 0, 0), 0.001); + std::ignore = aGraph.Editor().Vertices().Add(gp_Pnt(2, 0, 0), 0.001); // Remove the first vertex. aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Vertex, 0)); - int aCount = 0; + uint32_t aCount = 0; for (BRepGraph_VertexIterator aVertexIt(aGraph); aVertexIt.More(); aVertexIt.Next()) { - const BRepGraphInc::VertexDef& aVertexDef = aVertexIt.Current(); - EXPECT_FALSE(aVertexDef.IsRemoved); + EXPECT_FALSE(aVertexIt.CurrentId().IsRemoved(aGraph)); ++aCount; } EXPECT_EQ(aCount, 2); @@ -644,7 +798,7 @@ TEST(BRepGraph_BuilderTest, SkipsFirstNode) // Item 4: Cascading Soft Removal // ============================================================ -TEST(BRepGraph_BuilderTest, RemoveFace_RemovesWiresAndEdges) +TEST(BRepGraph_ShapesViewImportTest, RemoveFace_RemovesWiresAndEdges) { BRepGraph aGraph; @@ -663,16 +817,17 @@ TEST(BRepGraph_BuilderTest, RemoveFace_RemovesWiresAndEdges) BRepGraph_EdgeId aE2 = aGraph.Editor().Edges().Add(aV2, aV3, aL2, 0.0, 10.0, 0.001); BRepGraph_EdgeId aE3 = aGraph.Editor().Edges().Add(aV3, aV0, aL3, 0.0, 10.0, 0.001); - NCollection_DynamicArray> aEdges; - aEdges.Append({aE0, TopAbs_FORWARD}); - aEdges.Append({aE1, TopAbs_FORWARD}); - aEdges.Append({aE2, TopAbs_FORWARD}); - aEdges.Append({aE3, TopAbs_FORWARD}); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE0, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE1, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE2, TopAbs_FORWARD)); + aCoEdges.Append(aGraph.Editor().CoEdges().Add(aE3, TopAbs_FORWARD)); - BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aEdges); - occ::handle aPlane = new Geom_Plane(gp_Pln()); - NCollection_DynamicArray aInnerWires; - BRepGraph_FaceId aFaceId = aGraph.Editor().Faces().Add(aPlane, aWireId, aInnerWires, 0.001); + BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + occ::handle aPlane = new Geom_Plane(gp_Pln()); + NCollection_LinearVector aInnerWires; + BRepGraph_FaceId aFaceId = + aGraph.Editor().Faces().Add(aPlane, aWireId, aInnerWires.ToArray1(), 0.001); // Remove the face subgraph. aGraph.Editor().Gen().RemoveSubgraph(aFaceId); @@ -689,16 +844,14 @@ TEST(BRepGraph_BuilderTest, RemoveFace_RemovesWiresAndEdges) EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(aV3)); } -TEST(BRepGraph_BuilderTest, RemoveSolid_CascadesToFaces) +TEST(BRepGraph_ShapesViewImportTest, RemoveSolid_CascadesToFaces) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = aGraph.Shapes().Add(aBox); BRepGraph_SolidId aSolidId(0); aGraph.Editor().Gen().RemoveSubgraph(aSolidId); @@ -706,21 +859,21 @@ TEST(BRepGraph_BuilderTest, RemoveSolid_CascadesToFaces) EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(aSolidId)); // All shells should be removed. - const int aNbShells = aGraph.Topo().Shells().Nb(); + const uint32_t aNbShells = aGraph.Topo().Shells().Nb(); for (BRepGraph_ShellId aShellId(0); aShellId.IsValid(aNbShells); ++aShellId) { EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(aShellId)); } // All faces should be removed. - const int aNbFaces = aGraph.Topo().Faces().Nb(); + const uint32_t aNbFaces = aGraph.Topo().Faces().Nb(); for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) { EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(aFaceId)); } } -TEST(BRepGraph_BuilderTest, RemoveSubgraph_SharedFace_PreservesSharedEdgesAndVertices) +TEST(BRepGraph_ShapesViewImportTest, RemoveSubgraph_SharedFace_PreservesSharedEdgesAndVertices) { // Build a box (6 faces). Remove one face. Shared edges and vertices must // remain active because other faces still reference them. @@ -729,13 +882,11 @@ TEST(BRepGraph_BuilderTest, RemoveSubgraph_SharedFace_PreservesSharedEdgesAndVer BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = aGraph.Shapes().Add(aBox); - const int aNbFaces = aGraph.Topo().Faces().Nb(); - const int aNbEdges = aGraph.Topo().Edges().Nb(); - const int aNbVertices = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbFaces = aGraph.Topo().Faces().Nb(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); + const uint32_t aNbVertices = aGraph.Topo().Vertices().Nb(); ASSERT_EQ(aNbFaces, 6); ASSERT_EQ(aNbEdges, 12); ASSERT_EQ(aNbVertices, 8); @@ -748,7 +899,7 @@ TEST(BRepGraph_BuilderTest, RemoveSubgraph_SharedFace_PreservesSharedEdgesAndVer EXPECT_TRUE(aGraph.Topo().Gen().IsRemoved(aRemovedFace)); // Other faces must remain active. - for (int aIdx = 1; aIdx < aNbFaces; ++aIdx) + for (uint32_t aIdx = 1; aIdx < aNbFaces; ++aIdx) { EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(BRepGraph_FaceId(aIdx))); } @@ -756,14 +907,14 @@ TEST(BRepGraph_BuilderTest, RemoveSubgraph_SharedFace_PreservesSharedEdgesAndVer // All 12 edges of a box are shared by exactly 2 faces. // Removing 1 face leaves each shared edge with at least 1 active parent face. // Therefore all edges must remain active. - for (int aIdx = 0; aIdx < aNbEdges; ++aIdx) + for (uint32_t aIdx = 0; aIdx < aNbEdges; ++aIdx) { EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(BRepGraph_EdgeId(aIdx))); } // All 8 vertices of a box are shared by 3 edges. All edges are still active, // so all vertices must remain active. - for (int aIdx = 0; aIdx < aNbVertices; ++aIdx) + for (uint32_t aIdx = 0; aIdx < aNbVertices; ++aIdx) { EXPECT_FALSE(aGraph.Topo().Gen().IsRemoved(BRepGraph_VertexId(aIdx))); } @@ -773,49 +924,42 @@ TEST(BRepGraph_BuilderTest, RemoveSubgraph_SharedFace_PreservesSharedEdgesAndVer // Item 5: Edge Adjacency Queries // ============================================================ -TEST(BRepGraph_BuilderTest, FacesOfEdge_BoxSharedEdge) +TEST(BRepGraph_ShapesViewImportTest, FacesOfEdge_BoxSharedEdge) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = aGraph.Shapes().Add(aBox); // Every edge in a box is shared by exactly 2 faces. - const int aNbEdges = aGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - NCollection_DynamicArray aFaces = aGraph.Topo().Edges().Faces(anEdgeId); - EXPECT_EQ(aFaces.Length(), 2) << "Edge " << anEdgeId.Index << " has " << aFaces.Length() - << " faces"; + const uint32_t aNbFaces = countIterator(aGraph.Topo().Edges().FacesOf(anEdgeId)); + EXPECT_EQ(aNbFaces, 2) << "Edge " << anEdgeId.Index << " has " << aNbFaces << " faces"; } } -TEST(BRepGraph_BuilderTest, SharedEdges_AdjacentBoxFaces) +TEST(BRepGraph_ShapesViewImportTest, SharedEdges_AdjacentBoxFaces) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aBox); ASSERT_EQ(aGraph.Topo().Faces().Nb(), 6); // Count total shared edge pairs across all face pairs. - int aSharingPairs = 0; - const int aNbFaces = aGraph.Topo().Faces().Nb(); + int aSharingPairs = 0; + const uint32_t aNbFaces = aGraph.Topo().Faces().Nb(); for (BRepGraph_FaceId aFaceA(0); aFaceA.IsValid(aNbFaces); ++aFaceA) { for (BRepGraph_FaceId aFaceB(aFaceA.Index + 1); aFaceB.IsValid(aNbFaces); ++aFaceB) { - NCollection_DynamicArray aShared = - aGraph.Topo().Faces().SharedEdges(aFaceA, aFaceB, aGraph.Allocator()); - if (!aShared.IsEmpty()) + if (countSharedEdges(aGraph, aFaceA, aFaceB) > 0) { ++aSharingPairs; } @@ -825,30 +969,27 @@ TEST(BRepGraph_BuilderTest, SharedEdges_AdjacentBoxFaces) EXPECT_EQ(aSharingPairs, 12); } -TEST(BRepGraph_BuilderTest, AdjacentFaces_BoxFace) +TEST(BRepGraph_ShapesViewImportTest, AdjacentFaces_BoxFace) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = aGraph.Shapes().Add(aBox); ASSERT_EQ(aGraph.Topo().Faces().Nb(), 6); // Each face of a box is adjacent to 4 other faces. - const int aNbFaces = aGraph.Topo().Faces().Nb(); + const uint32_t aNbFaces = aGraph.Topo().Faces().Nb(); for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) { - NCollection_DynamicArray aAdj = - aGraph.Topo().Faces().Adjacent(aFaceId, aGraph.Allocator()); - EXPECT_EQ(aAdj.Length(), 4) << "Face " << aFaceId.Index << " has " << aAdj.Length() - << " adjacent faces"; + const uint32_t aNbAdjacentFaces = countAdjacentFaces(aGraph, aFaceId); + EXPECT_EQ(aNbAdjacentFaces, 4) + << "Face " << aFaceId.Index << " has " << aNbAdjacentFaces << " adjacent faces"; } } -TEST(BRepGraph_BuilderTest, FacesOfEdge_NoFaces_Programmatic) +TEST(BRepGraph_ShapesViewImportTest, FacesOfEdge_NoFaces_Programmatic) { BRepGraph aGraph; BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 0.001); @@ -858,22 +999,20 @@ TEST(BRepGraph_BuilderTest, FacesOfEdge_NoFaces_Programmatic) BRepGraph_EdgeId anEdgeId = aGraph.Editor().Edges().Add(aV0, aV1, aLine, 0.0, 10.0, 0.001); // Edge not in any face => empty result. - const NCollection_DynamicArray& aFaces = aGraph.Topo().Edges().Faces(anEdgeId); - EXPECT_EQ(aFaces.Length(), 0); + EXPECT_EQ(countIterator(aGraph.Topo().Edges().FacesOf(anEdgeId)), 0); } // ============ Topology adjacency methods ============ -TEST(BRepGraph_BuilderTest, EdgesOfFace_Box_HasEdges) +TEST(BRepGraph_ShapesViewImportTest, EdgesOfFace_Box_HasEdges) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); // Each box face has 4 edges (rectangular loop). - int aNbEdges = 0; + uint32_t aNbEdges = 0; for (BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_FaceId::Start(), BRepGraph_NodeId::Kind::Edge); @@ -885,15 +1024,14 @@ TEST(BRepGraph_BuilderTest, EdgesOfFace_Box_HasEdges) EXPECT_EQ(aNbEdges, 4); } -TEST(BRepGraph_BuilderTest, VerticesOfEdge_Box_HasTwoVertices) +TEST(BRepGraph_ShapesViewImportTest, VerticesOfEdge_Box_HasTwoVertices) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - int aNbVertices = 0; + uint32_t aNbVertices = 0; for (BRepGraph_ChildExplorer anExp(aGraph, BRepGraph_EdgeId::Start(), BRepGraph_NodeId::Kind::Vertex); @@ -905,68 +1043,90 @@ TEST(BRepGraph_BuilderTest, VerticesOfEdge_Box_HasTwoVertices) EXPECT_EQ(aNbVertices, 2); } -TEST(BRepGraph_BuilderTest, EdgesOfVertex_Box_ThreeEdges) +TEST(BRepGraph_ShapesViewImportTest, EdgesOfVertex_Box_ThreeEdges) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); // Each box corner vertex is shared by 3 edges. - const NCollection_DynamicArray& aEdges = + const NCollection_LinearVector& aEdges = aGraph.Topo().Vertices().Edges(BRepGraph_VertexId::Start()); - EXPECT_EQ(aEdges.Length(), 3); + EXPECT_EQ(aEdges.Size(), 3); } -TEST(BRepGraph_BuilderTest, AdjacentEdges_Box_SharedVertex) +TEST(BRepGraph_ShapesViewImportTest, AdjacentEdges_Box_SharedVertex) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); // Box edge shares 2 vertices, each with 3 incident edges. // Adjacent = (3 - 1) + (3 - 1) - overlap = at least 4 adjacent edges. - NCollection_DynamicArray aAdj = - aGraph.Topo().Edges().Adjacent(BRepGraph_EdgeId::Start(), aGraph.Allocator()); - EXPECT_GE(aAdj.Length(), 4); + EXPECT_GE(countAdjacentEdgesOfEdge(aGraph, BRepGraph_EdgeId::Start()), 4); } -TEST(BRepGraph_BuilderTest, NbFacesOfEdge_Box_TwoFaces) +TEST(BRepGraph_ShapesViewImportTest, NbFacesOfEdge_Box_TwoFaces) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); // Every box edge is shared by exactly 2 faces (manifold). EXPECT_EQ(aGraph.Topo().Edges().NbFaces(BRepGraph_EdgeId::Start()), 2); } -TEST(BRepGraph_BuilderTest, IsManifoldEdge_Box_True) +TEST(BRepGraph_ShapesViewImportTest, IsManifoldEdge_Box_True) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes19 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes19 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - EXPECT_TRUE(aGraph.Topo().Edges().IsManifold(BRepGraph_EdgeId::Start())); - EXPECT_FALSE(aGraph.Topo().Edges().IsBoundary(BRepGraph_EdgeId::Start())); + EXPECT_TRUE(BRepGraph_Tool::Edge::IsManifold(aGraph, BRepGraph_EdgeId::Start())); + EXPECT_FALSE(BRepGraph_Tool::Edge::IsBoundary(aGraph, BRepGraph_EdgeId::Start())); } -TEST(BRepGraph_BuilderTest, InvalidInput_ReturnsEmpty) +TEST(BRepGraph_ShapesViewImportTest, InvalidInput_ReturnsEmpty) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes20 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10, 20, 30).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes20 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10, 20, 30).Shape()); // Out-of-range typed ids return empty results. - EXPECT_EQ(aGraph.Topo().Vertices().Edges(BRepGraph_VertexId(999)).Length(), 0); - EXPECT_EQ(aGraph.Topo().Edges().Adjacent(BRepGraph_EdgeId(999), aGraph.Allocator()).Length(), 0); + EXPECT_EQ(aGraph.Topo().Vertices().Edges(BRepGraph_VertexId(999)).Size(), 0); + EXPECT_EQ(countAdjacentEdgesOfEdge(aGraph, BRepGraph_EdgeId(999)), 0); +} + +TEST(BRepGraph_ShapesViewImportTest, AddWithHistory_PreservesRequestedAddedNodes) +{ + BRepGraph aGraph; + aGraph.LayerRegistry().Ensure()->SetEnabled(false); + + BRepGraph::ShapesView::Options anOptions; + anOptions.TrackAddedNodes = true; + + NCollection_DataMap anInputs; + const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10, 20, 30).Shape(); + const BRepGraph::ShapesView::Result aResult = + aGraph.Shapes().AddWithHistory(aBox, + anInputs, + occ::handle(), + TCollection_AsciiString("Test:DisabledHistory"), + anOptions); + + ASSERT_TRUE(aResult.IsOk()); + EXPECT_GT(aResult.AddedNodes.Extent(), 0); + + NCollection_LinearVector aRoots; + aRoots.Append(aResult.TopologyRoot); + NCollection_DataMap aInputsFromRoots; + aGraph.Shapes().CollectHistoryInputs(aRoots.ToArray1(), aInputsFromRoots); + EXPECT_GT(aInputsFromRoots.Extent(), 0); + + EXPECT_EQ(aGraph.LayerRegistry().Ensure()->NbRecords(), 0); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Sharing_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Sharing_Test.cxx index e0e2972fde..c1661ec8f2 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Sharing_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Sharing_Test.cxx @@ -18,7 +18,8 @@ #include "BRepGraph_RefTestTools.hxx" #include #include -#include +#include +#include #include #include #include @@ -41,8 +42,7 @@ protected: BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBox); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = myGraph.Shapes().Add(aBox); } BRepGraph myGraph; @@ -54,7 +54,7 @@ protected: TEST_F(BRepGraph_SharingTest, EdgeDef_EachSharedByTwoFaces) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); EXPECT_EQ(myGraph.Topo().Edges().Nb(), 12); // In a box, each edge is shared by exactly 2 faces. for (BRepGraph_EdgeIterator anEdgeIt(myGraph); anEdgeIt.More(); anEdgeIt.Next()) @@ -68,7 +68,7 @@ TEST_F(BRepGraph_SharingTest, EdgeDef_EachSharedByTwoFaces) TEST_F(BRepGraph_SharingTest, FaceDef_EachHasValidSurface) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); EXPECT_EQ(myGraph.Topo().Faces().Nb(), 6); for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -81,14 +81,14 @@ TEST_F(BRepGraph_SharingTest, FaceDef_EachHasValidSurface) TEST_F(BRepGraph_SharingTest, SolidDef_HasOneShellRef) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); EXPECT_EQ(myGraph.Topo().Solids().Nb(), 1); EXPECT_EQ(BRepGraph_TestTools::CountShellRefsOfSolid(myGraph, BRepGraph_SolidId::Start()), 1); } TEST_F(BRepGraph_SharingTest, ShellDef_HasSixFaceRefs) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); EXPECT_EQ(myGraph.Topo().Shells().Nb(), 1); EXPECT_EQ(BRepGraph_TestTools::CountFaceRefsOfShell(myGraph, BRepGraph_ShellId::Start()), 6); } @@ -99,21 +99,21 @@ TEST_F(BRepGraph_SharingTest, ShellDef_HasSixFaceRefs) TEST_F(BRepGraph_SharingTest, SolidDef_ContainsOneShellRef) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); EXPECT_EQ(myGraph.Topo().Solids().Nb(), 1); EXPECT_EQ(BRepGraph_TestTools::CountShellRefsOfSolid(myGraph, BRepGraph_SolidId::Start()), 1); } TEST_F(BRepGraph_SharingTest, ShellDef_ContainsSixFaceRefs) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); EXPECT_EQ(myGraph.Topo().Shells().Nb(), 1); EXPECT_EQ(BRepGraph_TestTools::CountFaceRefsOfShell(myGraph, BRepGraph_ShellId::Start()), 6); } TEST_F(BRepGraph_SharingTest, FaceDef_OuterWireIdx_Valid) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); @@ -122,29 +122,28 @@ TEST_F(BRepGraph_SharingTest, FaceDef_OuterWireIdx_Valid) } } -TEST_F(BRepGraph_SharingTest, WireDef_CoEdgeRefsCount_FourPerBoxFace) +TEST_F(BRepGraph_SharingTest, WireDef_CoEdgesCount_FourPerBoxFace) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); for (BRepGraph_WireIterator aWireIt(myGraph); aWireIt.More(); aWireIt.Next()) { - const BRepGraph_WireId aWireId = aWireIt.CurrentId(); - const int aNbCoEdgeRefs = BRepGraph_TestTools::CountCoEdgeRefsOfWire(myGraph, aWireId); - EXPECT_GT(aNbCoEdgeRefs, 0) << "Wire def " << aWireId.Index << " has no coedge refs"; + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); + const size_t aNbCoEdges = BRepGraph_TestTools::CountCoEdgesOfWire(myGraph, aWireId); + EXPECT_GT(aNbCoEdges, 0) << "Wire def " << aWireId.Index << " has no coedges"; // Box face wires have 4 edges - EXPECT_EQ(aNbCoEdgeRefs, 4) << "Wire def " << aWireId.Index - << " expected 4 coedge refs for box face"; + EXPECT_EQ(aNbCoEdges, 4) << "Wire def " << aWireId.Index << " expected 4 coedges for box face"; } } TEST_F(BRepGraph_SharingTest, EdgeDef_VertexDefs_BothValid) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(myGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - EXPECT_TRUE(BRepGraph_Tool::Edge::StartVertexRef(myGraph, anEdgeId).VertexDefId.IsValid()) + EXPECT_TRUE(BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdgeId).IsValid()) << "Edge def " << anEdgeId.Index << " has invalid start vertex def"; - EXPECT_TRUE(BRepGraph_Tool::Edge::EndVertexRef(myGraph, anEdgeId).VertexDefId.IsValid()) + EXPECT_TRUE(BRepGraph_Tool::Edge::EndVertexId(myGraph, anEdgeId).IsValid()) << "Edge def " << anEdgeId.Index << " has invalid end vertex def"; } } @@ -155,7 +154,7 @@ TEST_F(BRepGraph_SharingTest, EdgeDef_VertexDefs_BothValid) TEST_F(BRepGraph_SharingTest, SharedEdge_IncidenceRefs_DifferentOrientation) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); // In a box, shared edges between adjacent faces have coedges on // different face definitions. Check that at least some edges have // coedges referencing more than one face. @@ -163,18 +162,18 @@ TEST_F(BRepGraph_SharingTest, SharedEdge_IncidenceRefs_DifferentOrientation) for (BRepGraph_EdgeIterator anEdgeIt(myGraph); anEdgeIt.More(); anEdgeIt.Next()) { const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const NCollection_DynamicArray& aCoEdgeIdxs = + const NCollection_LinearVector& aCoEdgeIdxs = myGraph.Topo().Edges().CoEdges(anEdgeId); - if (aCoEdgeIdxs.Length() < 2) + if (aCoEdgeIdxs.Size() < 2) { continue; } // Check if coedges reference different faces. const BRepGraph_NodeId aFace0 = - myGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(0)).FaceDefId; - for (int aCEI = 1; aCEI < aCoEdgeIdxs.Length(); ++aCEI) + myGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(0)).FaceId; + for (size_t aCEI = 1; aCEI < aCoEdgeIdxs.Size(); ++aCEI) { - if (myGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(aCEI)).FaceDefId != aFace0) + if (myGraph.Topo().CoEdges().Definition(aCoEdgeIdxs.Value(aCEI)).FaceId != aFace0) { ++aMultiFaceEdgeCount; break; @@ -187,20 +186,17 @@ TEST_F(BRepGraph_SharingTest, SharedEdge_IncidenceRefs_DifferentOrientation) TEST_F(BRepGraph_SharingTest, NonClosedEdge_StartEnd_Different) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); for (BRepGraph_EdgeIterator anEdgeIt(myGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const BRepGraphInc::EdgeDef& aDef = anEdgeIt.Current(); - if (aDef.IsDegenerate) + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + if (BRepGraph_Tool::Edge::Degenerated(myGraph, anEdgeId)) { continue; } // Box edges are not closed, so start and end vertex defs must differ - const BRepGraph_VertexId aStartVtx = - BRepGraph_Tool::Edge::StartVertexRef(myGraph, anEdgeId).VertexDefId; - const BRepGraph_VertexId anEndVtx = - BRepGraph_Tool::Edge::EndVertexRef(myGraph, anEdgeId).VertexDefId; + const BRepGraph_VertexRefId aStartVtx = BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdgeId); + const BRepGraph_VertexRefId anEndVtx = BRepGraph_Tool::Edge::EndVertexId(myGraph, anEdgeId); EXPECT_NE(aStartVtx, anEndVtx) << "Non-degenerate edge def " << anEdgeId.Index << " has identical start and end vertex def ids"; } @@ -208,7 +204,7 @@ TEST_F(BRepGraph_SharingTest, NonClosedEdge_StartEnd_Different) TEST_F(BRepGraph_SharingTest, VertexDef_Points_MatchExpectedBoxCorners) { - ASSERT_TRUE(myGraph.IsDone()); + ASSERT_FALSE(myGraph.IsEmpty()); // For a simple 10x20x30 box, all 8 vertex points should be valid. EXPECT_EQ(myGraph.Topo().Vertices().Nb(), 8); for (BRepGraph_VertexIterator aVertexIt(myGraph); aVertexIt.More(); aVertexIt.Next()) @@ -241,9 +237,8 @@ TEST_F(BRepGraph_SharingTest, CompoundTwoIdenticalBoxes) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); // Same TShape added twice to compound: definition is shared (1 solid def), // compound has 2 ChildRefs pointing to the same solid index. @@ -255,11 +250,56 @@ TEST_F(BRepGraph_SharingTest, CompoundTwoIdenticalBoxes) EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8); // Compound has 2 child references to the same solid. - const NCollection_DynamicArray aChildRefs = + const NCollection_LinearVector& aChildRefs = BRepGraph_TestTools::ChildRefsOfParent(aGraph, BRepGraph_CompoundId::Start()); - ASSERT_EQ(aChildRefs.Length(), 2); - EXPECT_EQ(aGraph.Refs().Children().Entry(aChildRefs.Value(0)).ChildDefId.Index, - aGraph.Refs().Children().Entry(aChildRefs.Value(1)).ChildDefId.Index); + ASSERT_EQ(aChildRefs.Size(), 2); + EXPECT_NE(aChildRefs.Value(0), aChildRefs.Value(1)) + << "Repeated child occurrences in one parent must keep separate slots"; + EXPECT_EQ(aGraph.Refs().Children().Entry(aChildRefs.Value(0)).ChildNodeId.Index, + aGraph.Refs().Children().Entry(aChildRefs.Value(1)).ChildNodeId.Index); +} + +TEST_F(BRepGraph_SharingTest, + SameReferenceRepresentationAcrossDifferentParents_UsesDistinctChildRefs) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRep_Builder aBuilder; + TopoDS_Compound aParentA; + TopoDS_Compound aParentB; + TopoDS_Compound aRoot; + aBuilder.MakeCompound(aParentA); + aBuilder.MakeCompound(aParentB); + aBuilder.MakeCompound(aRoot); + aBuilder.Add(aParentA, aBox); + aBuilder.Add(aParentB, aBox); + aBuilder.Add(aRoot, aParentA); + aBuilder.Add(aRoot, aParentB); + + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aRoot); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_NodeId aParentANode = aGraph.Shapes().FindNode(aParentA); + const BRepGraph_NodeId aParentBNode = aGraph.Shapes().FindNode(aParentB); + ASSERT_EQ(aParentANode.NodeKind, BRepGraph_NodeId::Kind::Compound); + ASSERT_EQ(aParentBNode.NodeKind, BRepGraph_NodeId::Kind::Compound); + + const NCollection_LinearVector& aParentARefs = + BRepGraph_TestTools::ChildRefsOfParent(aGraph, BRepGraph_CompoundId(aParentANode)); + const NCollection_LinearVector& aParentBRefs = + BRepGraph_TestTools::ChildRefsOfParent(aGraph, BRepGraph_CompoundId(aParentBNode)); + ASSERT_EQ(aParentARefs.Size(), 1); + ASSERT_EQ(aParentBRefs.Size(), 1); + EXPECT_NE(aParentARefs.Value(0), aParentBRefs.Value(0)) + << "Each parent usage must have its own child-reference entry"; + EXPECT_EQ(aGraph.Refs().Children().Entry(aParentARefs.Value(0)).ChildNodeId, + aGraph.Refs().Children().Entry(aParentBRefs.Value(0)).ChildNodeId); + + const BRepGraph_Validate::Result anAuditResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_EQ(anAuditResult.NbIssues(BRepGraph_Validate::Severity::Error), 0); } TEST_F(BRepGraph_SharingTest, CompoundTwoDistinctBoxes) @@ -278,9 +318,8 @@ TEST_F(BRepGraph_SharingTest, CompoundTwoDistinctBoxes) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); // Two different TShapes: no sharing, definitions are independent EXPECT_EQ(aGraph.Topo().Solids().Nb(), 2); @@ -306,14 +345,14 @@ TEST_F(BRepGraph_SharingTest, CompoundWithLocation_MoreUsagesThanDefs) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); - // Same TShape with different locations: defs are shared. - EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); - EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12); - EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8); + // Same TShape with different locations: normal topology definitions are split + // because the location is baked into the definitions during Populate. + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 12); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 24); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 16); } TEST_F(BRepGraph_SharingTest, TranslatedCopy_SameTShape_SharedDefs) @@ -333,16 +372,13 @@ TEST_F(BRepGraph_SharingTest, TranslatedCopy_SameTShape_SharedDefs) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); - // Moved() preserves TShape, so all definitions are shared (1 solid def). - // Compound has 2 ChildRefs with different locations. - EXPECT_EQ(aGraph.Topo().Solids().Nb(), 1); + // Moved() preserves TShape, but locations are baked into topology definitions. + EXPECT_EQ(aGraph.Topo().Solids().Nb(), 2); - // Face/edge/vertex defs are shared (same TShape). - EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); - EXPECT_EQ(aGraph.Topo().Edges().Nb(), 12); - EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 8); + EXPECT_EQ(aGraph.Topo().Faces().Nb(), 12); + EXPECT_EQ(aGraph.Topo().Edges().Nb(), 24); + EXPECT_EQ(aGraph.Topo().Vertices().Nb(), 16); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_SparseModel_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_SparseModel_Test.cxx new file mode 100644 index 0000000000..4ed78ec3a4 --- /dev/null +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_SparseModel_Test.cxx @@ -0,0 +1,754 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +// ======================================================================= +// Group 1 - Compact on sparse models (no removed nodes -> no-op or valid pass) +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_BareVertices_NoOp) +{ + BRepGraph aGraph; + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7).IsValid()); + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(1, 0, 0), 1.e-7).IsValid()); + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(2, 0, 0), 1.e-7).IsValid()); + + const uint32_t aVtxBefore = aGraph.Topo().Vertices().NbActive(); + ASSERT_EQ(aVtxBefore, 3); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbNodesBefore, aRes.NbNodesAfter); + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 3); + const BRepGraph_Validate::Result aValidateResult = BRepGraph_Validate::Perform(aGraph); + EXPECT_TRUE(aValidateResult.IsValid()) + << (aValidateResult.Issues.IsEmpty() ? "" + : aValidateResult.Issues.First().Description.ToCString()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_FreeEdgeNoCurve_NoOp) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + ASSERT_TRUE(aV0.IsValid() && aV1.IsValid()); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + + ASSERT_EQ(aGraph.Topo().Vertices().NbActive(), 2); + ASSERT_EQ(aGraph.Topo().Edges().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbNodesBefore, aRes.NbNodesAfter); + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 2); + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 1); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_FreeWireSingleCoEdge_NoOp) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + ASSERT_TRUE(aV0.IsValid() && aV1.IsValid()); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + ASSERT_TRUE(aGraph.Editor().Wires().Add(aCoEdges.ToArray1()).IsValid()); + + ASSERT_EQ(aGraph.Topo().Vertices().NbActive(), 2); + ASSERT_EQ(aGraph.Topo().Edges().NbActive(), 1); + ASSERT_EQ(aGraph.Topo().Wires().NbActive(), 1); + ASSERT_GE(aGraph.Topo().CoEdges().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbNodesBefore, aRes.NbNodesAfter); + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 2); + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 1); + EXPECT_EQ(aGraph.Topo().Wires().NbActive(), 1); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_EmptyCompound_NoOp) +{ + BRepGraph aGraph; + NCollection_LinearVector anEmpty; + ASSERT_TRUE(aGraph.Editor().Compounds().Add(anEmpty.ToArray1()).IsValid()); + + ASSERT_EQ(aGraph.Topo().Compounds().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbNodesBefore, aRes.NbNodesAfter); + EXPECT_EQ(aGraph.Topo().Compounds().NbActive(), 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_EmptyShell_NoOp) +{ + BRepGraph aGraph; + ASSERT_TRUE(aGraph.Editor().Shells().Add().IsValid()); + + ASSERT_EQ(aGraph.Topo().Shells().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbNodesBefore, aRes.NbNodesAfter); + EXPECT_EQ(aGraph.Topo().Shells().NbActive(), 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_EmptySolid_NoOp) +{ + BRepGraph aGraph; + ASSERT_TRUE(aGraph.Editor().Solids().Add().IsValid()); + + ASSERT_EQ(aGraph.Topo().Solids().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbNodesBefore, aRes.NbNodesAfter); + EXPECT_EQ(aGraph.Topo().Solids().NbActive(), 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_WirelessSurfacelessFace_NoOp) +{ + BRepGraph aGraph; + NCollection_LinearVector anEmpty; + ASSERT_TRUE(aGraph.Editor() + .Faces() + .Add(occ::handle(), BRepGraph_WireId(), anEmpty.ToArray1(), 1.e-7) + .IsValid()); + + ASSERT_EQ(aGraph.Topo().Faces().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbNodesBefore, aRes.NbNodesAfter); + EXPECT_EQ(aGraph.Topo().Faces().NbActive(), 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= +// Group 2 - Compact after RemoveNode on sparse models +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_RemoveVertexFromFreeEdge_RemapsEdgeRefs) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + ASSERT_TRUE(aV0.IsValid() && aV1.IsValid()); + const BRepGraph_EdgeId aRvEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + ASSERT_TRUE(aRvEdge.IsValid()); + + ASSERT_EQ(aGraph.Topo().Vertices().NbActive(), 2); + ASSERT_EQ(aGraph.Topo().Edges().NbActive(), 1); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aV0)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 1); + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 1); + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 1); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_RemoveEdgeFromFreeWire_RetiresCoEdgeAndPrunesRef) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + ASSERT_TRUE(anEdge.IsValid()); + + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + ASSERT_TRUE(aGraph.Editor().Wires().Add(aCoEdges.ToArray1()).IsValid()); + + const uint32_t aCoEdgesBefore = aGraph.Topo().CoEdges().NbActive(); + ASSERT_GE(aCoEdgesBefore, 1); + ASSERT_EQ(aGraph.Topo().Wires().NbActive(), 1); + ASSERT_EQ(aGraph.Topo().Edges().NbActive(), 1); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(anEdge)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().CoEdges().NbActive(), aCoEdgesBefore - 1) + << "Phase 4a should retire orphaned CoEdge"; + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aGraph.Topo().CoEdges().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Wires().NbActive(), 1); + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 2); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_RemoveFace_FreeWireCoEdgesSurvive) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); + ASSERT_TRUE(aFace.IsValid()); + + const uint32_t aCoEdgesBefore = aGraph.Topo().CoEdges().NbActive(); + const uint32_t aWiresBefore = aGraph.Topo().Wires().NbActive(); + ASSERT_GE(aCoEdgesBefore, 1); + ASSERT_EQ(aWiresBefore, 1); + ASSERT_EQ(aGraph.Topo().Faces().NbActive(), 1); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFace)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Faces().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().CoEdges().NbActive(), aCoEdgesBefore) + << "Free-wire CoEdges must survive face removal (FaceId cleared, ChildEdgeId valid)"; + EXPECT_EQ(aGraph.Topo().Wires().NbActive(), aWiresBefore) << "Wire must survive face removal"; + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aGraph.Topo().Faces().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().CoEdges().NbActive(), aCoEdgesBefore); + EXPECT_EQ(aGraph.Topo().Wires().NbActive(), aWiresBefore); + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 1); + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 2); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_RemoveTopologyChild_OccurrenceRetired) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + aGraph.Editor().Shells().Append(aShell, aFace); + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid, aShell); + + const BRepGraph_ProductId aProduct = aGraph.Editor().Products().Add(BRepGraph_NodeId(aSolid)); + ASSERT_TRUE(aProduct.IsValid()); + aGraph.Editor().Products().AppendDocumentRoot(aProduct); + + ASSERT_EQ(aGraph.Topo().Products().NbActive(), 1); + ASSERT_EQ(aGraph.Topo().Occurrences().NbActive(), 1); + ASSERT_EQ(aGraph.Topo().Solids().NbActive(), 1); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aSolid)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Solids().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Occurrences().NbActive(), 0) + << "Phase 10 should retire occurrence whose ChildNodeId was removed"; + EXPECT_EQ(aGraph.Topo().Products().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aGraph.Topo().Solids().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Occurrences().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Products().NbActive(), 1); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Compact_MultipleRemovals_VertexEdgeFace_Validates) +{ + BRepGraph aGraph; + + const BRepGraph_VertexId aExtraVtx = aGraph.Editor().Vertices().Add(gp_Pnt(100, 0, 0), 1.e-7); + ASSERT_TRUE(aExtraVtx.IsValid()); + + const BRepGraph_VertexId aV0f = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1f = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId aFreeEdge = + aGraph.Editor().Edges().Add(aV0f, aV1f, occ::handle(), 0.0, 10.0, 1.e-7); + + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 1, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 1, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); + ASSERT_TRUE(aFace.IsValid()); + ASSERT_TRUE(aFreeEdge.IsValid()); + + const uint32_t aVtxBefore = aGraph.Topo().Vertices().NbActive(); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aExtraVtx)); + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFreeEdge)); + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFace)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), aVtxBefore - 1); + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 1); + EXPECT_EQ(aGraph.Topo().Faces().NbActive(), 0); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); +} + +// ======================================================================= +// Group 3 - Deduplicate on sparse models +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Dedup_BareVertices_NoRewrite) +{ + BRepGraph aGraph; + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7).IsValid()); + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(1, 0, 0), 1.e-7).IsValid()); + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(2, 0, 0), 1.e-7).IsValid()); + + const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); + EXPECT_EQ(aRes.NbCanonicalSurfaces, 0); + EXPECT_EQ(aRes.NbCanonicalCurves, 0); + EXPECT_EQ(aRes.NbSurfaceRewrites, 0); + EXPECT_EQ(aRes.NbCurveRewrites, 0); + EXPECT_FALSE(aRes.IsEntityMergeApplied); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Dedup_TwoIdenticalVertices_MergeWhenSafe) +{ + BRepGraph aGraph; + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7).IsValid()); + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7).IsValid()); + + ASSERT_EQ(aGraph.Topo().Vertices().NbActive(), 2); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + EXPECT_GE(aRes.NbMergedVertices, 1); + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Dedup_TwoIdenticalVertices_NoMergeByDefault) +{ + BRepGraph aGraph; + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7).IsValid()); + ASSERT_TRUE(aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7).IsValid()); + + ASSERT_EQ(aGraph.Topo().Vertices().NbActive(), 2); + + const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); + EXPECT_EQ(aRes.NbMergedVertices, 0); + EXPECT_FALSE(aRes.IsEntityMergeApplied); + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 2); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Dedup_FreeEdgesWithCurve_CanonicalizesCurves) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_VertexId aV0b = aGraph.Editor().Vertices().Add(gp_Pnt(0, 1, 0), 1.e-7); + const BRepGraph_VertexId aV1b = aGraph.Editor().Vertices().Add(gp_Pnt(10, 1, 0), 1.e-7); + const occ::handle aLine1 = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)); + const occ::handle aLine2 = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)); + ASSERT_NE(aLine1.get(), aLine2.get()); + + ASSERT_TRUE(aGraph.Editor().Edges().Add(aV0, aV1, aLine1, 0.0, 10.0, 1.e-7).IsValid()); + ASSERT_TRUE(aGraph.Editor().Edges().Add(aV0b, aV1b, aLine2, 0.0, 10.0, 1.e-7).IsValid()); + + const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); + EXPECT_EQ(aRes.NbCanonicalCurves, 1); + EXPECT_EQ(aRes.NbCurveRewrites, 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Dedup_FreeWireSingleCoEdge_NoGeometryRewrite) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + ASSERT_TRUE(aGraph.Editor().Wires().Add(aCoEdges.ToArray1()).IsValid()); + + const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); + EXPECT_EQ(aRes.NbCanonicalSurfaces, 0); + EXPECT_EQ(aRes.NbCanonicalCurves, 1); + EXPECT_EQ(aRes.NbCurveRewrites, 0); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Dedup_EmptyCompound_NoOp) +{ + BRepGraph aGraph; + NCollection_LinearVector anEmpty; + ASSERT_TRUE(aGraph.Editor().Compounds().Add(anEmpty.ToArray1()).IsValid()); + + const BRepGraph_Deduplicate::Result aRes = BRepGraph_Deduplicate::Perform(aGraph); + EXPECT_EQ(aRes.NbCanonicalSurfaces, 0); + EXPECT_EQ(aRes.NbCanonicalCurves, 0); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph).IsValid()); +} + +// ======================================================================= +// Group 4 - CleanupRemovedReferences edge cases +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Cleanup_RemoveFace_CoEdgeFaceIdCleared_CoEdgeSurvives) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); + ASSERT_TRUE(aFace.IsValid()); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFace)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + for (BRepGraph_CoEdgeIterator anIt(aGraph); anIt.More(); anIt.Next()) + { + const BRepGraphInc::CoEdgeDef& aCE = anIt.Current(); + EXPECT_FALSE(anIt.CurrentId().IsRemoved(aGraph)) + << "Free-wire CoEdge must not be retired by Phase 4a"; + EXPECT_FALSE(aCE.FaceId.IsValid()) + << "FaceId should be cleared for CoEdges whose face was removed"; + EXPECT_TRUE(aCE.ChildEdgeId.IsValid()) << "ChildEdgeId must remain valid for free-wire CoEdges"; + } +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Cleanup_RemoveEdge_FreeWireCoEdgeRetired) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + ASSERT_TRUE(aGraph.Editor().Wires().Add(aCoEdges.ToArray1()).IsValid()); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(anEdge)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().CoEdges().NbActive(), 0) + << "All CoEdges should be removed (Phase 4a orphan retirement)"; + EXPECT_GE(aGraph.Topo().CoEdges().Nb(), 1) + << "Total CoEdges (including removed) should still reflect the pre-cleanup count"; +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Cleanup_RemoveVertex_EdgeVertexRefsCleared) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + ASSERT_TRUE( + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7).IsValid()); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aV0)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Vertices().NbActive(), 1); + EXPECT_EQ(aGraph.Topo().Edges().NbActive(), 1); + + for (BRepGraph_EdgeIterator anIt(aGraph); anIt.More(); anIt.Next()) + { + const BRepGraphInc::EdgeDef& anED = anIt.Current(); + EXPECT_FALSE(anED.StartVertexRefId.IsValid()) + << "StartVertexRef should be cleared for removed vertex"; + EXPECT_TRUE(anED.EndVertexRefId.IsValid()) << "EndVertexRef to surviving vertex must persist"; + } +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Cleanup_RemoveWire_FaceWireRefPruned) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); + ASSERT_TRUE(aFace.IsValid()); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aWire)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Wires().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Faces().NbActive(), 1); + const BRepGraphInc::FaceRelations& aFaceRelations = + aGraph.Topo().Faces().Relations(BRepGraph_FaceId::Start()); + EXPECT_EQ(aFaceRelations.WireRefIds.Size(), 0) << "WireRef should be pruned by Phase 5"; +} + +// ======================================================================= +// Group 5 - Product / Occurrence canonical pattern stress +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Product_EmptyProduct_CompactAndAudit) +{ + BRepGraph aGraph; + ASSERT_TRUE(aGraph.Editor().Products().Add().IsValid()); + + ASSERT_EQ(aGraph.Topo().Products().NbActive(), 1); + ASSERT_EQ(aGraph.Topo().Occurrences().NbActive(), 0); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aRes.NbNodesBefore, aRes.NbNodesAfter); + EXPECT_EQ(aGraph.Topo().Products().NbActive(), 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Product_FullCanonical_RemoveSolid_WholeChainCleans) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + aGraph.Editor().Shells().Append(aShell, aFace); + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid, aShell); + const BRepGraph_ProductId aProduct = aGraph.Editor().Products().Add(BRepGraph_NodeId(aSolid)); + ASSERT_TRUE(aProduct.IsValid()); + aGraph.Editor().Products().AppendDocumentRoot(aProduct); + + ASSERT_EQ(aGraph.Topo().Products().NbActive(), 1); + ASSERT_EQ(aGraph.Topo().Occurrences().NbActive(), 1); + ASSERT_EQ(aGraph.Topo().Solids().NbActive(), 1); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aSolid)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Solids().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Occurrences().NbActive(), 0) + << "Phase 10 retires occurrence with removed ChildNodeId"; + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aGraph.Topo().Solids().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Occurrences().NbActive(), 0); + EXPECT_EQ(aGraph.Topo().Products().NbActive(), 1); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Product_FullCanonical_CompactAndDedup_NoRegression) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const occ::handle aLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)); + const BRepGraph_EdgeId anEdge = aGraph.Editor().Edges().Add(aV0, aV1, aLine, 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + aGraph.Editor().Shells().Append(aShell, aFace); + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid, aShell); + const BRepGraph_ProductId aProduct = aGraph.Editor().Products().Add(BRepGraph_NodeId(aSolid)); + ASSERT_TRUE(aProduct.IsValid()); + aGraph.Editor().Products().AppendDocumentRoot(aProduct); + + std::ignore = BRepGraph_Compact::Perform(aGraph); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); + + const BRepGraph_Deduplicate::Result aDedupRes = BRepGraph_Deduplicate::Perform(aGraph); + EXPECT_EQ(aDedupRes.NbCanonicalSurfaces, 1); + EXPECT_EQ(aDedupRes.NbCanonicalCurves, 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); +} + +// ======================================================================= +// Group 6 - Stress pipelines (multiple operations combined) +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Pipeline_RemoveCleanupCompact_GraphValid) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0 = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1 = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_VertexId aVExtra = aGraph.Editor().Vertices().Add(gp_Pnt(100, 0, 0), 1.e-7); + const BRepGraph_EdgeId anEdge = + aGraph.Editor().Edges().Add(aV0, aV1, occ::handle(), 0.0, 10.0, 1.e-7); + NCollection_LinearVector aCoEdges; + aCoEdges.Append(aGraph.Editor().CoEdges().Add(anEdge, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aCoEdges.ToArray1()); + const occ::handle aPlane = new Geom_Plane(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); + NCollection_LinearVector anEmpty; + const BRepGraph_FaceId aFace = + aGraph.Editor().Faces().Add(aPlane, aWire, anEmpty.ToArray1(), 1.e-7); + ASSERT_TRUE(aFace.IsValid()); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFace)); + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aVExtra)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + std::ignore = BRepGraph_Compact::Perform(aGraph); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Pipeline_RemoveCleanupDedupCompact_GraphValid) +{ + BRepGraph aGraph; + const BRepGraph_VertexId aV0a = aGraph.Editor().Vertices().Add(gp_Pnt(0, 0, 0), 1.e-7); + const BRepGraph_VertexId aV1a = aGraph.Editor().Vertices().Add(gp_Pnt(10, 0, 0), 1.e-7); + const BRepGraph_VertexId aV0b = aGraph.Editor().Vertices().Add(gp_Pnt(0, 1, 0), 1.e-7); + const BRepGraph_VertexId aV1b = aGraph.Editor().Vertices().Add(gp_Pnt(10, 1, 0), 1.e-7); + const occ::handle aLine1 = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)); + const occ::handle aLine2 = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)); + ASSERT_NE(aLine1.get(), aLine2.get()); + + ASSERT_TRUE(aGraph.Editor().Edges().Add(aV0a, aV1a, aLine1, 0.0, 10.0, 1.e-7).IsValid()); + ASSERT_TRUE(aGraph.Editor().Edges().Add(aV0b, aV1b, aLine2, 0.0, 10.0, 1.e-7).IsValid()); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aV0a)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + BRepGraph_Deduplicate::Options aDedupOpts; + aDedupOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(aGraph, aDedupOpts); + std::ignore = BRepGraph_Compact::Perform(aGraph); + + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); +} + +// ======================================================================= + +TEST(BRepGraph_SparseModelTest, Pipeline_CompoundWithEmptyShells_CompactAndAudit) +{ + BRepGraph aGraph; + const BRepGraph_ShellId aShell1 = aGraph.Editor().Shells().Add(); + const BRepGraph_ShellId aShell2 = aGraph.Editor().Shells().Add(); + ASSERT_TRUE(aShell1.IsValid() && aShell2.IsValid()); + + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_NodeId(aShell1)); + aChildren.Append(BRepGraph_NodeId(aShell2)); + ASSERT_TRUE(aGraph.Editor().Compounds().Add(aChildren.ToArray1()).IsValid()); + + ASSERT_EQ(aGraph.Topo().Shells().NbActive(), 2); + ASSERT_EQ(aGraph.Topo().Compounds().NbActive(), 1); + + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aShell1)); + aGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_EQ(aGraph.Topo().Shells().NbActive(), 1); + + [[maybe_unused]] const BRepGraph_Compact::Result aRes = BRepGraph_Compact::Perform(aGraph); + EXPECT_EQ(aGraph.Topo().Shells().NbActive(), 1); + EXPECT_EQ(aGraph.Topo().Compounds().NbActive(), 1); + EXPECT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid()); +} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx index 0882427c51..bd7a541c80 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Test.cxx @@ -15,29 +15,35 @@ #include #include #include +#include #include #include #include -#include -#include +#include +#include #include #include #include "BRepGraph_RefTestTools.hxx" #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 @@ -51,6 +57,7 @@ #include #include +#include #include @@ -71,30 +78,21 @@ static_assert(std::is_constructible_v); -static_assert(!std::is_convertible_v); -static_assert(!std::is_constructible_v); -static_assert(std::is_convertible_v); +static_assert(!std::is_convertible_v); +static_assert(!std::is_constructible_v); +static_assert(std::is_convertible_v); -const occ::handle& testDoubleAttrKind() +template +bool containsId(ContainerT theIds, const IdT theId) { - static const occ::handle THE_KIND = - new BRepGraph_CacheKind(Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10021"), - "TestDoubleAttr"); - return THE_KIND; -} - -const occ::handle& testIntAttrKind() -{ - static const occ::handle THE_KIND = - new BRepGraph_CacheKind(Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10022"), "TestIntAttr"); - return THE_KIND; -} - -const occ::handle& testAuxAttrKind() -{ - static const occ::handle THE_KIND = - new BRepGraph_CacheKind(Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10023"), "TestAuxAttr"); - return THE_KIND; + for (const IdT& anId : theIds) + { + if (anId == theId) + { + return true; + } + } + return false; } static int componentKey(const BRepGraph_NodeId theNode) @@ -107,9 +105,8 @@ static NCollection_DynamicArray collectFreeEdges(const BRepGra NCollection_DynamicArray aResult(16); for (BRepGraph_EdgeIterator anEdgeIt(theGraph); anEdgeIt.More(); anEdgeIt.Next()) { - const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); - const BRepGraphInc::EdgeDef& anEdge = anEdgeIt.Current(); - if (anEdge.IsDegenerate) + const BRepGraph_EdgeId anEdgeId = anEdgeIt.CurrentId(); + if (BRepGraph_Tool::Edge::Degenerated(theGraph, anEdgeId)) { continue; } @@ -134,6 +131,23 @@ static NCollection_DynamicArray collectFreeEdges(const BRepGra return aResult; } +static BRepGraph_EdgeId addCompatibleReplacementEdge(BRepGraph& theGraph, + const BRepGraph_EdgeId theOldEdge, + const bool theReversed) +{ + const BRepGraph_VertexRefId aStartRef = BRepGraph_Tool::Edge::StartVertexId(theGraph, theOldEdge); + const BRepGraph_VertexRefId anEndRef = BRepGraph_Tool::Edge::EndVertexId(theGraph, theOldEdge); + const BRepGraph_VertexId aStartVertex = theGraph.Refs().Vertices().Entry(aStartRef).ChildVertexId; + const BRepGraph_VertexId anEndVertex = theGraph.Refs().Vertices().Entry(anEndRef).ChildVertexId; + const BRepGraph_VertexId aNewStart = theReversed ? anEndVertex : aStartVertex; + const BRepGraph_VertexId aNewEnd = theReversed ? aStartVertex : anEndVertex; + const gp_Pnt aP0 = BRepGraph_Tool::Vertex::Pnt(theGraph, aNewStart); + const gp_Pnt aP1 = BRepGraph_Tool::Vertex::Pnt(theGraph, aNewEnd); + const double aLength = aP0.Distance(aP1); + occ::handle aCurve = new Geom_Line(gp_Lin(aP0, gp_Dir(gp_Vec(aP0, aP1)))); + return theGraph.Editor().Edges().Add(aNewStart, aNewEnd, aCurve, 0.0, aLength, 1.0e-7); +} + static BRepGraph_NodeId componentRootOfFace(const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) { @@ -164,420 +178,26 @@ static int countFaceComponents(const BRepGraph& theGraph) return aRoots.Extent(); } -struct ReverseIndexInputData +static uint32_t countSameDomainFaces(const BRepGraph& theGraph, const BRepGraph_FaceId theFace) { - NCollection_DynamicArray Vertices; - NCollection_DynamicArray Edges; - NCollection_DynamicArray CoEdges; - NCollection_DynamicArray Wires; - NCollection_DynamicArray Faces; - NCollection_DynamicArray Shells; - NCollection_DynamicArray Solids; - NCollection_DynamicArray Compounds; - NCollection_DynamicArray CompSolids; - NCollection_DynamicArray ShellRefs; - NCollection_DynamicArray FaceRefs; - NCollection_DynamicArray WireRefs; - NCollection_DynamicArray CoEdgeRefs; - NCollection_DynamicArray SolidRefs; - NCollection_DynamicArray ChildRefs; - NCollection_DynamicArray VertexRefs; -}; + const occ::handle& aSurface = BRepGraph_Tool::Face::Surface(theGraph, theFace); + if (aSurface.IsNull()) + { + return 0; + } -static ReverseIndexInputData buildReverseIndexBaseInput() -{ - ReverseIndexInputData aData; - - BRepGraphInc::EdgeDef& anEdge = aData.Edges.Appended(); - anEdge.InitVectors(occ::handle()); - - // Create vertex ref entries for start and end vertices. - BRepGraphInc::VertexRef& aStartVRef0 = aData.VertexRefs.Appended(); - aStartVRef0.ParentId = BRepGraph_EdgeId::Start(); - aStartVRef0.VertexDefId = BRepGraph_VertexId::Start(); - aStartVRef0.Orientation = TopAbs_FORWARD; - anEdge.StartVertexRefId = BRepGraph_VertexRefId(aData.VertexRefs.Length() - 1); - - BRepGraphInc::VertexRef& anEndVRef0 = aData.VertexRefs.Appended(); - anEndVRef0.ParentId = BRepGraph_EdgeId::Start(); - anEndVRef0.VertexDefId = BRepGraph_VertexId(1); - anEndVRef0.Orientation = TopAbs_REVERSED; - anEdge.EndVertexRefId = BRepGraph_VertexRefId(aData.VertexRefs.Length() - 1); - - BRepGraphInc::CoEdgeDef& aCoEdge = aData.CoEdges.Appended(); - aCoEdge.InitVectors(occ::handle()); - aCoEdge.EdgeDefId = BRepGraph_EdgeId::Start(); - aCoEdge.FaceDefId = BRepGraph_FaceId::Start(); - - BRepGraphInc::WireDef& aWire = aData.Wires.Appended(); - aWire.InitVectors(occ::handle()); - - BRepGraphInc::CoEdgeRef& aCoEdgeRef = aData.CoEdgeRefs.Appended(); - aCoEdgeRef.ParentId = BRepGraph_WireId::Start(); - aCoEdgeRef.CoEdgeDefId = BRepGraph_CoEdgeId::Start(); - aWire.CoEdgeRefIds.Append(BRepGraph_CoEdgeRefId::Start()); - - BRepGraphInc::FaceDef& aFace = aData.Faces.Appended(); - aFace.InitVectors(occ::handle()); - - BRepGraphInc::WireRef& aWireRef = aData.WireRefs.Appended(); - aWireRef.ParentId = BRepGraph_FaceId::Start(); - aWireRef.WireDefId = BRepGraph_WireId::Start(); - aWireRef.IsOuter = true; - aFace.WireRefIds.Append(BRepGraph_WireRefId::Start()); - - BRepGraphInc::ShellDef& aShell = aData.Shells.Appended(); - aShell.InitVectors(occ::handle()); - - BRepGraphInc::FaceRef& aFaceRef = aData.FaceRefs.Appended(); - aFaceRef.ParentId = BRepGraph_ShellId::Start(); - aFaceRef.FaceDefId = BRepGraph_FaceId::Start(); - aShell.FaceRefIds.Append(BRepGraph_FaceRefId::Start()); - - BRepGraphInc::SolidDef& aSolid = aData.Solids.Appended(); - aSolid.InitVectors(occ::handle()); - - BRepGraphInc::ShellRef& aShellRef = aData.ShellRefs.Appended(); - aShellRef.ParentId = BRepGraph_SolidId::Start(); - aShellRef.ShellDefId = BRepGraph_ShellId::Start(); - aSolid.ShellRefIds.Append(BRepGraph_ShellRefId::Start()); - - return aData; + uint32_t aCount = 0; + for (BRepGraph_FaceIterator aFaceIt(theGraph); aFaceIt.More(); aFaceIt.Next()) + { + const BRepGraph_FaceId anOtherFace = aFaceIt.CurrentId(); + if (anOtherFace != theFace && BRepGraph_Tool::Face::Surface(theGraph, anOtherFace) == aSurface) + { + ++aCount; + } + } + return aCount; } -static void appendReverseIndexDeltaInput(ReverseIndexInputData& theData) -{ - // Active edge/wire/face/shell/solid chain. - BRepGraphInc::EdgeDef& anEdge = theData.Edges.Appended(); - anEdge.InitVectors(occ::handle()); - - BRepGraphInc::VertexRef& aStartVRef1 = theData.VertexRefs.Appended(); - aStartVRef1.ParentId = BRepGraph_EdgeId(1); - aStartVRef1.VertexDefId = BRepGraph_VertexId(2); - aStartVRef1.Orientation = TopAbs_FORWARD; - anEdge.StartVertexRefId = BRepGraph_VertexRefId(theData.VertexRefs.Length() - 1); - - BRepGraphInc::VertexRef& anEndVRef1 = theData.VertexRefs.Appended(); - anEndVRef1.ParentId = BRepGraph_EdgeId(1); - anEndVRef1.VertexDefId = BRepGraph_VertexId(3); - anEndVRef1.Orientation = TopAbs_REVERSED; - anEdge.EndVertexRefId = BRepGraph_VertexRefId(theData.VertexRefs.Length() - 1); - - BRepGraphInc::CoEdgeDef& aCoEdge = theData.CoEdges.Appended(); - aCoEdge.InitVectors(occ::handle()); - aCoEdge.EdgeDefId = BRepGraph_EdgeId(1); - aCoEdge.FaceDefId = BRepGraph_FaceId(1); - - BRepGraphInc::WireDef& aWire = theData.Wires.Appended(); - aWire.InitVectors(occ::handle()); - - BRepGraphInc::CoEdgeRef& aCoEdgeRef = theData.CoEdgeRefs.Appended(); - aCoEdgeRef.ParentId = BRepGraph_WireId(1); - aCoEdgeRef.CoEdgeDefId = BRepGraph_CoEdgeId(1); - aWire.CoEdgeRefIds.Append(BRepGraph_CoEdgeRefId(theData.CoEdgeRefs.Length() - 1)); - - BRepGraphInc::FaceDef& aFace = theData.Faces.Appended(); - aFace.InitVectors(occ::handle()); - - BRepGraphInc::WireRef& aWireRef = theData.WireRefs.Appended(); - aWireRef.ParentId = BRepGraph_FaceId(1); - aWireRef.WireDefId = BRepGraph_WireId(1); - aWireRef.IsOuter = true; - aFace.WireRefIds.Append(BRepGraph_WireRefId(theData.WireRefs.Length() - 1)); - - BRepGraphInc::ShellDef& aShell = theData.Shells.Appended(); - aShell.InitVectors(occ::handle()); - - BRepGraphInc::FaceRef& aFaceRef = theData.FaceRefs.Appended(); - aFaceRef.ParentId = BRepGraph_ShellId(1); - aFaceRef.FaceDefId = BRepGraph_FaceId(1); - aShell.FaceRefIds.Append(BRepGraph_FaceRefId(theData.FaceRefs.Length() - 1)); - - BRepGraphInc::SolidDef& aSolid = theData.Solids.Appended(); - aSolid.InitVectors(occ::handle()); - - BRepGraphInc::ShellRef& aShellRef = theData.ShellRefs.Appended(); - aShellRef.ParentId = BRepGraph_SolidId(1); - aShellRef.ShellDefId = BRepGraph_ShellId(1); - aSolid.ShellRefIds.Append(BRepGraph_ShellRefId(theData.ShellRefs.Length() - 1)); - - BRepGraphInc::CompoundDef& aCompoundOfSolid = theData.Compounds.Appended(); - aCompoundOfSolid.InitVectors(occ::handle()); - - BRepGraphInc::ChildRef& aCompoundSolidRef = theData.ChildRefs.Appended(); - aCompoundSolidRef.ParentId = BRepGraph_CompoundId::Start(); - aCompoundSolidRef.ChildDefId = BRepGraph_SolidId(1); - aCompoundOfSolid.ChildRefIds.Append(BRepGraph_ChildRefId(theData.ChildRefs.Length() - 1)); - - BRepGraphInc::CompoundDef& aCompoundOfShell = theData.Compounds.Appended(); - aCompoundOfShell.InitVectors(occ::handle()); - - BRepGraphInc::ChildRef& aCompoundShellRef = theData.ChildRefs.Appended(); - aCompoundShellRef.ParentId = BRepGraph_CompoundId(1); - aCompoundShellRef.ChildDefId = BRepGraph_ShellId(1); - aCompoundOfShell.ChildRefIds.Append(BRepGraph_ChildRefId(theData.ChildRefs.Length() - 1)); - - BRepGraphInc::CompoundDef& aCompoundOfFace = theData.Compounds.Appended(); - aCompoundOfFace.InitVectors(occ::handle()); - - BRepGraphInc::ChildRef& aCompoundFaceRef = theData.ChildRefs.Appended(); - aCompoundFaceRef.ParentId = BRepGraph_CompoundId(2); - aCompoundFaceRef.ChildDefId = BRepGraph_FaceId(1); - aCompoundOfFace.ChildRefIds.Append(BRepGraph_ChildRefId(theData.ChildRefs.Length() - 1)); - - BRepGraphInc::CompoundDef& aNestedCompound = theData.Compounds.Appended(); - aNestedCompound.InitVectors(occ::handle()); - - BRepGraphInc::ChildRef& aNestedCompoundRef = theData.ChildRefs.Appended(); - aNestedCompoundRef.ParentId = BRepGraph_CompoundId(3); - aNestedCompoundRef.ChildDefId = BRepGraph_CompoundId::Start(); - aNestedCompound.ChildRefIds.Append(BRepGraph_ChildRefId(theData.ChildRefs.Length() - 1)); - - BRepGraphInc::CompSolidDef& aCompSolid = theData.CompSolids.Appended(); - aCompSolid.InitVectors(occ::handle()); - - BRepGraphInc::SolidRef& aCompSolidRef = theData.SolidRefs.Appended(); - aCompSolidRef.ParentId = BRepGraph_CompSolidId::Start(); - aCompSolidRef.SolidDefId = BRepGraph_SolidId(1); - aCompSolid.SolidRefIds.Append(BRepGraph_SolidRefId(theData.SolidRefs.Length() - 1)); - - BRepGraphInc::CompoundDef& aCompoundOfCompSolid = theData.Compounds.Appended(); - aCompoundOfCompSolid.InitVectors(occ::handle()); - - BRepGraphInc::ChildRef& aCompoundCompSolidRef = theData.ChildRefs.Appended(); - aCompoundCompSolidRef.ParentId = BRepGraph_CompoundId(4); - aCompoundCompSolidRef.ChildDefId = BRepGraph_CompSolidId::Start(); - aCompoundOfCompSolid.ChildRefIds.Append(BRepGraph_ChildRefId(theData.ChildRefs.Length() - 1)); - - // Removed entities to ensure BuildDelta skips them. - BRepGraphInc::EdgeDef& aRemovedEdge = theData.Edges.Appended(); - aRemovedEdge.InitVectors(occ::handle()); - aRemovedEdge.IsRemoved = true; - - BRepGraphInc::VertexRef& aRemovedStartVRef = theData.VertexRefs.Appended(); - aRemovedStartVRef.ParentId = BRepGraph_EdgeId(2); - aRemovedStartVRef.VertexDefId = BRepGraph_VertexId(10); - aRemovedStartVRef.Orientation = TopAbs_FORWARD; - aRemovedEdge.StartVertexRefId = BRepGraph_VertexRefId(theData.VertexRefs.Length() - 1); - - BRepGraphInc::VertexRef& aRemovedEndVRef = theData.VertexRefs.Appended(); - aRemovedEndVRef.ParentId = BRepGraph_EdgeId(2); - aRemovedEndVRef.VertexDefId = BRepGraph_VertexId(11); - aRemovedEndVRef.Orientation = TopAbs_REVERSED; - aRemovedEdge.EndVertexRefId = BRepGraph_VertexRefId(theData.VertexRefs.Length() - 1); - - BRepGraphInc::CoEdgeDef& aRemovedCoEdge = theData.CoEdges.Appended(); - aRemovedCoEdge.InitVectors(occ::handle()); - aRemovedCoEdge.IsRemoved = true; - aRemovedCoEdge.EdgeDefId = BRepGraph_EdgeId(2); - aRemovedCoEdge.FaceDefId = BRepGraph_FaceId(2); - - BRepGraphInc::WireDef& aRemovedWire = theData.Wires.Appended(); - aRemovedWire.InitVectors(occ::handle()); - aRemovedWire.IsRemoved = true; - - BRepGraphInc::CoEdgeRef& aRemovedCoEdgeRef = theData.CoEdgeRefs.Appended(); - aRemovedCoEdgeRef.ParentId = BRepGraph_WireId(2); - aRemovedCoEdgeRef.CoEdgeDefId = BRepGraph_CoEdgeId(2); - aRemovedCoEdgeRef.IsRemoved = true; - - BRepGraphInc::FaceDef& aRemovedFace = theData.Faces.Appended(); - aRemovedFace.InitVectors(occ::handle()); - aRemovedFace.IsRemoved = true; - - BRepGraphInc::WireRef& aRemovedWireRef = theData.WireRefs.Appended(); - aRemovedWireRef.ParentId = BRepGraph_FaceId(2); - aRemovedWireRef.WireDefId = BRepGraph_WireId(2); - aRemovedWireRef.IsRemoved = true; - - BRepGraphInc::ShellDef& aRemovedShell = theData.Shells.Appended(); - aRemovedShell.InitVectors(occ::handle()); - aRemovedShell.IsRemoved = true; - - BRepGraphInc::FaceRef& aRemovedFaceRef = theData.FaceRefs.Appended(); - aRemovedFaceRef.ParentId = BRepGraph_ShellId(2); - aRemovedFaceRef.FaceDefId = BRepGraph_FaceId(2); - aRemovedFaceRef.IsRemoved = true; - - BRepGraphInc::SolidDef& aRemovedSolid = theData.Solids.Appended(); - aRemovedSolid.InitVectors(occ::handle()); - aRemovedSolid.IsRemoved = true; - - BRepGraphInc::ShellRef& aRemovedShellRef = theData.ShellRefs.Appended(); - aRemovedShellRef.ParentId = BRepGraph_SolidId(2); - aRemovedShellRef.ShellDefId = BRepGraph_ShellId(2); - aRemovedShellRef.IsRemoved = true; -} - -static void verifyBuildDeltaScenario(const occ::handle& theAllocator) -{ - ReverseIndexInputData aData = buildReverseIndexBaseInput(); - - BRepGraphInc_ReverseIndex aRevIdx; - aRevIdx.SetAllocator(theAllocator); - aRevIdx.Build(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs); - - EXPECT_TRUE(aRevIdx.Validate(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs)); - - const NCollection_DynamicArray* aBaseWires = - aRevIdx.WiresOfEdge(BRepGraph_EdgeId::Start()); - ASSERT_NE(aBaseWires, nullptr); - ASSERT_EQ(aBaseWires->Length(), 1); - EXPECT_EQ(aBaseWires->Value(0), BRepGraph_WireId::Start()); - - const int anOldNbEdges = aData.Edges.Length(); - const int anOldNbWires = aData.Wires.Length(); - const int anOldNbFaces = aData.Faces.Length(); - const int anOldNbShells = aData.Shells.Length(); - const int anOldNbSolids = aData.Solids.Length(); - const int anOldNbCompounds = aData.Compounds.Length(); - const int anOldNbCompSolids = aData.CompSolids.Length(); - const int anOldNbChildRefs = aData.ChildRefs.Length(); - const int anOldNbSolidRefs = aData.SolidRefs.Length(); - - appendReverseIndexDeltaInput(aData); - - aRevIdx.BuildDelta(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs, - anOldNbEdges, - anOldNbWires, - anOldNbFaces, - anOldNbShells, - anOldNbSolids, - anOldNbCompounds, - anOldNbCompSolids, - anOldNbChildRefs, - anOldNbSolidRefs); - - EXPECT_TRUE(aRevIdx.Validate(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs)); - - const NCollection_DynamicArray* aActiveWires = - aRevIdx.WiresOfEdge(BRepGraph_EdgeId(1)); - ASSERT_NE(aActiveWires, nullptr); - ASSERT_EQ(aActiveWires->Length(), 1); - EXPECT_EQ(aActiveWires->Value(0), BRepGraph_WireId(1)); - - const NCollection_DynamicArray* aActiveFaces = - aRevIdx.FacesOfWire(BRepGraph_WireId(1)); - ASSERT_NE(aActiveFaces, nullptr); - ASSERT_EQ(aActiveFaces->Length(), 1); - EXPECT_EQ(aActiveFaces->Value(0), BRepGraph_FaceId(1)); - - const NCollection_DynamicArray* anActiveShells = - aRevIdx.ShellsOfFace(BRepGraph_FaceId(1)); - ASSERT_NE(anActiveShells, nullptr); - ASSERT_EQ(anActiveShells->Length(), 1); - EXPECT_EQ(anActiveShells->Value(0), BRepGraph_ShellId(1)); - - const NCollection_DynamicArray* anActiveSolids = - aRevIdx.SolidsOfShell(BRepGraph_ShellId(1)); - ASSERT_NE(anActiveSolids, nullptr); - ASSERT_EQ(anActiveSolids->Length(), 1); - EXPECT_EQ(anActiveSolids->Value(0), BRepGraph_SolidId(1)); - - const NCollection_DynamicArray* aCompoundsOfSolid = - aRevIdx.CompoundsOfSolid(BRepGraph_SolidId(1)); - ASSERT_NE(aCompoundsOfSolid, nullptr); - ASSERT_EQ(aCompoundsOfSolid->Length(), 1); - EXPECT_EQ(aCompoundsOfSolid->Value(0), BRepGraph_CompoundId::Start()); - - const NCollection_DynamicArray* aCompoundsOfShell = - aRevIdx.CompoundsOfShell(BRepGraph_ShellId(1)); - ASSERT_NE(aCompoundsOfShell, nullptr); - ASSERT_EQ(aCompoundsOfShell->Length(), 1); - EXPECT_EQ(aCompoundsOfShell->Value(0), BRepGraph_CompoundId(1)); - - const NCollection_DynamicArray* aCompoundsOfFace = - aRevIdx.CompoundsOfFace(BRepGraph_FaceId(1)); - ASSERT_NE(aCompoundsOfFace, nullptr); - ASSERT_EQ(aCompoundsOfFace->Length(), 1); - EXPECT_EQ(aCompoundsOfFace->Value(0), BRepGraph_CompoundId(2)); - - const NCollection_DynamicArray* aCompoundsOfCompound = - aRevIdx.CompoundsOfCompound(BRepGraph_CompoundId::Start()); - ASSERT_NE(aCompoundsOfCompound, nullptr); - ASSERT_EQ(aCompoundsOfCompound->Length(), 1); - EXPECT_EQ(aCompoundsOfCompound->Value(0), BRepGraph_CompoundId(3)); - - const NCollection_DynamicArray* aCompSolidsOfSolid = - aRevIdx.CompSolidsOfSolid(BRepGraph_SolidId(1)); - ASSERT_NE(aCompSolidsOfSolid, nullptr); - ASSERT_EQ(aCompSolidsOfSolid->Length(), 1); - EXPECT_EQ(aCompSolidsOfSolid->Value(0), BRepGraph_CompSolidId::Start()); - - const NCollection_DynamicArray* aCompoundsOfCompSolid = - aRevIdx.CompoundsOfCompSolid(BRepGraph_CompSolidId::Start()); - ASSERT_NE(aCompoundsOfCompSolid, nullptr); - ASSERT_EQ(aCompoundsOfCompSolid->Length(), 1); - EXPECT_EQ(aCompoundsOfCompSolid->Value(0), BRepGraph_CompoundId(4)); - - EXPECT_EQ(aRevIdx.NbFacesOfEdge(BRepGraph_EdgeId(1)), 1); - - EXPECT_EQ(aRevIdx.WiresOfEdge(BRepGraph_EdgeId(2)), nullptr); - EXPECT_EQ(aRevIdx.EdgesOfVertex(BRepGraph_VertexId(10)), nullptr); - EXPECT_EQ(aRevIdx.NbFacesOfEdge(BRepGraph_EdgeId(2)), 0); -} -} // namespace - class BRepGraphTest : public testing::Test { protected: @@ -586,395 +206,17 @@ protected: BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBox); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = myGraph.Shapes().Add(aBox); } BRepGraph myGraph; }; -TEST_F(BRepGraphTest, FaceCountMatchesFacesVector_AfterBindUnbindSequence) +} // namespace + +TEST_F(BRepGraphTest, Build_SimpleBox_IsNotEmpty) { - ReverseIndexInputData aData = buildReverseIndexBaseInput(); - - // Add a second active face for bind/unbind sequence checks. - BRepGraphInc::FaceDef& aFace1 = aData.Faces.Appended(); - aFace1.InitVectors(occ::handle()); - - BRepGraphInc_ReverseIndex aRevIdx; - aRevIdx.Build(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs); - - auto expectCountsMatch = [&]() { - for (int anEdgeIdx = 0; anEdgeIdx < aData.Edges.Length(); ++anEdgeIdx) - { - const NCollection_DynamicArray* aFaces = - aRevIdx.FacesOfEdge(BRepGraph_EdgeId(anEdgeIdx)); - const int aExpectedCount = (aFaces == nullptr) ? 0 : aFaces->Length(); - EXPECT_EQ(aRevIdx.NbFacesOfEdge(BRepGraph_EdgeId(anEdgeIdx)), aExpectedCount) - << "Edge " << anEdgeIdx << " face-count cache mismatch"; - } - }; - - expectCountsMatch(); - - // Duplicate bind should be idempotent. - aRevIdx.BindEdgeToFace(BRepGraph_EdgeId::Start(), BRepGraph_FaceId::Start()); - expectCountsMatch(); - - // Bind/unbind/rebind sequence must keep cached count consistent. - aRevIdx.BindEdgeToFace(BRepGraph_EdgeId::Start(), BRepGraph_FaceId(1)); - expectCountsMatch(); - aRevIdx.UnbindEdgeFromFace(BRepGraph_EdgeId::Start(), BRepGraph_FaceId::Start()); - expectCountsMatch(); - aRevIdx.BindEdgeToFace(BRepGraph_EdgeId::Start(), BRepGraph_FaceId::Start()); - expectCountsMatch(); - aRevIdx.UnbindEdgeFromFace(BRepGraph_EdgeId::Start(), BRepGraph_FaceId(1)); - expectCountsMatch(); -} - -TEST_F(BRepGraphTest, ReverseIndexValidate_DetectsStaleEdgeWireMapping) -{ - ReverseIndexInputData aData = buildReverseIndexBaseInput(); - - BRepGraphInc_ReverseIndex aRevIdx; - aRevIdx.Build(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs); - - ASSERT_TRUE(aRevIdx.Validate(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs)); - - // Inject stale reverse entry (edge 0 -> wire 1) with no matching forward coedge ref. - BRepGraphInc::WireDef& aWire1 = aData.Wires.Appended(); - aWire1.InitVectors(occ::handle()); - - aRevIdx.BindEdgeToWire(BRepGraph_EdgeId::Start(), BRepGraph_WireId(1)); - - EXPECT_FALSE(aRevIdx.Validate(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs)); -} - -TEST_F(BRepGraphTest, ReverseIndexValidate_DetectsStaleCompoundAndCompSolidMappings) -{ - ReverseIndexInputData aData = buildReverseIndexBaseInput(); - appendReverseIndexDeltaInput(aData); - - BRepGraphInc_ReverseIndex aRevIdx; - aRevIdx.Build(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs); - - ASSERT_TRUE(aRevIdx.Validate(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs)); - - // Corrupt forward refs after Build() so reverse tables become stale. - aData.SolidRefs.ChangeValue(0).IsRemoved = true; - aData.ChildRefs.ChangeValue(0).IsRemoved = true; - - EXPECT_FALSE(aRevIdx.Validate(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs)); -} - -TEST_F(BRepGraphTest, BuildDelta_IndexesNewRefsOnExistingCompoundAndCompSolid) -{ - ReverseIndexInputData aData = buildReverseIndexBaseInput(); - - // Pre-existing compound and compsolid parents (no refs yet). - BRepGraphInc::CompoundDef& aCompound0 = aData.Compounds.Appended(); - aCompound0.InitVectors(occ::handle()); - - BRepGraphInc::CompSolidDef& aCompSolid0 = aData.CompSolids.Appended(); - aCompSolid0.InitVectors(occ::handle()); - - BRepGraphInc_ReverseIndex aRevIdx; - aRevIdx.Build(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs); - - const int anOldNbEdges = aData.Edges.Length(); - const int anOldNbWires = aData.Wires.Length(); - const int anOldNbFaces = aData.Faces.Length(); - const int anOldNbShells = aData.Shells.Length(); - const int anOldNbSolids = aData.Solids.Length(); - const int anOldNbCompounds = aData.Compounds.Length(); - const int anOldNbCompSolids = aData.CompSolids.Length(); - const int anOldNbChildRefs = aData.ChildRefs.Length(); - const int anOldNbSolidRefs = aData.SolidRefs.Length(); - - // Append refs under existing parents to validate parent-agnostic delta indexing. - BRepGraphInc::ChildRef& aChildRef = aData.ChildRefs.Appended(); - aChildRef.ParentId = BRepGraph_CompoundId::Start(); - aChildRef.ChildDefId = BRepGraph_SolidId::Start(); - aCompound0.ChildRefIds.Append(BRepGraph_ChildRefId(aData.ChildRefs.Length() - 1)); - - BRepGraphInc::SolidRef& aSolidRef = aData.SolidRefs.Appended(); - aSolidRef.ParentId = BRepGraph_CompSolidId::Start(); - aSolidRef.SolidDefId = BRepGraph_SolidId::Start(); - aCompSolid0.SolidRefIds.Append(BRepGraph_SolidRefId(aData.SolidRefs.Length() - 1)); - - aRevIdx.BuildDelta(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs, - anOldNbEdges, - anOldNbWires, - anOldNbFaces, - anOldNbShells, - anOldNbSolids, - anOldNbCompounds, - anOldNbCompSolids, - anOldNbChildRefs, - anOldNbSolidRefs); - - const NCollection_DynamicArray* aCompoundsOfSolid = - aRevIdx.CompoundsOfSolid(BRepGraph_SolidId::Start()); - ASSERT_NE(aCompoundsOfSolid, nullptr); - EXPECT_EQ(aCompoundsOfSolid->Length(), 1); - EXPECT_EQ(aCompoundsOfSolid->Value(0), BRepGraph_CompoundId::Start()); - - const NCollection_DynamicArray* aCompSolidsOfSolid = - aRevIdx.CompSolidsOfSolid(BRepGraph_SolidId::Start()); - ASSERT_NE(aCompSolidsOfSolid, nullptr); - EXPECT_EQ(aCompSolidsOfSolid->Length(), 1); - EXPECT_EQ(aCompSolidsOfSolid->Value(0), BRepGraph_CompSolidId::Start()); - - EXPECT_TRUE(aRevIdx.Validate(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs)); -} - -TEST_F(BRepGraphTest, ReverseIndex_CompoundOfAtomicKinds_WireEdgeVertex) -{ - // A TopoDS_Compound can legally hold atomic topology (wire / edge / vertex). - // Before the Wave-2 fix, these ChildRefs were silently dropped from the - // reverse index and CompoundsOfWire/Edge/Vertex returned empty. - ReverseIndexInputData aData = buildReverseIndexBaseInput(); - - // Ensure vertex slots exist for Vertex(0)/Vertex(1)/Vertex(2) that the atomic-compound refs - // will target below. - for (int i = 0; i < 3; ++i) - { - aData.Vertices.Appended(); - } - - // Parent compound that contains a Wire, an Edge, and a Vertex directly. - BRepGraphInc::CompoundDef& aCompound = aData.Compounds.Appended(); - aCompound.InitVectors(occ::handle()); - - auto appendAtomicChild = [&](const BRepGraph_NodeId::Kind theKind, const int theChildIdx) { - BRepGraphInc::ChildRef& aRef = aData.ChildRefs.Appended(); - aRef.ParentId = BRepGraph_CompoundId::Start(); - aRef.ChildDefId = BRepGraph_NodeId(theKind, theChildIdx); - aCompound.ChildRefIds.Append(BRepGraph_ChildRefId(aData.ChildRefs.Length() - 1)); - }; - - appendAtomicChild(BRepGraph_NodeId::Kind::Wire, 0); // Compound -> Wire(0) - appendAtomicChild(BRepGraph_NodeId::Kind::Edge, 0); // Compound -> Edge(0) - appendAtomicChild(BRepGraph_NodeId::Kind::Vertex, 2); // Compound -> Vertex(2) - - BRepGraphInc_ReverseIndex aRevIdx; - aRevIdx.Build(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs); - - const NCollection_DynamicArray* aCompoundsOfWire = - aRevIdx.CompoundsOfWire(BRepGraph_WireId::Start()); - ASSERT_NE(aCompoundsOfWire, nullptr); - EXPECT_EQ(aCompoundsOfWire->Length(), 1); - EXPECT_EQ(aCompoundsOfWire->Value(0), BRepGraph_CompoundId::Start()); - - const NCollection_DynamicArray* aCompoundsOfEdge = - aRevIdx.CompoundsOfEdge(BRepGraph_EdgeId::Start()); - ASSERT_NE(aCompoundsOfEdge, nullptr); - EXPECT_EQ(aCompoundsOfEdge->Length(), 1); - EXPECT_EQ(aCompoundsOfEdge->Value(0), BRepGraph_CompoundId::Start()); - - const NCollection_DynamicArray* aCompoundsOfVertex = - aRevIdx.CompoundsOfVertex(BRepGraph_VertexId(2)); - ASSERT_NE(aCompoundsOfVertex, nullptr); - EXPECT_EQ(aCompoundsOfVertex->Length(), 1); - EXPECT_EQ(aCompoundsOfVertex->Value(0), BRepGraph_CompoundId::Start()); - - EXPECT_TRUE(aRevIdx.Validate(aData.Vertices, - aData.Edges, - aData.CoEdges, - aData.Wires, - aData.Faces, - aData.Shells, - aData.Solids, - aData.Compounds, - aData.CompSolids, - aData.ShellRefs, - aData.FaceRefs, - aData.WireRefs, - aData.CoEdgeRefs, - aData.SolidRefs, - aData.ChildRefs, - aData.VertexRefs)); -} - -TEST_F(BRepGraphTest, Build_SimpleBox_IsDone) -{ - EXPECT_TRUE(myGraph.IsDone()); -} - -TEST_F(BRepGraphTest, BuildDelta_ValidateAndSkipRemoved_NullAllocator) -{ - verifyBuildDeltaScenario(occ::handle()); -} - -TEST_F(BRepGraphTest, BuildDelta_ValidateAndSkipRemoved_WithAllocator) -{ - const occ::handle anAllocator = new NCollection_IncAllocator(); - verifyBuildDeltaScenario(anAllocator); + EXPECT_FALSE(myGraph.IsEmpty()); } TEST_F(BRepGraphTest, Build_SimpleBox_CorrectCounts) @@ -1011,9 +253,9 @@ TEST_F(BRepGraphTest, Edge_CurveAndVertices_AreValid) EXPECT_TRUE(BRepGraph_Tool::Edge::HasCurve(myGraph, anEdgeId)) << "Edge " << anEdgeId.Index << " has no Curve3D rep"; } - EXPECT_TRUE(BRepGraph_Tool::Edge::StartVertexRef(myGraph, anEdgeId).VertexDefId.IsValid()) + EXPECT_TRUE(BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdgeId).IsValid()) << "Edge " << anEdgeId.Index << " has invalid StartVertexId"; - EXPECT_TRUE(BRepGraph_Tool::Edge::EndVertexRef(myGraph, anEdgeId).VertexDefId.IsValid()) + EXPECT_TRUE(BRepGraph_Tool::Edge::EndVertexId(myGraph, anEdgeId).IsValid()) << "Edge " << anEdgeId.Index << " has invalid EndVertexId"; } } @@ -1038,7 +280,7 @@ TEST_F(BRepGraphTest, FaceDef_HasValidSurface) } } -TEST_F(BRepGraphTest, FindPCurve_ValidPair) +TEST_F(BRepGraphTest, FindPCurveCoEdgeId_ValidPair) { for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { @@ -1048,21 +290,20 @@ TEST_F(BRepGraphTest, FindPCurve_ValidPair) { continue; } - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, anOuterWire); - for (const BRepGraph_CoEdgeRefId& aCoEdgeRefId : aCoEdgeRefs) + const NCollection_LinearVector& aCoEdgeIds = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, anOuterWire); + for (const BRepGraph_CoEdgeId& aCoEdgeId : aCoEdgeIds) { - const BRepGraphInc::CoEdgeRef& aCR = myGraph.Refs().CoEdges().Entry(aCoEdgeRefId); - const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aCR.CoEdgeDefId); - if (BRepGraph_Tool::Edge::Degenerated(myGraph, BRepGraph_EdgeId(aCoEdge.EdgeDefId))) + const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aCoEdgeId); + if (BRepGraph_Tool::Edge::Degenerated(myGraph, BRepGraph_EdgeId(aCoEdge.ChildEdgeId))) { continue; } - const BRepGraphInc::CoEdgeDef* aPCurveEntry = - BRepGraph_Tool::Edge::FindPCurve(myGraph, - BRepGraph_EdgeId(aCoEdge.EdgeDefId), - aFaceIt.CurrentId()); - EXPECT_NE(aPCurveEntry, nullptr) << "Missing PCurve for edge " << aCoEdge.EdgeDefId.Index + const BRepGraph_CoEdgeId aPCurveId = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(myGraph, + BRepGraph_EdgeId(aCoEdge.ChildEdgeId), + aFaceIt.CurrentId()); + EXPECT_TRUE(aPCurveId.IsValid()) << "Missing PCurve for edge " << aCoEdge.ChildEdgeId.Index << " on face " << aFaceIt.CurrentId().Index; } } @@ -1070,7 +311,7 @@ TEST_F(BRepGraphTest, FindPCurve_ValidPair) TEST_F(BRepGraphTest, UID_Unique) { - NCollection_Map aUIDSet; + NCollection_FlatMap aUIDSet; for (BRepGraph_SolidIterator aSolidIt(myGraph); aSolidIt.More(); aSolidIt.Next()) { EXPECT_TRUE(aUIDSet.Add(myGraph.UIDs().Of(BRepGraph_NodeId(aSolidIt.CurrentId())))); @@ -1113,10 +354,8 @@ TEST_F(BRepGraphTest, SameDomainFaces_Box_Empty) { for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { - BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); - NCollection_DynamicArray aSameDomain = - myGraph.Topo().Faces().SameDomain(aFaceId, myGraph.Allocator()); - EXPECT_EQ(aSameDomain.Length(), 0) + BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + EXPECT_EQ(countSameDomainFaces(myGraph, aFaceId), 0) << "Box face " << aFaceId.Index << " should have no same-domain faces"; } } @@ -1139,30 +378,13 @@ TEST_F(BRepGraphTest, Decompose_TwoSeparateFaces) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 2); EXPECT_EQ(countFaceComponents(aGraph), 2); } -TEST_F(BRepGraphTest, UserAttribute_SetGet) -{ - BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); - - Handle(BRepGraph_TypedCacheValue) anAttr = new BRepGraph_TypedCacheValue(3.14); - myGraph.Cache().Set(aFaceId, testDoubleAttrKind(), anAttr); - - occ::handle aRetrieved = myGraph.Cache().Get(aFaceId, testDoubleAttrKind()); - ASSERT_FALSE(aRetrieved.IsNull()); - - Handle(BRepGraph_TypedCacheValue) aTyped = - Handle(BRepGraph_TypedCacheValue)::DownCast(aRetrieved); - ASSERT_FALSE(aTyped.IsNull()); - EXPECT_NEAR(aTyped->UncheckedValue(), 3.14, 1.0e-10); -} - TEST_F(BRepGraphTest, ReBuild_UIDMonotonic) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); @@ -1170,21 +392,18 @@ TEST_F(BRepGraphTest, ReBuild_UIDMonotonic) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aBox); - const uint32_t aGen1 = aGraph.UIDs().Generation(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aBox); + const uint32_t aGen1 = aGraph.UIDs().Generation(); // Access a UID from the first build to verify it works. ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); const BRepGraph_NodeId aFirstFace(BRepGraph_NodeId::Kind::Face, 0); const BRepGraph_UID aFirstUID = aGraph.UIDs().Of(aFirstFace); EXPECT_TRUE(aFirstUID.IsValid()); - EXPECT_EQ(aFirstUID.Generation(), aGen1); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aBox); - const uint32_t aGen2 = aGraph.UIDs().Generation(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aBox); + const uint32_t aGen2 = aGraph.UIDs().Generation(); EXPECT_GT(aGen2, aGen1); @@ -1195,8 +414,6 @@ TEST_F(BRepGraphTest, ReBuild_UIDMonotonic) BRepGraph_UID aUID = aGraph.UIDs().Of(aNodeId); EXPECT_TRUE(aUID.IsValid()) << "Face " << aFaceIt.CurrentId().Index << " should have a valid UID"; - EXPECT_EQ(aUID.Generation(), aGen2) - << "Face " << aFaceIt.CurrentId().Index << " UID should have new generation"; } // First build's UID should no longer be valid in the new generation. @@ -1213,20 +430,19 @@ TEST_F(BRepGraphTest, DetectMissingPCurves_ValidBox_Empty) anEdgeExp.More(); anEdgeExp.Next()) { - const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::FromNodeId(anEdgeExp.Current().DefId); - const BRepGraphInc::EdgeDef& anEdge = myGraph.Topo().Edges().Definition(anEdgeId); - if (anEdge.IsDegenerate) + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::FromNodeId(anEdgeExp.Current().DefId); + if (BRepGraph_Tool::Edge::Degenerated(myGraph, anEdgeId)) { continue; } - if (BRepGraph_Tool::Edge::FindPCurve(myGraph, anEdgeId, aFaceId) == nullptr) + if (!BRepGraph_Tool::Edge::FindPCurveCoEdgeId(myGraph, anEdgeId, aFaceId).IsValid()) { aMissing.Append(std::make_pair(anEdgeId, aFaceId)); } } } - EXPECT_EQ(aMissing.Length(), 0); + EXPECT_EQ(aMissing.Size(), 0); } TEST_F(BRepGraphTest, DetectDegenerateWires_ValidBox_Empty) @@ -1255,19 +471,19 @@ TEST_F(BRepGraphTest, DetectDegenerateWires_ValidBox_Empty) aFaceExp.Next()) { const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::FromNodeId(aFaceExp.Current().DefId); - if (myGraph.Topo().Faces().OuterWire(aFaceId) == aWireId) + if (BRepGraph_Tool::Face::OuterWire(myGraph, aFaceId) == aWireId) { isOuterWire = true; break; } } - if (isOuterWire && !aWireIt.Current().IsClosed) + if (isOuterWire && !BRepGraph_Tool::Wire::IsClosed(myGraph, aWireIt.CurrentId())) { aDegenerate.Append(aWireId); } } - EXPECT_EQ(aDegenerate.Length(), 0); + EXPECT_EQ(aDegenerate.Size(), 0); } TEST_F(BRepGraphTest, MutableEdge_ModifyTolerance) @@ -1298,45 +514,47 @@ TEST_F(BRepGraphTest, NbFacesOfEdge_SharedEdge) TEST_F(BRepGraphTest, FreeEdges_ClosedBox_Empty) { NCollection_DynamicArray aFree = collectFreeEdges(myGraph); - EXPECT_EQ(aFree.Length(), 0); + EXPECT_EQ(aFree.Size(), 0); } TEST_F(BRepGraphTest, RecordHistory_BasicEntry) { - size_t aBefore = myGraph.History().NbRecords(); - BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); - BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); - NCollection_DynamicArray aRepl; + size_t aBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); + BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); + BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); + NCollection_LinearVector aRepl; aRepl.Append(anEdge1); - myGraph.History().Record("TestOp", anEdge0, aRepl); - EXPECT_EQ(myGraph.History().NbRecords(), aBefore + 1); - EXPECT_TRUE(myGraph.History().Record(aBefore).OperationName.IsEqual("TestOp")); + myGraph.LayerRegistry().Ensure()->Record("TestOp", + anEdge0, + aRepl.ToArray1()); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aBefore + 1); + EXPECT_TRUE( + myGraph.LayerRegistry().Ensure()->Record(aBefore).OperationName.IsEqual( + "TestOp")); } TEST_F(BRepGraphTest, ReplaceEdge_Substitution) { // Get the first wire and its first edge via incidence refs. - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefs.Length(), 1); - const BRepGraphInc::CoEdgeRef& aOldCR = myGraph.Refs().CoEdges().Entry(aCoEdgeRefs.Value(0)); + const NCollection_LinearVector& aCoEdgeIds = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, BRepGraph_WireId::Start()); + ASSERT_GE(aCoEdgeIds.Size(), 1); const BRepGraphInc::CoEdgeDef& anOldCoEdge = - myGraph.Topo().CoEdges().Definition(aOldCR.CoEdgeDefId); - const BRepGraph_EdgeId anOldEdgeId = anOldCoEdge.EdgeDefId; + myGraph.Topo().CoEdges().Definition(aCoEdgeIds.Value(0)); + const BRepGraph_EdgeId anOldEdgeId = anOldCoEdge.ChildEdgeId; - // Pick a different edge to substitute. - const BRepGraph_EdgeId aNewEdgeId((anOldEdgeId.Index + 1) % myGraph.Topo().Edges().Nb()); + const BRepGraph_EdgeId aNewEdgeId = addCompatibleReplacementEdge(myGraph, anOldEdgeId, false); + ASSERT_TRUE(aNewEdgeId.IsValid()); myGraph.Editor().Wires().ReplaceEdge(BRepGraph_WireId::Start(), anOldEdgeId, aNewEdgeId, false); // Verify the substitution via the updated incidence refs. - const NCollection_DynamicArray aCoEdgeRefsAfter = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefsAfter.Length(), 1); - const BRepGraphInc::CoEdgeRef& aNewCR = myGraph.Refs().CoEdges().Entry(aCoEdgeRefsAfter.Value(0)); + const NCollection_LinearVector& aCoEdgeIdsAfter = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, BRepGraph_WireId::Start()); + ASSERT_GE(aCoEdgeIdsAfter.Size(), 1); const BRepGraphInc::CoEdgeDef& aNewCoEdge = - myGraph.Topo().CoEdges().Definition(aNewCR.CoEdgeDefId); - EXPECT_EQ(aNewCoEdge.EdgeDefId.Index, aNewEdgeId.Index); + myGraph.Topo().CoEdges().Definition(aCoEdgeIdsAfter.Value(0)); + EXPECT_EQ(aNewCoEdge.ChildEdgeId.Index, aNewEdgeId.Index); } TEST_F(BRepGraphTest, ParallelBuild_SameAsSequential) @@ -1346,15 +564,15 @@ TEST_F(BRepGraphTest, ParallelBuild_SameAsSequential) BRepGraph aSeqGraph; aSeqGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aSeqGraph, aBox, BRepGraph_Builder::Options{{}, true, false, false}); - ASSERT_TRUE(aSeqGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = + aSeqGraph.Shapes().Add(aBox, BRepGraph::ShapesView::Options{{}, true, false, false}); + ASSERT_FALSE(aSeqGraph.IsEmpty()); BRepGraph aParGraph; aParGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aParGraph, aBox, BRepGraph_Builder::Options{{}, true, false, true}); - ASSERT_TRUE(aParGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = + aParGraph.Shapes().Add(aBox, BRepGraph::ShapesView::Options{{}, true, false, true}); + ASSERT_FALSE(aParGraph.IsEmpty()); EXPECT_EQ(aParGraph.Topo().Solids().Nb(), aSeqGraph.Topo().Solids().Nb()); EXPECT_EQ(aParGraph.Topo().Shells().Nb(), aSeqGraph.Topo().Shells().Nb()); @@ -1386,9 +604,9 @@ TEST_F(BRepGraphTest, ParallelBuild_CompoundOfFaces) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aCompound, BRepGraph_Builder::Options{{}, true, false, true}); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = + aGraph.Shapes().Add(aCompound, BRepGraph::ShapesView::Options{{}, true, false, true}); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 12); } @@ -1400,9 +618,10 @@ TEST_F(BRepGraphTest, ReconstructFace_EachBoxFace_SameSubShapeCounts) { for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { - BRepGraph_NodeId aFaceId = BRepGraph_NodeId(aFaceIt.CurrentId()); - const TopoDS_Shape& anOrigFace = myGraph.Shapes().OriginalOf(aFaceId); - const TopoDS_Shape aReconstructed = myGraph.Shapes().Reconstruct(aFaceIt.CurrentId()); + BRepGraph_NodeId aFaceId = BRepGraph_NodeId(aFaceIt.CurrentId()); + const TopoDS_Shape anOrigFace = myGraph.Shapes().Original(aFaceId); + ASSERT_FALSE(anOrigFace.IsNull()); + const TopoDS_Shape aReconstructed = myGraph.Shapes().Reconstruct(aFaceIt.CurrentId()); NCollection_IndexedMap anOrigVerts, anOrigEdges, anOrigWires; @@ -1436,30 +655,26 @@ TEST_F(BRepGraphTest, ReconstructFace_EachBoxFace_SameSubShapeCounts) TEST_F(BRepGraphTest, ReconstructFace_AfterEdgeReplace_ContainsNewEdge) { - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefs.Length(), 1); - const BRepGraphInc::CoEdgeRef& aCR0 = myGraph.Refs().CoEdges().Entry(aCoEdgeRefs.Value(0)); - const BRepGraph_EdgeId anOldEdgeId = - myGraph.Topo().CoEdges().Definition(aCR0.CoEdgeDefId).EdgeDefId; + const NCollection_LinearVector& aCoEdgeIds = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, BRepGraph_WireId::Start()); + ASSERT_GE(aCoEdgeIds.Size(), 1); + const BRepGraph_EdgeId anOldEdgeId = + myGraph.Topo().CoEdges().Definition(aCoEdgeIds.Value(0)).ChildEdgeId; - // Pick a different edge. - const int aNewIdx = (anOldEdgeId.Index + 1) % myGraph.Topo().Edges().Nb(); - const BRepGraph_EdgeId aNewEdgeId(aNewIdx); + const BRepGraph_EdgeId aNewEdgeId = addCompatibleReplacementEdge(myGraph, anOldEdgeId, false); + ASSERT_TRUE(aNewEdgeId.IsValid()); // Get 3D curve handles from graph for old/new edges. - occ::handle aNewCurve = - BRepGraph_Tool::Edge::Curve(myGraph, BRepGraph_EdgeId(aNewIdx)); + occ::handle aNewCurve = BRepGraph_Tool::Edge::Curve(myGraph, aNewEdgeId); occ::handle anOldCurve = BRepGraph_Tool::Edge::Curve(myGraph, anOldEdgeId); myGraph.Editor().Wires().ReplaceEdge(BRepGraph_WireId::Start(), anOldEdgeId, aNewEdgeId, false); // Reconstruct face 0 (the face owning wire 0). - const int aFaceIdx = - BRepGraph_TestTools::FaceUsesWire(myGraph, BRepGraph_FaceId::Start(), BRepGraph_WireId::Start()) - ? 0 - : -1; - ASSERT_GE(aFaceIdx, 0); + ASSERT_TRUE(BRepGraph_TestTools::FaceUsesWire(myGraph, + BRepGraph_FaceId::Start(), + BRepGraph_WireId::Start())); + const uint32_t aFaceIdx = 0; const TopoDS_Shape aReconstructed = myGraph.Shapes().Reconstruct(BRepGraph_FaceId(aFaceIdx)); // Check via 3D curve handle identity (reconstructed edges have new TShapes). @@ -1499,9 +714,10 @@ TEST_F(BRepGraphTest, ReconstructShape_SolidRoot_SameFaceCount) TEST_F(BRepGraphTest, ReconstructShape_FaceRoot_ReturnsSameShape) { - BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); - const TopoDS_Shape aReconstructed = myGraph.Shapes().Reconstruct(aFaceId); - const TopoDS_Shape& anOriginal = myGraph.Shapes().OriginalOf(aFaceId); + BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); + const TopoDS_Shape aReconstructed = myGraph.Shapes().Reconstruct(aFaceId); + const TopoDS_Shape anOriginal = myGraph.Shapes().Original(aFaceId); + ASSERT_FALSE(anOriginal.IsNull()); // Reconstructed face should have the same surface handle. const TopoDS_Face& anOrigF = TopoDS::Face(anOriginal); @@ -1514,39 +730,42 @@ TEST_F(BRepGraphTest, ReconstructShape_FaceRoot_ReturnsSameShape) TEST_F(BRepGraphTest, Shape_Unmodified_ReturnsSameShape) { - BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); - TopoDS_Shape aShape = myGraph.Shapes().Shape(aFaceId); - const TopoDS_Shape& anOrig = myGraph.Shapes().OriginalOf(aFaceId); + BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); + TopoDS_Shape aShape = myGraph.Shapes().Shape(aFaceId); + const TopoDS_Shape anOrig = myGraph.Shapes().Original(aFaceId); + ASSERT_FALSE(anOrig.IsNull()); EXPECT_TRUE(aShape.IsSame(anOrig)); } TEST_F(BRepGraphTest, Shape_AfterReplaceEdge_DiffersFromOriginal) { - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefs.Length(), 1); - const BRepGraphInc::CoEdgeRef& aCR0 = myGraph.Refs().CoEdges().Entry(aCoEdgeRefs.Value(0)); - const BRepGraph_EdgeId anOldEdgeId = - myGraph.Topo().CoEdges().Definition(aCR0.CoEdgeDefId).EdgeDefId; - const int aNewIdx = (anOldEdgeId.Index + 1) % myGraph.Topo().Edges().Nb(); - const BRepGraph_EdgeId aNewEdgeId(aNewIdx); + const NCollection_LinearVector& aCoEdgeIds = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, BRepGraph_WireId::Start()); + ASSERT_GE(aCoEdgeIds.Size(), 1); + const BRepGraph_EdgeId anOldEdgeId = + myGraph.Topo().CoEdges().Definition(aCoEdgeIds.Value(0)).ChildEdgeId; + const BRepGraph_EdgeId aNewEdgeId = addCompatibleReplacementEdge(myGraph, anOldEdgeId, false); + ASSERT_TRUE(aNewEdgeId.IsValid()); myGraph.Editor().Wires().ReplaceEdge(BRepGraph_WireId::Start(), anOldEdgeId, aNewEdgeId, false); // Find the face that owns wire 0. - int aFaceDefIdx = -1; + uint32_t aFaceIdx = 0; + bool isFaceFound = false; for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { if (BRepGraph_TestTools::FaceUsesWire(myGraph, aFaceIt.CurrentId(), BRepGraph_WireId::Start())) { - aFaceDefIdx = aFaceIt.CurrentId().Index; + aFaceIdx = aFaceIt.CurrentId().Index; + isFaceFound = true; break; } } - ASSERT_GE(aFaceDefIdx, 0); - BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, aFaceDefIdx); - TopoDS_Shape aShape = myGraph.Shapes().Shape(aFaceId); - const TopoDS_Shape& anOrig = myGraph.Shapes().OriginalOf(aFaceId); + ASSERT_TRUE(isFaceFound); + BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, aFaceIdx); + TopoDS_Shape aShape = myGraph.Shapes().Shape(aFaceId); + const TopoDS_Shape anOrig = myGraph.Shapes().Original(aFaceId); + ASSERT_FALSE(anOrig.IsNull()); EXPECT_FALSE(aShape.IsSame(anOrig)); } @@ -1587,15 +806,15 @@ TEST_F(BRepGraphTest, OwnGen_MutableEdge_PropagatesSubtreeGenUp) if (BRepGraph_EdgeId::Start().IsValid(myGraph.Topo().Edges().Nb())) { // Find a wire containing this edge. - const NCollection_DynamicArray& aWires = - myGraph.Topo().Edges().Wires(BRepGraph_EdgeId::Start()); - if (aWires.Length() > 0) + BRepGraph_WiresOfEdge aWireIt = myGraph.Topo().Edges().WiresOf(BRepGraph_EdgeId::Start()); + if (aWireIt.More()) { - EXPECT_GT(myGraph.Topo().Wires().Definition(aWires.Value(0)).SubtreeGen, 0u); + const BRepGraph_WireId aWireId = aWireIt.CurrentId(); + EXPECT_GT(myGraph.Topo().Wires().Definition(aWireId).SubtreeGen, 0u); // Check propagation to owning face. for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { - if (BRepGraph_TestTools::FaceUsesWire(myGraph, aFaceIt.CurrentId(), aWires.Value(0))) + if (BRepGraph_TestTools::FaceUsesWire(myGraph, aFaceIt.CurrentId(), aWireId)) { EXPECT_GT(myGraph.Topo().Faces().Definition(aFaceIt.CurrentId()).SubtreeGen, 0u); break; @@ -1620,14 +839,14 @@ TEST_F(BRepGraphTest, HasOriginalShape_AfterBuild_True) BRepGraph_NodeId aFaceId(aFaceIt.CurrentId()); EXPECT_TRUE(myGraph.Shapes().HasOriginal(aFaceId)) << "Face " << aFaceIt.CurrentId().Index - << " should have original shape after BRepGraph_Builder::Add()"; + << " should have original shape after BRepGraph::ShapesView::Add()"; } for (BRepGraph_EdgeIterator anEdgeIt(myGraph); anEdgeIt.More(); anEdgeIt.Next()) { BRepGraph_NodeId anEdgeId(anEdgeIt.CurrentId()); EXPECT_TRUE(myGraph.Shapes().HasOriginal(anEdgeId)) << "Edge " << anEdgeIt.CurrentId().Index - << " should have original shape after BRepGraph_Builder::Add()"; + << " should have original shape after BRepGraph::ShapesView::Add()"; } } @@ -1659,10 +878,10 @@ TEST_F(BRepGraphTest, DefaultBuild_AssignsValidUIDs) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); const BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); @@ -1680,13 +899,13 @@ TEST_F(BRepGraphTest, UIDsGeneration_IncrementsAcrossBuilds) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, aBoxMaker1.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(aBoxMaker1.Shape()); const uint32_t aGeneration1 = aGraph.UIDs().Generation(); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aBoxMaker2.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = + aGraph.Shapes().Add(aBoxMaker2.Shape()); const uint32_t aGeneration2 = aGraph.UIDs().Generation(); EXPECT_GT(aGeneration1, 0u); @@ -1700,8 +919,8 @@ TEST_F(BRepGraphTest, StaleUID_HasReturnsFalseAfterRebuild) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, aBoxMaker1.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = + aGraph.Shapes().Add(aBoxMaker1.Shape()); ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); const BRepGraph_UID anOldUID = aGraph.UIDs().Of(BRepGraph_NodeId(BRepGraph_NodeId::Kind::Face, 0)); @@ -1709,8 +928,8 @@ TEST_F(BRepGraphTest, StaleUID_HasReturnsFalseAfterRebuild) ASSERT_TRUE(aGraph.UIDs().Has(anOldUID)); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aBoxMaker2.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = + aGraph.Shapes().Add(aBoxMaker2.Shape()); EXPECT_FALSE(aGraph.UIDs().Has(anOldUID)); EXPECT_FALSE(aGraph.UIDs().NodeIdFrom(anOldUID).IsValid()); @@ -1743,30 +962,45 @@ TEST(BRepGraph_UIDsViewTest, ReverseLookupStaysCurrentAfterProgrammaticAdd) TEST_F(BRepGraphTest, RecordHistory_MultipleRecords_SequenceNumbers) { - const size_t aBefore = myGraph.History().NbRecords(); + const size_t aBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); - NCollection_DynamicArray aRepl; + NCollection_LinearVector aRepl; aRepl.Append(anEdge1); - myGraph.History().Record("OpA", anEdge0, aRepl); - myGraph.History().Record("OpB", anEdge0, aRepl); - myGraph.History().Record("OpC", anEdge0, aRepl); + myGraph.LayerRegistry().Ensure()->Record("OpA", + anEdge0, + aRepl.ToArray1()); + myGraph.LayerRegistry().Ensure()->Record("OpB", + anEdge0, + aRepl.ToArray1()); + myGraph.LayerRegistry().Ensure()->Record("OpC", + anEdge0, + aRepl.ToArray1()); - EXPECT_EQ(myGraph.History().NbRecords(), aBefore + 3); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aBefore + 3); // Check monotonically increasing sequence numbers. for (size_t anIdx = aBefore + 1; anIdx < aBefore + 3; ++anIdx) { - EXPECT_GT(myGraph.History().Record(anIdx).SequenceNumber, - myGraph.History().Record(anIdx - 1).SequenceNumber) + EXPECT_GT( + myGraph.LayerRegistry().Ensure()->Record(anIdx).SequenceNumber, + myGraph.LayerRegistry().Ensure()->Record(anIdx - 1).SequenceNumber) << "SequenceNumber not monotonically increasing at index " << anIdx; } - EXPECT_TRUE(myGraph.History().Record(aBefore).OperationName.IsEqual("OpA")); - EXPECT_TRUE(myGraph.History().Record(aBefore + 1).OperationName.IsEqual("OpB")); - EXPECT_TRUE(myGraph.History().Record(aBefore + 2).OperationName.IsEqual("OpC")); + EXPECT_TRUE( + myGraph.LayerRegistry().Ensure()->Record(aBefore).OperationName.IsEqual( + "OpA")); + EXPECT_TRUE(myGraph.LayerRegistry() + .Ensure() + ->Record(aBefore + 1) + .OperationName.IsEqual("OpB")); + EXPECT_TRUE(myGraph.LayerRegistry() + .Ensure() + ->Record(aBefore + 2) + .OperationName.IsEqual("OpC")); } TEST_F(BRepGraphTest, FindOriginal_SingleHop_ReturnsSource) @@ -1775,14 +1009,15 @@ TEST_F(BRepGraphTest, FindOriginal_SingleHop_ReturnsSource) BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); auto aModifier = [&](BRepGraph& /*theGraph*/, BRepGraph_NodeId /*theTarget*/) { - NCollection_DynamicArray aResult; + NCollection_LinearVector aResult; aResult.Append(anEdge1); return aResult; }; myGraph.Editor().Gen().ApplyModification(anEdge0, aModifier, "TestHop"); - BRepGraph_NodeId anOriginal = myGraph.History().FindOriginal(anEdge1); + BRepGraph_NodeId anOriginal = + myGraph.LayerRegistry().Ensure()->FindOriginal(anEdge1); EXPECT_EQ(anOriginal, anEdge0); } @@ -1792,15 +1027,16 @@ TEST_F(BRepGraphTest, FindDerived_SingleHop_ContainsTarget) BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); auto aModifier = [&](BRepGraph& /*theGraph*/, BRepGraph_NodeId /*theTarget*/) { - NCollection_DynamicArray aResult; + NCollection_LinearVector aResult; aResult.Append(anEdge1); return aResult; }; myGraph.Editor().Gen().ApplyModification(anEdge0, aModifier, "TestHop"); - NCollection_DynamicArray aDerived = myGraph.History().FindDerived(anEdge0); - bool isFound = false; + NCollection_LinearVector aDerived = + myGraph.LayerRegistry().Ensure()->FindDerived(anEdge0); + bool isFound = false; for (const BRepGraph_NodeId& aDerivedId : aDerived) { if (aDerivedId == anEdge1) @@ -1820,7 +1056,7 @@ TEST_F(BRepGraphTest, ApplyModification_MultiStepChain_FindOriginalTracesBack) // Step 1: edge0 -> edge1 auto aModifier1 = [&](BRepGraph& /*theGraph*/, BRepGraph_NodeId /*theTarget*/) { - NCollection_DynamicArray aResult; + NCollection_LinearVector aResult; aResult.Append(anEdge1); return aResult; }; @@ -1828,20 +1064,22 @@ TEST_F(BRepGraphTest, ApplyModification_MultiStepChain_FindOriginalTracesBack) // Step 2: edge1 -> edge2 auto aModifier2 = [&](BRepGraph& /*theGraph*/, BRepGraph_NodeId /*theTarget*/) { - NCollection_DynamicArray aResult; + NCollection_LinearVector aResult; aResult.Append(anEdge2); return aResult; }; myGraph.Editor().Gen().ApplyModification(anEdge1, aModifier2, "Step2"); // FindOriginal from edge2 should trace back to edge0. - BRepGraph_NodeId anOriginal = myGraph.History().FindOriginal(anEdge2); + BRepGraph_NodeId anOriginal = + myGraph.LayerRegistry().Ensure()->FindOriginal(anEdge2); EXPECT_EQ(anOriginal, anEdge0); // FindDerived from edge0 returns leaf-only transitive descendants. // edge1 is an intermediate (it has further derived edge2), so only edge2 is returned. - NCollection_DynamicArray aDerived = myGraph.History().FindDerived(anEdge0); - bool isEdge2Found = false; + NCollection_LinearVector aDerived = + myGraph.LayerRegistry().Ensure()->FindDerived(anEdge0); + bool isEdge2Found = false; for (const BRepGraph_NodeId& aDerivedId : aDerived) { if (aDerivedId == anEdge2) @@ -1852,8 +1090,9 @@ TEST_F(BRepGraphTest, ApplyModification_MultiStepChain_FindOriginalTracesBack) EXPECT_TRUE(isEdge2Found) << "Edge2 not found in FindDerived(Edge0)"; // edge1 can be found by querying FindDerived on the intermediate step. - NCollection_DynamicArray aDerived1 = myGraph.History().FindDerived(anEdge1); - bool isEdge2FromEdge1 = false; + NCollection_LinearVector aDerived1 = + myGraph.LayerRegistry().Ensure()->FindDerived(anEdge1); + bool isEdge2FromEdge1 = false; for (const BRepGraph_NodeId& aDerivedId : aDerived1) { if (aDerivedId == anEdge2) @@ -1868,48 +1107,56 @@ TEST_F(BRepGraphTest, ApplyModification_MultiStepChain_FindOriginalTracesBack) // Group 3: Mutation APIs // =================================================================== -TEST_F(BRepGraphTest, AddPCurve_NewPCurve_RetrievableViaFindPCurve) +TEST_F(BRepGraphTest, AddPCurve_NewPCurve_RetrievableViaFindPCurveCoEdgeId) { BRepGraph_EdgeId anEdgeId(0); BRepGraph_FaceId aFaceId(0); occ::handle aCurve2d = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0)); - myGraph.Editor().CoEdges().AddPCurve(anEdgeId, aFaceId, aCurve2d, 0.0, 1.0); + std::ignore = myGraph.Editor().CoEdges().Add(anEdgeId, aFaceId, aCurve2d, 0.0, 1.0); - const BRepGraphInc::CoEdgeDef* aRetrieved = - BRepGraph_Tool::Edge::FindPCurve(myGraph, anEdgeId, aFaceId); - EXPECT_NE(aRetrieved, nullptr); - if (aRetrieved != nullptr) + const BRepGraph_CoEdgeId aRetrievedId = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(myGraph, anEdgeId, aFaceId); + EXPECT_TRUE(aRetrievedId.IsValid()); + if (aRetrievedId.IsValid()) { - EXPECT_TRUE(aRetrieved->Curve2DRepId.IsValid()); + EXPECT_TRUE(myGraph.Topo().CoEdges().Definition(aRetrievedId).Curve2DRepId.IsValid()); + bool isListedByEdge = false; + for (const BRepGraph_CoEdgeId& aCoEdgeId : myGraph.Topo().Edges().CoEdges(anEdgeId)) + { + if (aCoEdgeId == aRetrievedId) + { + isListedByEdge = true; + break; + } + } + EXPECT_TRUE(isListedByEdge) << "Created face-bound coedge must be present in edge relations"; } + EXPECT_TRUE(myGraph.ValidateRelations()); } TEST_F(BRepGraphTest, ReplaceEdge_Reversed_OrientationFlipped) { - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefs.Length(), 1); + const NCollection_LinearVector& aCoEdgeIds = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, BRepGraph_WireId::Start()); + ASSERT_GE(aCoEdgeIds.Size(), 1); - const BRepGraphInc::CoEdgeRef& anOrigCR = myGraph.Refs().CoEdges().Entry(aCoEdgeRefs.Value(0)); const BRepGraphInc::CoEdgeDef& anOrigCoEdge = - myGraph.Topo().CoEdges().Definition(anOrigCR.CoEdgeDefId); - const BRepGraph_EdgeId anOldEdgeId = anOrigCoEdge.EdgeDefId; + myGraph.Topo().CoEdges().Definition(aCoEdgeIds.Value(0)); + const BRepGraph_EdgeId anOldEdgeId = anOrigCoEdge.ChildEdgeId; TopAbs_Orientation anOrigOrientation = anOrigCoEdge.Orientation; - // Pick a different edge. - const int aNewIdx = (anOldEdgeId.Index + 1) % myGraph.Topo().Edges().Nb(); - const BRepGraph_EdgeId aNewEdgeId(aNewIdx); + const BRepGraph_EdgeId aNewEdgeId = addCompatibleReplacementEdge(myGraph, anOldEdgeId, true); + ASSERT_TRUE(aNewEdgeId.IsValid()); myGraph.Editor().Wires().ReplaceEdge(BRepGraph_WireId::Start(), anOldEdgeId, aNewEdgeId, true); - const NCollection_DynamicArray aCoEdgeRefsAfter = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefsAfter.Length(), 1); - const BRepGraphInc::CoEdgeRef& aNewCR = myGraph.Refs().CoEdges().Entry(aCoEdgeRefsAfter.Value(0)); + const NCollection_LinearVector& aCoEdgeIdsAfter = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, BRepGraph_WireId::Start()); + ASSERT_GE(aCoEdgeIdsAfter.Size(), 1); const BRepGraphInc::CoEdgeDef& aNewCoEdge = - myGraph.Topo().CoEdges().Definition(aNewCR.CoEdgeDefId); - EXPECT_EQ(aNewCoEdge.EdgeDefId.Index, aNewEdgeId.Index); + myGraph.Topo().CoEdges().Definition(aCoEdgeIdsAfter.Value(0)); + EXPECT_EQ(aNewCoEdge.ChildEdgeId.Index, aNewEdgeId.Index); // Orientation should be flipped relative to original. TopAbs_Orientation anExpected = @@ -1917,19 +1164,19 @@ TEST_F(BRepGraphTest, ReplaceEdge_Reversed_OrientationFlipped) EXPECT_EQ(aNewCoEdge.Orientation, anExpected); } -TEST_F(BRepGraphTest, ReplaceEdge_UpdatesEdgeToCoEdgeReverseIndex) +TEST_F(BRepGraphTest, ReplaceEdge_UpdatesEdgeToCoEdgeRelations) { - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefs.Length(), 1); + const NCollection_LinearVector& aCoEdgeIds = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, BRepGraph_WireId::Start()); + ASSERT_GE(aCoEdgeIds.Size(), 1); - const BRepGraphInc::CoEdgeRef& aRef = myGraph.Refs().CoEdges().Entry(aCoEdgeRefs.Value(0)); - const BRepGraph_CoEdgeId aCoEdgeId = aRef.CoEdgeDefId; - const BRepGraph_EdgeId anOldEdgeId = myGraph.Topo().CoEdges().Definition(aCoEdgeId).EdgeDefId; - const BRepGraph_EdgeId aNewEdgeId((anOldEdgeId.Index + 1) % myGraph.Topo().Edges().Nb()); + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIds.Value(0); + const BRepGraph_EdgeId anOldEdgeId = myGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId; + const BRepGraph_EdgeId aNewEdgeId = addCompatibleReplacementEdge(myGraph, anOldEdgeId, false); + ASSERT_TRUE(aNewEdgeId.IsValid()); auto hasCoEdge = [&](const BRepGraph_EdgeId theEdgeId) { - const NCollection_DynamicArray& aCoEdges = + const NCollection_LinearVector& aCoEdges = myGraph.Topo().Edges().CoEdges(theEdgeId); for (const BRepGraph_CoEdgeId& aCoEdge : aCoEdges) { @@ -1949,26 +1196,24 @@ TEST_F(BRepGraphTest, ReplaceEdge_UpdatesEdgeToCoEdgeReverseIndex) EXPECT_TRUE(hasCoEdge(aNewEdgeId)); } -TEST_F(BRepGraphTest, RemoveCoEdge_PrunesOrphanAndKeepsReverseIndexConsistent) +TEST_F(BRepGraphTest, RemoveCoEdge_PrunesOrphanAndKeepsRelationsConsistent) { - const NCollection_DynamicArray aCoEdgeRefsBefore = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefsBefore.Length(), 1); + const NCollection_LinearVector aCoEdgeIdsBefore = + myGraph.Topo().Wires().Relations(BRepGraph_WireId::Start()).CoEdgeIds; + ASSERT_GE(aCoEdgeIdsBefore.Size(), 1); - const BRepGraphInc::CoEdgeRef& aRef = myGraph.Refs().CoEdges().Entry(aCoEdgeRefsBefore.Value(0)); - const BRepGraph_CoEdgeRefId aCoEdgeRefId = aCoEdgeRefsBefore.Value(0); - const BRepGraph_CoEdgeId aCoEdgeId = aRef.CoEdgeDefId; - const BRepGraph_EdgeId anEdgeId = myGraph.Topo().CoEdges().Definition(aCoEdgeId).EdgeDefId; + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIdsBefore.Value(0); + const BRepGraph_EdgeId anEdgeId = myGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId; - ASSERT_TRUE(myGraph.Editor().Wires().RemoveCoEdge(BRepGraph_WireId::Start(), aCoEdgeRefId)); + ASSERT_TRUE(myGraph.Editor().Wires().RemoveCoEdge(BRepGraph_WireId::Start(), aCoEdgeId)); - const NCollection_DynamicArray aCoEdgeRefsAfter = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - EXPECT_EQ(aCoEdgeRefsAfter.Length(), aCoEdgeRefsBefore.Length() - 1); - EXPECT_TRUE(myGraph.Topo().CoEdges().Definition(aCoEdgeId).IsRemoved); + const NCollection_LinearVector aCoEdgeIdsAfter = + myGraph.Topo().Wires().Relations(BRepGraph_WireId::Start()).CoEdgeIds; + EXPECT_EQ(aCoEdgeIdsAfter.Size(), aCoEdgeIdsBefore.Size() - 1); + EXPECT_TRUE(aCoEdgeId.IsRemoved(myGraph)); EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); - for (const BRepGraph_WireId& aWireId : myGraph.Topo().Edges().Wires(anEdgeId)) + for (const BRepGraph_WireId& aWireId : myGraph.Topo().Edges().WiresOf(anEdgeId)) { EXPECT_NE(aWireId, BRepGraph_WireId::Start()); } @@ -1979,134 +1224,186 @@ TEST_F(BRepGraphTest, RemoveCoEdge_PrunesOrphanAndKeepsReverseIndexConsistent) } } -TEST_F(BRepGraphTest, RemoveChild_PreservesSharedChildAndRebuildsReverseIndex) +TEST_F(BRepGraphTest, CleanupRemovedRefs_ManuallyRemovedEdge_UnbindsEdgeWireRelations) { - NCollection_DynamicArray aChildren; + const BRepGraph_WireId aWireId = BRepGraph_WireId::Start(); + const NCollection_LinearVector& aCoEdgeIds = + myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds; + ASSERT_GE(aCoEdgeIds.Size(), 1); + + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIds.Value(0); + const BRepGraph_EdgeId anEdgeId = myGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId; + ASSERT_TRUE(containsId(myGraph.Topo().Edges().WiresOf(anEdgeId), aWireId)); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(anEdgeId)); + + myGraph.Editor().Gen().CleanupRemovedReferences(); + + EXPECT_TRUE(aCoEdgeId.IsRemoved(myGraph)); + EXPECT_FALSE(containsId(myGraph.Topo().Edges().WiresOf(anEdgeId), aWireId)); + EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); +} + +TEST_F(BRepGraphTest, RemoveChild_PreservesSharedChildAndKeepsRelationsConsistent) +{ + NCollection_LinearVector aChildren; aChildren.Append(BRepGraph_NodeId(BRepGraph_SolidId::Start())); - const BRepGraph_CompoundId aCompoundId = myGraph.Editor().Compounds().Add(aChildren); + const BRepGraph_CompoundId aCompoundId = myGraph.Editor().Compounds().Add(aChildren.ToArray1()); ASSERT_TRUE(aCompoundId.IsValid()); - const NCollection_DynamicArray aChildRefsBefore = + const NCollection_LinearVector& aChildRefsBefore = BRepGraph_TestTools::ChildRefsOfParent(myGraph, BRepGraph_NodeId(aCompoundId)); - ASSERT_EQ(aChildRefsBefore.Length(), 1); + ASSERT_EQ(aChildRefsBefore.Size(), 1); const BRepGraph_ChildRefId aChildRefId = aChildRefsBefore.Value(0); const BRepGraphInc::ChildRef& aRef = myGraph.Refs().Children().Entry(aChildRefId); - const BRepGraph_NodeId aChildId = aRef.ChildDefId; + const BRepGraph_NodeId aChildId = aRef.ChildNodeId; ASSERT_TRUE(myGraph.Editor().Compounds().RemoveChild(aCompoundId, aChildRefId)); EXPECT_EQ(BRepGraph_TestTools::CountChildRefsOfParent(myGraph, BRepGraph_NodeId(aCompoundId)), 0); EXPECT_FALSE(myGraph.Topo().Gen().IsRemoved(aChildId)); EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); - EXPECT_EQ(myGraph.Topo().Compounds().ParentCompounds(aCompoundId).Length(), 0); + EXPECT_EQ(myGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aCompoundId)).Size(), 0); } -TEST_F(BRepGraphTest, RemoveChild_RemovesAuxChildUsage) +TEST_F(BRepGraphTest, RemoveChild_UsesDistinctRefsForSharedChild) { - const BRepGraph_ChildRefId aChildRefId = - myGraph.Editor().Shells().AddChild(BRepGraph_ShellId::Start(), - BRepGraph_NodeId(BRepGraph_WireId::Start())); - ASSERT_TRUE(aChildRefId.IsValid()); - ASSERT_EQ(myGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).AuxChildRefIds.Length(), + ASSERT_GT(myGraph.Topo().Solids().Nb(), 0); + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_NodeId(aSolidId)); + const BRepGraph_CompoundId aFirstCompound = + myGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aFirstCompound.IsValid()); + + NCollection_LinearVector anEmptyChildren; + const BRepGraph_CompoundId aSecondCompound = + myGraph.Editor().Compounds().Add(anEmptyChildren.ToArray1()); + ASSERT_TRUE(aSecondCompound.IsValid()); + + const NCollection_LinearVector& aFirstRefs = + BRepGraph_TestTools::ChildRefsOfParent(myGraph, BRepGraph_NodeId(aFirstCompound)); + ASSERT_EQ(aFirstRefs.Size(), 1); + const BRepGraph_ChildRefId aFirstRefId = aFirstRefs.Value(0); + + const BRepGraph_ChildRefId aSecondRefId = + myGraph.Editor().Compounds().Append(aSecondCompound, BRepGraph_NodeId(aSolidId)); + ASSERT_TRUE(aSecondRefId.IsValid()); + ASSERT_NE(aFirstRefId, aSecondRefId); + myGraph.Editor().Gen().CleanupRemovedReferences(); + + const BRepGraph_Validate::Result aValidDistinctRefs = + BRepGraph_Validate::Perform(myGraph, BRepGraph_Validate::Options::Audit()); + ASSERT_TRUE(aValidDistinctRefs.IsValid()); + BRepGraph_CompoundsOfChild aCompoundsBefore( + myGraph, + myGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aSolidId))); + EXPECT_TRUE(containsId(aCompoundsBefore, aFirstCompound)); + EXPECT_TRUE(containsId(aCompoundsBefore, aSecondCompound)); + + ASSERT_TRUE(myGraph.Editor().Compounds().RemoveChild(aFirstCompound, aFirstRefId)); + EXPECT_TRUE(aFirstRefId.IsRemoved(myGraph)); + EXPECT_FALSE(aSecondRefId.IsRemoved(myGraph)); + BRepGraph_CompoundsOfChild aCompoundsAfterFirstRemove( + myGraph, + myGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aSolidId))); + EXPECT_FALSE(containsId(aCompoundsAfterFirstRemove, aFirstCompound)); + EXPECT_TRUE(containsId(aCompoundsAfterFirstRemove, aSecondCompound)); + EXPECT_EQ(BRepGraph_TestTools::CountChildRefsOfParent(myGraph, BRepGraph_NodeId(aFirstCompound)), + 0); + EXPECT_EQ(BRepGraph_TestTools::CountChildRefsOfParent(myGraph, BRepGraph_NodeId(aSecondCompound)), 1); - ASSERT_TRUE(myGraph.Editor().Shells().RemoveChild(BRepGraph_ShellId::Start(), aChildRefId)); - - EXPECT_EQ(myGraph.Topo().Shells().Definition(BRepGraph_ShellId::Start()).AuxChildRefIds.Length(), - 0); - EXPECT_FALSE(myGraph.Topo().Gen().IsRemoved(BRepGraph_WireId::Start())); + ASSERT_TRUE(myGraph.Editor().Compounds().RemoveChild(aSecondCompound, aSecondRefId)); + EXPECT_TRUE(aSecondRefId.IsRemoved(myGraph)); + BRepGraph_CompoundsOfChild aCompoundsAfterSecondRemove( + myGraph, + myGraph.Topo().Gen().CompoundRefIds(BRepGraph_NodeId(aSolidId))); + EXPECT_FALSE(containsId(aCompoundsAfterSecondRemove, aSecondCompound)); EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); } -TEST_F(BRepGraphTest, RemoveChild_RemovesAuxChildUsageFromSolid) +TEST_F(BRepGraphTest, RemoveFace_PrunesOrphanAndKeepsRelationsConsistent) { - const BRepGraph_ChildRefId aChildRefId = - myGraph.Editor().Solids().AddChild(BRepGraph_SolidId::Start(), - BRepGraph_NodeId(BRepGraph_EdgeId::Start())); - ASSERT_TRUE(aChildRefId.IsValid()); - ASSERT_EQ(myGraph.Topo().Solids().Definition(BRepGraph_SolidId::Start()).AuxChildRefIds.Length(), - 1); - - ASSERT_TRUE(myGraph.Editor().Solids().RemoveChild(BRepGraph_SolidId::Start(), aChildRefId)); - - EXPECT_EQ(myGraph.Topo().Solids().Definition(BRepGraph_SolidId::Start()).AuxChildRefIds.Length(), - 0); - EXPECT_FALSE(myGraph.Topo().Gen().IsRemoved(BRepGraph_EdgeId::Start())); - EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); -} - -TEST_F(BRepGraphTest, RemoveFace_PrunesOrphanAndRebuildsReverseIndex) -{ - const NCollection_DynamicArray aFaceRefsBefore = + const NCollection_LinearVector& aFaceRefsBefore = BRepGraph_TestTools::FaceRefsOfShell(myGraph, BRepGraph_ShellId::Start()); - ASSERT_GE(aFaceRefsBefore.Length(), 1); + ASSERT_GE(aFaceRefsBefore.Size(), 1); + const size_t aNbFaceRefsBefore = aFaceRefsBefore.Size(); const BRepGraph_FaceRefId aFaceRefId = aFaceRefsBefore.Value(0); const BRepGraphInc::FaceRef& aRef = myGraph.Refs().Faces().Entry(aFaceRefId); - const BRepGraph_FaceId aFaceId = aRef.FaceDefId; + const BRepGraph_FaceId aFaceId = aRef.ChildFaceId; ASSERT_TRUE(myGraph.Editor().Shells().RemoveFace(BRepGraph_ShellId::Start(), aFaceRefId)); EXPECT_EQ(BRepGraph_TestTools::CountFaceRefsOfShell(myGraph, BRepGraph_ShellId::Start()), - aFaceRefsBefore.Length() - 1); - EXPECT_TRUE(myGraph.Topo().Faces().Definition(aFaceId).IsRemoved); + aNbFaceRefsBefore - 1); + EXPECT_TRUE(aFaceId.IsRemoved(myGraph)); EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); - for (const BRepGraph_SolidId& aSolidId : - myGraph.Topo().Shells().Solids(BRepGraph_ShellId::Start())) + for (const BRepGraph_SolidId& aSolidId : BRepGraph_SolidsOfShell( + myGraph, + myGraph.Topo().Shells().Relations(BRepGraph_ShellId::Start()).ParentShellRefIds)) { EXPECT_NE(aSolidId, BRepGraph_SolidId()); } - for (const BRepGraph_ShellId& aShellId : myGraph.Topo().Faces().Shells(aFaceId)) + for (const BRepGraph_ShellId& aShellId : + BRepGraph_ShellsOfFace(myGraph, myGraph.Topo().Faces().Relations(aFaceId).ParentFaceRefIds)) { EXPECT_NE(aShellId, BRepGraph_ShellId::Start()); } } -TEST_F(BRepGraphTest, RemoveShell_PrunesOrphanAndRebuildsReverseIndex) +TEST_F(BRepGraphTest, RemoveShell_PrunesOrphanAndKeepsRelationsConsistent) { - const NCollection_DynamicArray aShellRefsBefore = + const NCollection_LinearVector& aShellRefsBefore = BRepGraph_TestTools::ShellRefsOfSolid(myGraph, BRepGraph_SolidId::Start()); - ASSERT_GE(aShellRefsBefore.Length(), 1); + ASSERT_GE(aShellRefsBefore.Size(), 1); + const size_t aNbShellRefsBefore = aShellRefsBefore.Size(); const BRepGraph_ShellRefId aShellRefId = aShellRefsBefore.Value(0); const BRepGraphInc::ShellRef& aRef = myGraph.Refs().Shells().Entry(aShellRefId); - const BRepGraph_ShellId aShellId = aRef.ShellDefId; + const BRepGraph_ShellId aShellId = aRef.ChildShellId; ASSERT_TRUE(myGraph.Editor().Solids().RemoveShell(BRepGraph_SolidId::Start(), aShellRefId)); EXPECT_EQ(BRepGraph_TestTools::CountShellRefsOfSolid(myGraph, BRepGraph_SolidId::Start()), - aShellRefsBefore.Length() - 1); - EXPECT_TRUE(myGraph.Topo().Shells().Definition(aShellId).IsRemoved); + aNbShellRefsBefore - 1); + EXPECT_TRUE(aShellId.IsRemoved(myGraph)); EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); - for (const BRepGraph_CompSolidId& aCompSolidId : - myGraph.Topo().Solids().CompSolids(BRepGraph_SolidId::Start())) + for (const BRepGraph_CompSolidId& aCompSolidId : BRepGraph_CompSolidsOfSolid( + myGraph, + myGraph.Topo().Solids().Relations(BRepGraph_SolidId::Start()).ParentSolidRefIds)) { EXPECT_NE(aCompSolidId, BRepGraph_CompSolidId()); } - for (const BRepGraph_SolidId& aSolidId : myGraph.Topo().Shells().Solids(aShellId)) + for (const BRepGraph_SolidId& aSolidId : + BRepGraph_SolidsOfShell(myGraph, + myGraph.Topo().Shells().Relations(aShellId).ParentShellRefIds)) { EXPECT_NE(aSolidId, BRepGraph_SolidId::Start()); } } -TEST_F(BRepGraphTest, RemoveWire_PrunesOrphanAndRebuildsReverseIndex) +TEST_F(BRepGraphTest, RemoveWire_PrunesOrphanAndKeepsRelationsConsistent) { BRepGraph_FaceId aFaceId; BRepGraph_WireRefId aWireRefId; bool isFound = false; for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More() && !isFound; aFaceIt.Next()) { - const BRepGraph_FaceId aCandidateFaceId = aFaceIt.CurrentId(); - const NCollection_DynamicArray aWireRefs = + const BRepGraph_FaceId aCandidateFaceId = aFaceIt.CurrentId(); + const NCollection_LinearVector& aWireRefs = BRepGraph_TestTools::WireRefsOfFace(myGraph, aCandidateFaceId); for (const BRepGraph_WireRefId& aCandidateWireRefId : aWireRefs) { - if (myGraph.Refs().Wires().Entry(aCandidateWireRefId).WireDefId == BRepGraph_WireId::Start()) + if (myGraph.Refs().Wires().Entry(aCandidateWireRefId).ChildWireId + == BRepGraph_WireId::Start()) { aFaceId = aCandidateFaceId; aWireRefId = aCandidateWireRefId; @@ -2117,56 +1414,41 @@ TEST_F(BRepGraphTest, RemoveWire_PrunesOrphanAndRebuildsReverseIndex) } ASSERT_TRUE(isFound); - const int aNbWireRefsBefore = BRepGraph_TestTools::CountWireRefsOfFace(myGraph, aFaceId); + const size_t aNbWireRefsBefore = BRepGraph_TestTools::CountWireRefsOfFace(myGraph, aFaceId); ASSERT_TRUE(myGraph.Editor().Faces().RemoveWire(aFaceId, aWireRefId)); EXPECT_EQ(BRepGraph_TestTools::CountWireRefsOfFace(myGraph, aFaceId), aNbWireRefsBefore - 1); EXPECT_FALSE(BRepGraph_TestTools::FaceUsesWire(myGraph, aFaceId, BRepGraph_WireId::Start())); - EXPECT_TRUE(myGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).IsRemoved); + EXPECT_TRUE(BRepGraph_WireId::Start().IsRemoved(myGraph)); EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); - for (const BRepGraph_FaceId& aWireFaceId : - myGraph.Topo().Wires().Faces(BRepGraph_WireId::Start())) + for (const BRepGraph_FaceId& aWireFaceId : BRepGraph_FacesOfWire( + myGraph, + myGraph.Topo().Wires().Relations(BRepGraph_WireId::Start()).ParentWireRefIds)) { EXPECT_NE(aWireFaceId, aFaceId); } } -TEST_F(BRepGraphTest, RemoveVertex_PrunesDirectVertexUsage) -{ - const BRepGraph_VertexId aVertexId = - myGraph.Editor().Vertices().Add(gp_Pnt(1.0, 1.0, 1.0), Precision::Confusion()); - ASSERT_TRUE(aVertexId.IsValid()); - const BRepGraph_VertexRefId aVertexRefId = - myGraph.Editor().Faces().AddVertex(BRepGraph_FaceId::Start(), aVertexId, TopAbs_INTERNAL); - ASSERT_TRUE(aVertexRefId.IsValid()); - ASSERT_EQ(myGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).VertexRefIds.Length(), 1); - - ASSERT_TRUE(myGraph.Editor().Faces().RemoveVertex(BRepGraph_FaceId::Start(), aVertexRefId)); - - EXPECT_EQ(myGraph.Topo().Faces().Definition(BRepGraph_FaceId::Start()).VertexRefIds.Length(), 0); - EXPECT_TRUE(myGraph.Topo().Gen().IsRemoved(aVertexId)); - EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); -} - -TEST_F(BRepGraphTest, RemoveOccurrence_PrunesOccurrenceSubtreeAndRebuildsReverseIndex) +TEST_F(BRepGraphTest, RemoveOccurrence_PrunesOccurrenceSubtreeAndKeepsRelationsConsistent) { const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); - const BRepGraph_ProductId aAssemblyId = myGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyId = myGraph.Editor().Products().Add(); + myGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); ASSERT_TRUE(aAssemblyId.IsValid()); const BRepGraph_OccurrenceId anOccId = - myGraph.Editor().Products().LinkProducts(aAssemblyId, aPartId, TopLoc_Location()); + myGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location()); ASSERT_TRUE(anOccId.IsValid()); - const NCollection_DynamicArray& aOccurrenceRefsBefore = + const NCollection_LinearVector& aOccurrenceRefsBefore = myGraph.Refs().Occurrences().IdsOf(aAssemblyId); - ASSERT_EQ(aOccurrenceRefsBefore.Length(), 1); + ASSERT_EQ(aOccurrenceRefsBefore.Size(), 1); const BRepGraph_OccurrenceRefId anOccurrenceRefId = aOccurrenceRefsBefore.Value(0); ASSERT_TRUE(myGraph.Editor().Products().RemoveOccurrence(aAssemblyId, anOccurrenceRefId)); - EXPECT_EQ(myGraph.Refs().Occurrences().IdsOf(aAssemblyId).Length(), 0); + EXPECT_EQ(myGraph.Refs().Occurrences().IdsOf(aAssemblyId).Size(), 0); EXPECT_TRUE(myGraph.Topo().Gen().IsRemoved(anOccId)); EXPECT_EQ(myGraph.Topo().Products().NbComponents(aAssemblyId), 0); EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); @@ -2188,29 +1470,6 @@ TEST_F(BRepGraphTest, RemoveShapeRoot_PrunesUniqueTopologyRoot) EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); } -TEST_F(BRepGraphTest, RemoveVertex_PrunesInternalDirectVertexUsage) -{ - const BRepGraph_VertexId aVertexId = - myGraph.Editor().Vertices().Add(gp_Pnt(2.0, 2.0, 2.0), Precision::Confusion()); - ASSERT_TRUE(aVertexId.IsValid()); - const BRepGraph_VertexRefId aVertexRefId = - myGraph.Editor().Edges().AddInternalVertex(BRepGraph_EdgeId::Start(), - aVertexId, - TopAbs_INTERNAL); - ASSERT_TRUE(aVertexRefId.IsValid()); - ASSERT_EQ( - myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).InternalVertexRefIds.Length(), - 1); - - ASSERT_TRUE(myGraph.Editor().Edges().RemoveVertex(BRepGraph_EdgeId::Start(), aVertexRefId)); - - EXPECT_EQ( - myGraph.Topo().Edges().Definition(BRepGraph_EdgeId::Start()).InternalVertexRefIds.Length(), - 0); - EXPECT_TRUE(myGraph.Topo().Gen().IsRemoved(aVertexId)); - EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); -} - TEST_F(BRepGraphTest, RemoveVertex_ClearsBoundarySlotAndPrunesUniqueVertex) { const BRepGraph_VertexId aStartVertex = @@ -2236,35 +1495,34 @@ TEST_F(BRepGraphTest, RemoveVertex_ClearsBoundarySlotAndPrunesUniqueVertex) EXPECT_FALSE(myGraph.Topo().Edges().Definition(anEdge).StartVertexRefId.IsValid()); EXPECT_TRUE(myGraph.Topo().Gen().IsRemoved(aStartVertex)); - EXPECT_FALSE(BRepGraph_Tool::Edge::StartVertexRef(myGraph, anEdge).VertexDefId.IsValid()); - EXPECT_TRUE(BRepGraph_Tool::Edge::EndVertexRef(myGraph, anEdge).VertexDefId.IsValid()); + EXPECT_FALSE(BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdge).IsValid()); + EXPECT_TRUE(BRepGraph_Tool::Edge::EndVertexId(myGraph, anEdge).IsValid()); EXPECT_TRUE(myGraph.Editor().ValidateMutationBoundary()); } TEST_F(BRepGraphTest, RemoveNode_EdgeWithReplacement_ReparentsAllCoEdges) { - const NCollection_DynamicArray aCoEdgeRefs = - BRepGraph_TestTools::CoEdgeRefsOfWire(myGraph, BRepGraph_WireId::Start()); - ASSERT_GE(aCoEdgeRefs.Length(), 1); + const NCollection_LinearVector& aCoEdgeIds = + BRepGraph_TestTools::CoEdgesOfWire(myGraph, BRepGraph_WireId::Start()); + ASSERT_GE(aCoEdgeIds.Size(), 1); - const BRepGraphInc::CoEdgeRef& aRef = myGraph.Refs().CoEdges().Entry(aCoEdgeRefs.Value(0)); - const BRepGraph_EdgeId anOldEdgeId = - myGraph.Topo().CoEdges().Definition(aRef.CoEdgeDefId).EdgeDefId; + const BRepGraph_EdgeId anOldEdgeId = + myGraph.Topo().CoEdges().Definition(aCoEdgeIds.Value(0)).ChildEdgeId; const BRepGraph_EdgeId aNewEdgeId((anOldEdgeId.Index + 1) % myGraph.Topo().Edges().Nb()); - myGraph.Editor().Gen().RemoveNode(anOldEdgeId, aNewEdgeId); + myGraph.Editor().Gen().ReplaceNode(anOldEdgeId, aNewEdgeId); - const NCollection_DynamicArray& anOldCoEdges = + const NCollection_LinearVector& anOldCoEdges = myGraph.Topo().Edges().CoEdges(anOldEdgeId); - EXPECT_EQ(anOldCoEdges.Length(), 0); + EXPECT_EQ(anOldCoEdges.Size(), 0); - const NCollection_DynamicArray& aNewCoEdges = + const NCollection_LinearVector& aNewCoEdges = myGraph.Topo().Edges().CoEdges(aNewEdgeId); - EXPECT_GT(aNewCoEdges.Length(), 0); + EXPECT_GT(aNewCoEdges.Size(), 0); for (const BRepGraph_CoEdgeId& aNewCoEdgeId : aNewCoEdges) { const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aNewCoEdgeId); - EXPECT_EQ(aCoEdge.EdgeDefId, aNewEdgeId); + EXPECT_EQ(aCoEdge.ChildEdgeId, aNewEdgeId); } } @@ -2311,12 +1569,11 @@ TEST_F(BRepGraphTest, FreeEdges_SingleFace_AllEdgesFree) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, aFace); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = aGraph.Shapes().Add(aFace); + ASSERT_FALSE(aGraph.IsEmpty()); NCollection_DynamicArray aFreeEdges = collectFreeEdges(aGraph); - EXPECT_EQ(aFreeEdges.Length(), 4); + EXPECT_EQ(aFreeEdges.Size(), 4); } TEST_F(BRepGraphTest, Decompose_ThreeDisconnectedFaces_ThreeComponents) @@ -2338,9 +1595,8 @@ TEST_F(BRepGraphTest, Decompose_ThreeDisconnectedFaces_ThreeComponents) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 3); EXPECT_EQ(countFaceComponents(aGraph), 3); @@ -2415,7 +1671,7 @@ TEST_F(BRepGraphTest, DetectToleranceConflicts_ManualConflict_Detected) aCurveEdges.Append(anOtherId); } - if (aCurveEdges.Length() > 1 && aMaxTol - aMinTol > 0.5) + if (aCurveEdges.Size() > 1 && aMaxTol - aMinTol > 0.5) { for (const BRepGraph_EdgeId& aConflictId : aCurveEdges) { @@ -2426,27 +1682,11 @@ TEST_F(BRepGraphTest, DetectToleranceConflicts_ManualConflict_Detected) } } } - EXPECT_GE(aConflicts.Length(), 1); + EXPECT_GE(aConflicts.Size(), 1); } } -// =================================================================== -// Group 8: User Attributes & Error Cases -// =================================================================== - -TEST_F(BRepGraphTest, RemoveUserAttribute_AfterSet_ReturnsNull) -{ - BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); - occ::handle anAttr = new BRepGraph_TypedCacheValue(42); - - myGraph.Cache().Set(aFaceId, testIntAttrKind(), anAttr); - ASSERT_FALSE(myGraph.Cache().Get(aFaceId, testIntAttrKind()).IsNull()); - - myGraph.Cache().Remove(aFaceId, testIntAttrKind()); - EXPECT_TRUE(myGraph.Cache().Get(aFaceId, testIntAttrKind()).IsNull()); -} - -TEST_F(BRepGraphTest, Build_EmptyCompound_IsDoneZeroCounts) +TEST_F(BRepGraphTest, Build_EmptyCompound_IsEmptyZeroCounts) { BRep_Builder aBuilder; TopoDS_Compound aCompound; @@ -2454,9 +1694,8 @@ TEST_F(BRepGraphTest, Build_EmptyCompound_IsDoneZeroCounts) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, aCompound); - EXPECT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = aGraph.Shapes().Add(aCompound); + EXPECT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Solids().Nb(), 0); EXPECT_EQ(aGraph.Topo().Shells().Nb(), 0); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 0); @@ -2517,16 +1756,15 @@ TEST_F(BRepGraphTest, Allocator_DefaultConstructor_NotNull) EXPECT_FALSE(myGraph.Allocator().IsNull()); } -TEST_F(BRepGraphTest, Build_WithCustomAllocator_IsDone) +TEST_F(BRepGraphTest, Build_DefaultAllocator_IsNotEmpty) { - occ::handle anAlloc = NCollection_BaseAllocator::CommonBaseAllocator(); - BRepGraph aGraph(anAlloc); + BRepGraph aGraph; BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes16 = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes16 = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); EXPECT_FALSE(aGraph.Allocator().IsNull()); } @@ -2543,29 +1781,20 @@ TEST_F(BRepGraphTest, Wire_IsClosed_BoxOuterWires) const BRepGraph_WireId anOuterWire = BRepGraph_TestTools::OuterWireOfFace(myGraph, aFaceIt.CurrentId()); ASSERT_TRUE(anOuterWire.IsValid()); - const BRepGraphInc::WireDef& aWireDef = myGraph.Topo().Wires().Definition(anOuterWire); - EXPECT_TRUE(aWireDef.IsClosed) + EXPECT_TRUE(BRepGraph_Tool::Wire::IsClosed(myGraph, anOuterWire)) << "Outer wire of face " << aFaceIt.CurrentId().Index << " should be closed"; } } TEST_F(BRepGraphTest, Face_InnerWireRefs_BoxHasNone) { - // Box faces have no holes, so non-outer WireRefs should be empty. + // Box faces have no holes, so each face should have one wire ref. for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { - const NCollection_DynamicArray aWireRefs = + const NCollection_LinearVector& aWireRefs = BRepGraph_TestTools::WireRefsOfFace(myGraph, aFaceIt.CurrentId()); - int aNonOuterCount = 0; - for (const BRepGraph_WireRefId& aWireRefId : aWireRefs) - { - if (!myGraph.Refs().Wires().Entry(aWireRefId).IsOuter) - { - ++aNonOuterCount; - } - } - EXPECT_EQ(aNonOuterCount, 0) << "Box face " << aFaceIt.CurrentId().Index - << " should have no inner wires"; + EXPECT_EQ(aWireRefs.Size(), 1) + << "Box face " << aFaceIt.CurrentId().Index << " should have one wire"; } } @@ -2573,9 +1802,9 @@ TEST_F(BRepGraphTest, Face_Orientation_ValidValue) { // Verify face orientations in the shell's incidence refs. ASSERT_EQ(myGraph.Topo().Shells().Nb(), 1); - const NCollection_DynamicArray aFaceRefs = + const NCollection_LinearVector& aFaceRefs = BRepGraph_TestTools::FaceRefsOfShell(myGraph, BRepGraph_ShellId::Start()); - for (int aRefIdx = 0; aRefIdx < aFaceRefs.Length(); ++aRefIdx) + for (size_t aRefIdx = 0; aRefIdx < aFaceRefs.Size(); ++aRefIdx) { const BRepGraphInc::FaceRef& aFaceRef = myGraph.Refs().Faces().Entry(aFaceRefs.Value(aRefIdx)); TopAbs_Orientation anOri = aFaceRef.Orientation; @@ -2641,73 +1870,14 @@ TEST_F(BRepGraphTest, Face_ToleranceNonNegative) // Group 13: Mutation (Extended) // =================================================================== -TEST_F(BRepGraphTest, MutableWireDef_ModifyClosure_Verified) +TEST_F(BRepGraphTest, Wire_IsClosed_DerivedFromCoedgeChain) { - BRepGraph_MutGuard aMutWD = - myGraph.Editor().Wires().Mut(BRepGraph_WireId::Start()); - bool anOrigClosed = aMutWD->IsClosed; - myGraph.Editor().Wires().SetIsClosed(aMutWD, !anOrigClosed); - - EXPECT_EQ(myGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).IsClosed, !anOrigClosed); - - // Restore original state. - myGraph.Editor().Wires().SetIsClosed(BRepGraph_WireId::Start(), anOrigClosed); - EXPECT_EQ(myGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).IsClosed, anOrigClosed); -} - -TEST_F(BRepGraphTest, MultipleUserAttributes_SameNode_Independent) -{ - BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); - - Handle(BRepGraph_TypedCacheValue) anAttr1 = new BRepGraph_TypedCacheValue(100); - Handle(BRepGraph_TypedCacheValue) anAttr2 = new BRepGraph_TypedCacheValue(2.718); - - myGraph.Cache().Set(aFaceId, testIntAttrKind(), anAttr1); - myGraph.Cache().Set(aFaceId, testAuxAttrKind(), anAttr2); - - Handle(BRepGraph_TypedCacheValue) aRetrieved1 = - Handle(BRepGraph_TypedCacheValue)::DownCast( - myGraph.Cache().Get(aFaceId, testIntAttrKind())); - Handle(BRepGraph_TypedCacheValue) aRetrieved2 = - Handle(BRepGraph_TypedCacheValue)::DownCast( - myGraph.Cache().Get(aFaceId, testAuxAttrKind())); - - ASSERT_FALSE(aRetrieved1.IsNull()); - ASSERT_FALSE(aRetrieved2.IsNull()); - EXPECT_EQ(aRetrieved1->UncheckedValue(), 100); - EXPECT_NEAR(aRetrieved2->UncheckedValue(), 2.718, 1.0e-10); - - // Remove one; the other should remain. - myGraph.Cache().Remove(aFaceId, testIntAttrKind()); - EXPECT_TRUE(myGraph.Cache().Get(aFaceId, testIntAttrKind()).IsNull()); - EXPECT_FALSE(myGraph.Cache().Get(aFaceId, testAuxAttrKind()).IsNull()); -} - -TEST_F(BRepGraphTest, InvalidateUserAttribute_SpecificKey) -{ - BRepGraph_NodeId aFaceId(BRepGraph_NodeId::Kind::Face, 0); - Handle(BRepGraph_TypedCacheValue) anAttr = new BRepGraph_TypedCacheValue(42); - myGraph.Cache().Set(aFaceId, testIntAttrKind(), anAttr); - - // Invalidate should not remove, just mark dirty. - myGraph.Cache().Invalidate(aFaceId, testIntAttrKind()); - - occ::handle aRetrieved = myGraph.Cache().Get(aFaceId, testIntAttrKind()); - EXPECT_FALSE(aRetrieved.IsNull()); // still present -} - -TEST_F(BRepGraphTest, UserAttribute_OnEdgeNode) -{ - BRepGraph_NodeId anEdgeId(BRepGraph_NodeId::Kind::Edge, 0); - Handle(BRepGraph_TypedCacheValue) anAttr = new BRepGraph_TypedCacheValue(1.5); - - myGraph.Cache().Set(anEdgeId, testDoubleAttrKind(), anAttr); - - Handle(BRepGraph_TypedCacheValue) aRetrieved = - Handle(BRepGraph_TypedCacheValue)::DownCast( - myGraph.Cache().Get(anEdgeId, testDoubleAttrKind())); - ASSERT_FALSE(aRetrieved.IsNull()); - EXPECT_NEAR(aRetrieved->UncheckedValue(), 1.5, 1.0e-10); + // Wire closure is now derived from the ordered coedge chain. + const BRepGraph_WireId anOuterWire = + BRepGraph_TestTools::OuterWireOfFace(myGraph, BRepGraph_FaceId::Start()); + ASSERT_TRUE(anOuterWire.IsValid()); + // Outer wires of a box face should be closed. + EXPECT_TRUE(BRepGraph_Tool::Wire::IsClosed(myGraph, anOuterWire)); } // =================================================================== @@ -2722,9 +1892,8 @@ TEST_F(BRepGraphTest, Build_SingleFace_CorrectCounts) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes17 = - BRepGraph_Builder::Add(aGraph, aFace); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes17 = aGraph.Shapes().Add(aFace); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Solids().Nb(), 0); EXPECT_EQ(aGraph.Topo().Shells().Nb(), 0); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 1); @@ -2741,9 +1910,9 @@ TEST_F(BRepGraphTest, Build_Shell_CorrectCounts) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes18 = - BRepGraph_Builder::Add(aGraph, anExp.Current()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes18 = + aGraph.Shapes().Add(anExp.Current()); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Solids().Nb(), 0); EXPECT_EQ(aGraph.Topo().Shells().Nb(), 1); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 6); @@ -2764,9 +1933,8 @@ TEST_F(BRepGraphTest, Build_CompoundOfTwoSolids) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes19 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes19 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); EXPECT_EQ(aGraph.Topo().Solids().Nb(), 2); EXPECT_EQ(aGraph.Topo().Shells().Nb(), 2); EXPECT_EQ(aGraph.Topo().Faces().Nb(), 12); @@ -2780,9 +1948,9 @@ TEST_F(BRepGraphTest, ReconstructShape_ShellRoot_SameFaceCount) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes20 = - BRepGraph_Builder::Add(aGraph, anExp.Current()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes20 = + aGraph.Shapes().Add(anExp.Current()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_EQ(aGraph.Topo().Shells().Nb(), 1); BRepGraph_NodeId aShellId(BRepGraph_NodeId::Kind::Shell, 0); @@ -2836,15 +2004,15 @@ TEST_F(BRepGraphTest, Wire_OuterWireIdx_MatchesFaceDef) } } -TEST_F(BRepGraphTest, Wire_CoEdgeRefs_FourEdgesPerBoxFace) +TEST_F(BRepGraphTest, Wire_CoEdges_FourEdgesPerBoxFace) { for (BRepGraph_FaceIterator aFaceIt(myGraph); aFaceIt.More(); aFaceIt.Next()) { const BRepGraph_WireId anOuterWire = BRepGraph_TestTools::OuterWireOfFace(myGraph, aFaceIt.CurrentId()); ASSERT_TRUE(anOuterWire.IsValid()); - EXPECT_EQ(BRepGraph_TestTools::CountCoEdgeRefsOfWire(myGraph, anOuterWire), 4) - << "Box face " << aFaceIt.CurrentId().Index << " should have 4 coedge refs in its outer wire"; + EXPECT_EQ(BRepGraph_TestTools::CountCoEdgesOfWire(myGraph, anOuterWire), 4) + << "Box face " << aFaceIt.CurrentId().Index << " should have 4 coedges in its outer wire"; } } @@ -2854,62 +2022,71 @@ TEST_F(BRepGraphTest, Wire_CoEdgeRefs_FourEdgesPerBoxFace) TEST_F(BRepGraphTest, SetHistoryEnabled_DefaultTrue) { - EXPECT_TRUE(myGraph.History().IsEnabled()); + EXPECT_TRUE(myGraph.LayerRegistry().Ensure()->IsEnabled()); } TEST_F(BRepGraphTest, SetHistoryEnabled_DisableAndQuery) { - myGraph.History().SetEnabled(false); - EXPECT_FALSE(myGraph.History().IsEnabled()); - myGraph.History().SetEnabled(true); - EXPECT_TRUE(myGraph.History().IsEnabled()); + myGraph.LayerRegistry().Ensure()->SetEnabled(false); + EXPECT_FALSE(myGraph.LayerRegistry().Ensure()->IsEnabled()); + myGraph.LayerRegistry().Ensure()->SetEnabled(true); + EXPECT_TRUE(myGraph.LayerRegistry().Ensure()->IsEnabled()); } TEST_F(BRepGraphTest, RecordHistory_Disabled_NoRecordAdded) { - const size_t aBefore = myGraph.History().NbRecords(); + const size_t aBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); - myGraph.History().SetEnabled(false); + myGraph.LayerRegistry().Ensure()->SetEnabled(false); BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); - NCollection_DynamicArray aRepl; + NCollection_LinearVector aRepl; aRepl.Append(anEdge1); - myGraph.History().Record("ShouldNotRecord", anEdge0, aRepl); + myGraph.LayerRegistry().Ensure()->Record("ShouldNotRecord", + anEdge0, + aRepl.ToArray1()); - EXPECT_EQ(myGraph.History().NbRecords(), aBefore); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aBefore); } TEST_F(BRepGraphTest, RecordHistory_ReEnabled_RecordsAgain) { - myGraph.History().SetEnabled(false); + myGraph.LayerRegistry().Ensure()->SetEnabled(false); - const size_t aBefore = myGraph.History().NbRecords(); + const size_t aBefore = myGraph.LayerRegistry().Ensure()->NbRecords(); BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); - NCollection_DynamicArray aRepl; + NCollection_LinearVector aRepl; aRepl.Append(anEdge1); - myGraph.History().Record("Skipped", anEdge0, aRepl); - EXPECT_EQ(myGraph.History().NbRecords(), aBefore); + myGraph.LayerRegistry().Ensure()->Record("Skipped", + anEdge0, + aRepl.ToArray1()); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aBefore); - myGraph.History().SetEnabled(true); - myGraph.History().Record("Recorded", anEdge0, aRepl); - EXPECT_EQ(myGraph.History().NbRecords(), aBefore + 1); - EXPECT_TRUE(myGraph.History().Record(aBefore).OperationName.IsEqual("Recorded")); + myGraph.LayerRegistry().Ensure()->SetEnabled(true); + myGraph.LayerRegistry().Ensure()->Record("Recorded", + anEdge0, + aRepl.ToArray1()); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aBefore + 1); + EXPECT_TRUE( + myGraph.LayerRegistry().Ensure()->Record(aBefore).OperationName.IsEqual( + "Recorded")); } TEST_F(BRepGraphTest, ApplyModification_HistoryDisabled_NoHistoryNoDerivedEdges) { - myGraph.History().SetEnabled(false); + myGraph.LayerRegistry().Ensure()->SetEnabled(false); - const size_t aNbHistBefore = myGraph.History().NbRecords(); + const size_t aNbHistBefore = + myGraph.LayerRegistry().Ensure()->NbRecords(); BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); BRepGraph_NodeId anEdge1(BRepGraph_NodeId::Kind::Edge, 1); auto aModifier = [&](BRepGraph& /*theGraph*/, BRepGraph_NodeId /*theTarget*/) { - NCollection_DynamicArray aResult; + NCollection_LinearVector aResult; aResult.Append(anEdge1); return aResult; }; @@ -2917,19 +2094,19 @@ TEST_F(BRepGraphTest, ApplyModification_HistoryDisabled_NoHistoryNoDerivedEdges) myGraph.Editor().Gen().ApplyModification(anEdge0, aModifier, "NoHistory"); // No history records should be added. - EXPECT_EQ(myGraph.History().NbRecords(), aNbHistBefore); + EXPECT_EQ(myGraph.LayerRegistry().Ensure()->NbRecords(), aNbHistBefore); } TEST_F(BRepGraphTest, ApplyModification_HistoryDisabled_ModifierStillRuns) { - myGraph.History().SetEnabled(false); + myGraph.LayerRegistry().Ensure()->SetEnabled(false); bool isModifierCalled = false; BRepGraph_NodeId anEdge0(BRepGraph_NodeId::Kind::Edge, 0); auto aModifier = [&](BRepGraph& /*theGraph*/, BRepGraph_NodeId /*theTarget*/) { isModifierCalled = true; - NCollection_DynamicArray aResult; + NCollection_LinearVector aResult; aResult.Append(anEdge0); return aResult; }; diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Tool_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Tool_Test.cxx index 9e09cc2a3e..4074dcdd19 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Tool_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Tool_Test.cxx @@ -12,12 +12,16 @@ // commercial license or contractual agreement. #include +#include +#include #include -#include +#include +#include #include #include #include +#include #include #include @@ -31,13 +35,13 @@ protected: { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); myBoxGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myBoxGraph, aBoxMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = + myBoxGraph.Shapes().Add(aBoxMaker.Shape()); BRepPrimAPI_MakeCylinder aCylMaker(5.0, 15.0); myCylGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(myCylGraph, aCylMaker.Shape()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + myCylGraph.Shapes().Add(aCylMaker.Shape()); } BRepGraph myBoxGraph; @@ -70,7 +74,7 @@ TEST_F(BRepGraph_QuerySurfaceTest, Face_Bounds_BoxFaceHasFiniteBounds) TEST_F(BRepGraph_QuerySurfaceTest, Face_Bounds_CylinderFaceReturnsSurfaceBounds) { // Verify that Bounds() returns the same values as Surface()->Bounds() for each face. - const int aNbFaces = myCylGraph.Topo().Faces().Nb(); + const uint32_t aNbFaces = myCylGraph.Topo().Faces().Nb(); for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) { if (!BRepGraph_Tool::Face::HasSurface(myCylGraph, aFaceId)) @@ -101,10 +105,10 @@ TEST_F(BRepGraph_QuerySurfaceTest, Wire_FaceOf_ReturnsValidFace) TEST_F(BRepGraph_QuerySurfaceTest, Wire_IsOuter_FirstWireOfBoxFaceIsOuter) { const BRepGraph_FaceId aFaceId(0); - const BRepGraph_WireId anOuterWire = BRepGraph_Tool::Face::OuterWireId(myBoxGraph, aFaceId); + const BRepGraph_WireId anOuterWire = BRepGraph_Tool::Face::OuterWire(myBoxGraph, aFaceId); ASSERT_TRUE(anOuterWire.IsValid()); EXPECT_TRUE(BRepGraph_Tool::Wire::IsOuter(myBoxGraph, anOuterWire)) - << "OuterWireId-found wire should be flagged IsOuter"; + << "OuterWire-found wire should be flagged IsOuter"; } TEST_F(BRepGraph_QuerySurfaceTest, Edge_NbFaces_BoxEdgeHasExactlyTwoFaces) @@ -121,7 +125,7 @@ TEST_F(BRepGraph_QuerySurfaceTest, Edge_NbFaces_BoxEdgeHasExactlyTwoFaces) TEST_F(BRepGraph_QuerySurfaceTest, Edge_IsManifold_BoxEdgesAreManifold) { - const int aNbEdges = myBoxGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = myBoxGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { EXPECT_TRUE(BRepGraph_Tool::Edge::IsManifold(myBoxGraph, anEdgeId)) @@ -131,9 +135,91 @@ TEST_F(BRepGraph_QuerySurfaceTest, Edge_IsManifold_BoxEdgesAreManifold) } } +TEST_F(BRepGraph_QuerySurfaceTest, Edge_IsClosed_BoxEdgesAreOpen) +{ + const uint32_t aNbEdges = myBoxGraph.Topo().Edges().Nb(); + ASSERT_GT(aNbEdges, 0); + for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) + { + EXPECT_FALSE(BRepGraph_Tool::Edge::IsClosed(myBoxGraph, anEdgeId)) + << "Box edge " << anEdgeId.Index << " has distinct endpoints, should not be IsClosed"; + } +} + +TEST_F(BRepGraph_QuerySurfaceTest, Edge_IsClosed_DerivedFromVertexTopology) +{ + const BRepGraph_EdgeId anEdgeId(0); + ASSERT_TRUE(anEdgeId.IsValid(myBoxGraph.Topo().Edges().Nb())); + // Box edges have distinct start/end vertices, so IsClosed is derived as false. + EXPECT_FALSE(BRepGraph_Tool::Edge::IsClosed(myBoxGraph, anEdgeId)); +} + +TEST_F(BRepGraph_QuerySurfaceTest, CoEdge_PolygonOnTriangulation_RoundTrip) +{ + ASSERT_GT(myBoxGraph.Topo().CoEdges().Nb(), 0); + const BRepGraph_CoEdgeId aCoEdgeId(0); + const BRepGraph_FaceId aFaceId = BRepGraph_Tool::CoEdge::FaceOf(myBoxGraph, aCoEdgeId); + EXPECT_FALSE(myBoxGraph.Mesh().Persistent().CoEdges().HasPolygonOnTriangulation(aCoEdgeId)); + EXPECT_TRUE(myBoxGraph.Mesh().Persistent().CoEdges().PolygonOnTriangulation(aCoEdgeId).IsNull()); + + occ::handle aTri = new Poly_Triangulation(1, 1, false); + myBoxGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTri); + + occ::handle aPolyOnTri = new Poly_PolygonOnTriangulation(2, false); + myBoxGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, aPolyOnTri); + + EXPECT_TRUE(myBoxGraph.Mesh().Persistent().CoEdges().HasPolygonOnTriangulation(aCoEdgeId)); + EXPECT_EQ(myBoxGraph.Mesh().Persistent().CoEdges().PolygonOnTriangulation(aCoEdgeId).get(), + aPolyOnTri.get()); +} + +TEST_F(BRepGraph_QuerySurfaceTest, Edge_PolygonOnTriangulation_ResolvesViaFace) +{ + ASSERT_GT(myBoxGraph.Topo().CoEdges().Nb(), 0); + const BRepGraph_CoEdgeId aCoEdgeId(0); + const BRepGraph_EdgeId anEdgeId = BRepGraph_Tool::CoEdge::EdgeOf(myBoxGraph, aCoEdgeId); + const BRepGraph_FaceId aFaceId = BRepGraph_Tool::CoEdge::FaceOf(myBoxGraph, aCoEdgeId); + ASSERT_TRUE(anEdgeId.IsValid()); + ASSERT_TRUE(aFaceId.IsValid()); + + EXPECT_FALSE(myBoxGraph.Mesh().Persistent().Edges().HasPolygonOnTriangulation(anEdgeId, aFaceId)); + + occ::handle aTri = new Poly_Triangulation(1, 1, false); + myBoxGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTri); + + occ::handle aPolyOnTri = new Poly_PolygonOnTriangulation(2, false); + myBoxGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, aPolyOnTri); + + EXPECT_TRUE(myBoxGraph.Mesh().Persistent().Edges().HasPolygonOnTriangulation(anEdgeId, aFaceId)); + EXPECT_EQ(myBoxGraph.Mesh().Persistent().Edges().PolygonOnTriangulation(anEdgeId, aFaceId).get(), + aPolyOnTri.get()); +} + +TEST_F(BRepGraph_QuerySurfaceTest, Face_CachedTriangulation_DefaultEmpty) +{ + for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(myBoxGraph.Topo().Faces().Nb()); ++aFaceId) + { + EXPECT_FALSE(myBoxGraph.Mesh().Cache().Faces().Has(aFaceId)); + EXPECT_TRUE(myBoxGraph.Mesh().Cache().Faces().Triangulation(aFaceId).IsNull()); + } +} + +TEST_F(BRepGraph_QuerySurfaceTest, Face_CachedTriangulation_SetAndRead) +{ + ASSERT_GT(myBoxGraph.Topo().Faces().Nb(), 0); + const BRepGraph_FaceId aFaceId(0); + + occ::handle aTri = new Poly_Triangulation(1, 1, false); + + myBoxGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTri); + + EXPECT_TRUE(myBoxGraph.Mesh().Cache().Faces().Has(aFaceId)); + EXPECT_EQ(myBoxGraph.Mesh().Cache().Faces().Triangulation(aFaceId).get(), aTri.get()); +} + TEST_F(BRepGraph_QuerySurfaceTest, Vertex_NbEdges_BoxVertexHasThreeEdges) { - const int aNbVertices = myBoxGraph.Topo().Vertices().Nb(); + const uint32_t aNbVertices = myBoxGraph.Topo().Vertices().Nb(); for (BRepGraph_VertexId aVertexId(0); aVertexId.IsValid(aNbVertices); ++aVertexId) { const int aNbEdges = BRepGraph_Tool::Vertex::NbEdges(myBoxGraph, aVertexId); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Transform_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Transform_Test.cxx index ffffbe7030..26b37f827e 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Transform_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Transform_Test.cxx @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -36,12 +35,16 @@ #include #include #include +#include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -50,6 +53,71 @@ #include +namespace +{ + +BRepGraph_CoEdgeId firstCoEdgeOfFace(const BRepGraph& theGraph, const BRepGraph_FaceId theFaceId) +{ + for (BRepGraph_RefsWireOfFace aWireRefIt(theGraph, theFaceId); aWireRefIt.More(); + aWireRefIt.Next()) + { + const BRepGraph_WireId aWireId = + theGraph.Refs().Wires().Entry(aWireRefIt.CurrentId()).ChildWireId; + for (BRepGraph_CoEdgesOfWire aCoEdgeIt(theGraph, aWireId); aCoEdgeIt.More(); aCoEdgeIt.Next()) + { + const BRepGraph_CoEdgeId aCoEdgeId = aCoEdgeIt.CurrentId(); + if (theGraph.Topo().CoEdges().Definition(aCoEdgeId).FaceId == theFaceId) + { + return aCoEdgeId; + } + } + } + return BRepGraph_CoEdgeId(); +} + +occ::handle makePolygon3D(const gp_Pnt& theFirst, const gp_Pnt& theSecond) +{ + occ::handle aPolygon = new Poly_Polygon3D(2, false); + aPolygon->ChangeNodes().SetValue(1, theFirst); + aPolygon->ChangeNodes().SetValue(2, theSecond); + return aPolygon; +} + +occ::handle makePolygon2D(const gp_Pnt2d& theFirst, const gp_Pnt2d& theSecond) +{ + occ::handle aPolygon = new Poly_Polygon2D(2); + aPolygon->ChangeNodes().SetValue(1, theFirst); + aPolygon->ChangeNodes().SetValue(2, theSecond); + return aPolygon; +} + +occ::handle makePolygonOnTri() +{ + occ::handle aPolygon = new Poly_PolygonOnTriangulation(2, false); + aPolygon->SetNode(1, 1); + aPolygon->SetNode(2, 2); + return aPolygon; +} + +void expectVerticesTransformed(const BRepGraph& theSource, + const BRepGraph& theResult, + const gp_Trsf& theTrsf) +{ + const uint32_t aNbV = theSource.Topo().Vertices().Nb(); + ASSERT_EQ(theResult.Topo().Vertices().Nb(), aNbV); + for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) + { + gp_Pnt anExpected = BRepGraph_Tool::Vertex::Pnt(theSource, aVId); + anExpected.Transform(theTrsf); + const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(theResult, aVId); + EXPECT_NEAR(aTrans.X(), anExpected.X(), Precision::Confusion()) << "vertex " << aVId.Index; + EXPECT_NEAR(aTrans.Y(), anExpected.Y(), Precision::Confusion()) << "vertex " << aVId.Index; + EXPECT_NEAR(aTrans.Z(), anExpected.Z(), Precision::Confusion()) << "vertex " << aVId.Index; + } +} + +} // namespace + TEST(BRepGraph_TransformTest, TranslateBox_FaceCount) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); @@ -57,15 +125,16 @@ TEST(BRepGraph_TransformTest, TranslateBox_FaceCount) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(100.0, 200.0, 300.0)); - BRepGraph aResultGraph = BRepGraph_Transform::Perform(aGraph, aTrsf, true); - ASSERT_TRUE(aResultGraph.IsDone()); + BRepGraph aResultGraph; + ASSERT_TRUE( + BRepGraph_Transform::Perform(aGraph, aResultGraph, aTrsf, BRepGraph_Copy::GeomPolicy::Copy)); + ASSERT_FALSE(aResultGraph.IsEmpty()); EXPECT_EQ(aResultGraph.Topo().Faces().Nb(), 6); EXPECT_EQ(aResultGraph.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); } @@ -81,19 +150,20 @@ TEST(BRepGraph_TransformTest, TranslateBox_AreaPreserved) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(50.0, 0.0, 0.0)); - BRepGraph aResultGraph = BRepGraph_Transform::Perform(aGraph, aTrsf, true); - ASSERT_TRUE(aResultGraph.IsDone()); + BRepGraph aResultGraph; + ASSERT_TRUE( + BRepGraph_Transform::Perform(aGraph, aResultGraph, aTrsf, BRepGraph_Copy::GeomPolicy::Copy)); + ASSERT_FALSE(aResultGraph.IsEmpty()); // Verify area is preserved by summing individual face areas. - double aTransArea = 0.0; - const int aNbFaces = aResultGraph.Topo().Faces().Nb(); + double aTransArea = 0.0; + const uint32_t aNbFaces = aResultGraph.Topo().Faces().Nb(); for (BRepGraph_FaceId aFaceId(0); aFaceId.IsValid(aNbFaces); ++aFaceId) { TopoDS_Shape aFace = aResultGraph.Shapes().Reconstruct(aFaceId); @@ -112,21 +182,22 @@ TEST(BRepGraph_TransformTest, TranslateBox_VertexPointsShifted) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); const double aDx = 100.0, aDy = 200.0, aDz = 300.0; gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(aDx, aDy, aDz)); - BRepGraph aResultGraph = BRepGraph_Transform::Perform(aGraph, aTrsf, true); - ASSERT_TRUE(aResultGraph.IsDone()); + BRepGraph aResultGraph; + ASSERT_TRUE( + BRepGraph_Transform::Perform(aGraph, aResultGraph, aTrsf, BRepGraph_Copy::GeomPolicy::Copy)); + ASSERT_FALSE(aResultGraph.IsEmpty()); ASSERT_EQ(aResultGraph.Topo().Vertices().Nb(), aGraph.Topo().Vertices().Nb()); // Verify that all vertices have been shifted. - const int aNbVertices = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbVertices = aGraph.Topo().Vertices().Nb(); for (BRepGraph_VertexId aVertexId(0); aVertexId.IsValid(aNbVertices); ++aVertexId) { const gp_Pnt anOrigPt = BRepGraph_Tool::Vertex::Pnt(aGraph, aVertexId); @@ -140,6 +211,34 @@ TEST(BRepGraph_TransformTest, TranslateBox_VertexPointsShifted) } } +TEST(BRepGraph_TransformTest, PerformIntoNonEmptyTargetDoesNotMoveExistingVertices) +{ + BRepGraph aSource; + aSource.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aSourceBuild = + aSource.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aSource.IsEmpty()); + + BRepGraph aTarget; + const BRepGraph_VertexId anExistingVertex = + aTarget.Editor().Vertices().Add(gp_Pnt(1.0, 2.0, 3.0), 1.0e-7); + const gp_Pnt anOriginalPoint = BRepGraph_Tool::Vertex::Pnt(aTarget, anExistingVertex); + + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(100.0, 200.0, 300.0)); + + ASSERT_TRUE(BRepGraph_Transform::Perform(aSource, + aTarget, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop)); + + const gp_Pnt anAfterPoint = BRepGraph_Tool::Vertex::Pnt(aTarget, anExistingVertex); + EXPECT_NEAR(anAfterPoint.X(), anOriginalPoint.X(), Precision::Confusion()); + EXPECT_NEAR(anAfterPoint.Y(), anOriginalPoint.Y(), Precision::Confusion()); + EXPECT_NEAR(anAfterPoint.Z(), anOriginalPoint.Z(), Precision::Confusion()); +} + TEST(BRepGraph_TransformTest, LocationOnly_NoCopyGeom) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); @@ -147,23 +246,24 @@ TEST(BRepGraph_TransformTest, LocationOnly_NoCopyGeom) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); const double aDx = 50.0; gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(aDx, 0.0, 0.0)); - // theCopyGeom = false: location-only, no geometry modification. - BRepGraph aResultGraph = BRepGraph_Transform::Perform(aGraph, aTrsf, false); - ASSERT_TRUE(aResultGraph.IsDone()); + // GeomPolicy::Share: location-only, no geometry modification. + BRepGraph aResultGraph; + ASSERT_TRUE( + BRepGraph_Transform::Perform(aGraph, aResultGraph, aTrsf, BRepGraph_Copy::GeomPolicy::Share)); + ASSERT_FALSE(aResultGraph.IsEmpty()); EXPECT_EQ(aResultGraph.Topo().Faces().Nb(), 6); EXPECT_EQ(aResultGraph.Topo().Vertices().Nb(), aGraph.Topo().Vertices().Nb()); // Vertex definition points must NOT be modified (location-only mode). - const int aNbVertices = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbVertices = aGraph.Topo().Vertices().Nb(); for (BRepGraph_VertexId aVertexId(0); aVertexId.IsValid(aNbVertices); ++aVertexId) { const gp_Pnt anOrigPt = BRepGraph_Tool::Vertex::Pnt(aGraph, aVertexId); @@ -174,12 +274,12 @@ TEST(BRepGraph_TransformTest, LocationOnly_NoCopyGeom) // Verify the transform is stored on the shape-root OccurrenceRef's LocalLocation. ASSERT_GT(aResultGraph.Topo().Products().Nb(), 0); - const BRepGraph_ProductId aProductId = BRepGraph_ProductId::Start(); - const BRepGraphInc::ProductDef& aProductDef = - aResultGraph.Topo().Products().Definition(aProductId); - ASSERT_GE(aProductDef.OccurrenceRefIds.Length(), 1); + const BRepGraph_ProductId aProductId = BRepGraph_ProductId::Start(); + const BRepGraphInc::ProductRelations& aProductRelations = + aResultGraph.Topo().Products().Relations(aProductId); + ASSERT_GE(aProductRelations.OccurrenceRefIds.Size(), 1); const BRepGraphInc::OccurrenceRef& aOccRef = - aResultGraph.Refs().Occurrences().Entry(aProductDef.OccurrenceRefIds.Value(0)); + aResultGraph.Refs().Occurrences().Entry(aProductRelations.OccurrenceRefIds.Value(0)); const TopLoc_Location& aRootLoc = aOccRef.LocalLocation; EXPECT_FALSE(aRootLoc.IsIdentity()); const gp_Trsf aProductTrsf = aRootLoc.Transformation(); @@ -208,9 +308,8 @@ TEST(BRepGraph_TransformTest, TransformSingleFace) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Faces().Nb(), 0); gp_Trsf aTrsf; @@ -218,8 +317,14 @@ TEST(BRepGraph_TransformTest, TransformSingleFace) const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); const BRepGraph_NodeId aFaceNode(BRepGraph_NodeId::Kind::Face, aFaceId.Index); - BRepGraph aResultGraph = BRepGraph_Transform::TransformNode(aGraph, aFaceNode, aTrsf, true); - ASSERT_TRUE(aResultGraph.IsDone()); + BRepGraph aResultGraph; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResultGraph, + aFaceNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy) + .IsValid()); + ASSERT_FALSE(aResultGraph.IsEmpty()); EXPECT_EQ(aResultGraph.Topo().Faces().Nb(), 1); } @@ -230,8 +335,8 @@ TEST(BRepGraph_TransformTest, CopyMesh_TriangulationNodesTransformed) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); // Manually create a triangulation with known node positions on the first face. @@ -243,26 +348,25 @@ TEST(BRepGraph_TransformTest, CopyMesh_TriangulationNodesTransformed) aSrcTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); aSrcTri->Deflection(0.1); - const BRepGraph_TriangulationRepId aTriRepId = - BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, aSrcTri); - aGraph.Editor().Faces().SetTriangulationRep(aFaceId, aTriRepId); - BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aTriRepId); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aSrcTri); + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aSrcTri); const double aDx = 5.0, aDy = 10.0, aDz = 15.0; gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(aDx, aDy, aDz)); - // Transform with theCopyMesh = true. - BRepGraph aResult = BRepGraph_Transform::Perform(aGraph, aTrsf, true, true); - ASSERT_TRUE(aResult.IsDone()); + // Transform with MeshPolicy::Copy. + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::Perform(aGraph, + aResult, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Copy)); + ASSERT_FALSE(aResult.IsEmpty()); // The persistent triangulation on the first face must be present and transformed. - const BRepGraph_TriangulationRepId aNewTriRepId = - aResult.Topo().Faces().Definition(aFaceId).TriangulationRepId; - ASSERT_TRUE(aNewTriRepId.IsValid(aResult.Mesh().Poly().NbTriangulations())); - const occ::handle& aNewTri = - aResult.Mesh().Poly().TriangulationRep(aNewTriRepId).Triangulation; + aResult.Mesh().Persistent().Faces().Triangulation(aFaceId); ASSERT_FALSE(aNewTri.IsNull()); ASSERT_EQ(aNewTri->NbNodes(), 3); @@ -285,8 +389,8 @@ TEST(BRepGraph_TransformTest, CopyMesh_False_TriangulationInvalidated) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); occ::handle aTri = new Poly_Triangulation(3, 1, false); @@ -294,85 +398,96 @@ TEST(BRepGraph_TransformTest, CopyMesh_False_TriangulationInvalidated) aTri->SetNode(2, gp_Pnt(1, 0, 0)); aTri->SetNode(3, gp_Pnt(0, 1, 0)); aTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); - const BRepGraph_TriangulationRepId aTriRepId = - BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, aTri); - aGraph.Editor().Faces().SetTriangulationRep(aFaceId, aTriRepId); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTri); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0)); - // Default: theCopyMesh = false -> triangulations are discarded. - BRepGraph aResult = BRepGraph_Transform::Perform(aGraph, aTrsf, true, false); - ASSERT_TRUE(aResult.IsDone()); + // Default: MeshPolicy::Drop -> triangulations are discarded. + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::Perform(aGraph, + aResult, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop)); + ASSERT_FALSE(aResult.IsEmpty()); - const BRepGraph_TriangulationRepId aResultTriRepId = - aResult.Topo().Faces().Definition(aFaceId).TriangulationRepId; - EXPECT_FALSE(aResultTriRepId.IsValid()); - EXPECT_FALSE(aResult.Mesh().Faces().HasTriangulation(aFaceId)); + EXPECT_TRUE(aResult.Mesh().Persistent().Faces().Triangulation(aFaceId).IsNull()); + EXPECT_FALSE(aResult.Mesh().Effective().Faces().Has(aFaceId)); } -TEST(BRepGraph_TransformTest, MoveRef_FaceRef_LocationComposed) +TEST(BRepGraph_TransformTest, MoveRef_ChildRef_ComposesLocation) { - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - // The solid has shell refs; grab the first one. - const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); - ASSERT_GE(aGraph.Topo().Solids().Definition(aSolidId).ShellRefIds.Length(), 1); - const BRepGraph_ShellRefId aShellRef = - aGraph.Topo().Solids().Definition(aSolidId).ShellRefIds.Value(0); + NCollection_LinearVector aChildren; + aChildren.Append(BRepGraph_NodeId(BRepGraph_SolidId::Start())); + const BRepGraph_CompoundId aCompound = aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + ASSERT_TRUE(aCompound.IsValid()); + ASSERT_EQ(aGraph.Refs().Children().IdsOf(aCompound).Size(), 1); + const BRepGraph_ChildRefId aChildRef = aGraph.Refs().Children().IdsOf(aCompound).First(); - // Verify that the location starts as identity. - EXPECT_TRUE(aGraph.Refs().Shells().Entry(aShellRef).LocalLocation.IsIdentity()); + EXPECT_TRUE(aGraph.Refs().Children().Entry(aChildRef).LocalLocation.IsIdentity()); const double aDx = 42.0; gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(aDx, 0.0, 0.0)); - const bool aOk = BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aShellRef), aTrsf); + const bool aOk = BRepGraph_Transform::MoveRef(aGraph, aChildRef, aTrsf); EXPECT_TRUE(aOk); + EXPECT_NEAR(aGraph.Refs().Children().Entry(aChildRef).LocalLocation.Transformation().Value(1, 4), + aDx, + Precision::Confusion()); - const TopLoc_Location& aLoc = aGraph.Refs().Shells().Entry(aShellRef).LocalLocation; - EXPECT_FALSE(aLoc.IsIdentity()); - EXPECT_NEAR(aLoc.Transformation().Value(1, 4), aDx, Precision::Confusion()); - - // A second MoveRef composes (doubles the translation). - BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aShellRef), aTrsf); - const TopLoc_Location& aLoc2 = aGraph.Refs().Shells().Entry(aShellRef).LocalLocation; - EXPECT_NEAR(aLoc2.Transformation().Value(1, 4), 2.0 * aDx, Precision::Confusion()); + BRepGraph_Transform::MoveRef(aGraph, aChildRef, aTrsf); + EXPECT_NEAR(aGraph.Refs().Children().Entry(aChildRef).LocalLocation.Transformation().Value(1, 4), + 2.0 * aDx, + Precision::Confusion()); } -TEST(BRepGraph_TransformTest, MoveRef_ScaleRejected) +TEST(BRepGraph_TransformTest, MoveRef_OccurrenceRef_ComposesLocationAndRejectsScale) { - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); - const BRepGraph_ShellRefId aShellRef = - aGraph.Topo().Solids().Definition(aSolidId).ShellRefIds.Value(0); + const BRepGraph_ProductId aProductId = BRepGraph_ProductId::Start(); + ASSERT_FALSE(aGraph.Topo().Products().Relations(aProductId).OccurrenceRefIds.IsEmpty()); + const BRepGraph_OccurrenceRefId anOccRef = + aGraph.Topo().Products().Relations(aProductId).OccurrenceRefIds.First(); + + gp_Trsf aTrans; + aTrans.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); + EXPECT_TRUE(BRepGraph_Transform::MoveRef(aGraph, anOccRef, aTrans)); + EXPECT_NEAR( + aGraph.Refs().Occurrences().Entry(anOccRef).LocalLocation.Transformation().Value(1, 4), + 1.0, + Precision::Confusion()); gp_Trsf aScale; aScale.SetScale(gp_Pnt(), 2.0); // scale factor != 1 - const bool aOk = BRepGraph_Transform::MoveRef(aGraph, BRepGraph_RefId(aShellRef), aScale); + const bool aOk = BRepGraph_Transform::MoveRef(aGraph, anOccRef, aScale); EXPECT_FALSE(aOk); - // Location must remain unchanged (identity). - EXPECT_TRUE(aGraph.Refs().Shells().Entry(aShellRef).LocalLocation.IsIdentity()); + EXPECT_NEAR( + aGraph.Refs().Occurrences().Entry(anOccRef).LocalLocation.Transformation().Value(1, 4), + 1.0, + Precision::Confusion()); } TEST(BRepGraph_TransformTest, TransformNode_FaceKind_VertexPointsShifted) { - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const double aDx = 5.0, aDy = 10.0, aDz = 15.0; gp_Trsf aTrsf; @@ -380,97 +495,119 @@ TEST(BRepGraph_TransformTest, TransformNode_FaceKind_VertexPointsShifted) const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); const BRepGraph_NodeId aFaceNode(BRepGraph_NodeId::Kind::Face, aFaceId.Index); + BRepGraph aSourceSubgraph; + const BRepGraph_NodeId aCopiedFaceNode = + BRepGraph_Copy::CopyNode(aGraph, + aSourceSubgraph, + aFaceNode, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + ASSERT_TRUE(aCopiedFaceNode.IsValid()); + ASSERT_FALSE(aSourceSubgraph.IsEmpty()); - BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aFaceNode, aTrsf, true, false); - ASSERT_TRUE(aResult.IsDone()); + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aFaceNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); EXPECT_EQ(aResult.Topo().Faces().Nb(), 1); - - const int aNbV = aResult.Topo().Vertices().Nb(); - ASSERT_GT(aNbV, 0); - for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) - { - const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); - const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); - EXPECT_NEAR(aTrans.X(), anOrig.X() + aDx, Precision::Confusion()); - EXPECT_NEAR(aTrans.Y(), anOrig.Y() + aDy, Precision::Confusion()); - EXPECT_NEAR(aTrans.Z(), anOrig.Z() + aDz, Precision::Confusion()); - } + expectVerticesTransformed(aSourceSubgraph, aResult, aTrsf); } TEST(BRepGraph_TransformTest, TransformNode_ShellKind) { - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Shells().Nb(), 1); const BRepGraph_ShellId aShellId = BRepGraph_ShellId::Start(); const BRepGraph_NodeId aShellNode(BRepGraph_NodeId::Kind::Shell, aShellId.Index); + BRepGraph aSourceSubgraph; + const BRepGraph_NodeId aCopiedShellNode = + BRepGraph_Copy::CopyNode(aGraph, + aSourceSubgraph, + aShellNode, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + ASSERT_TRUE(aCopiedShellNode.IsValid()); + ASSERT_FALSE(aSourceSubgraph.IsEmpty()); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(100.0, 0.0, 0.0)); - BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aShellNode, aTrsf, true, false); - ASSERT_TRUE(aResult.IsDone()); + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aShellNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); // The shell copy should have the same number of faces as the source shell. EXPECT_EQ(aResult.Topo().Shells().Nb(), 1); EXPECT_EQ(aResult.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); // All vertices must be shifted by the translation. - const int aNbV = aGraph.Topo().Vertices().Nb(); - EXPECT_EQ(aResult.Topo().Vertices().Nb(), aNbV); - for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) - { - const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); - const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); - EXPECT_NEAR(aTrans.X(), anOrig.X() + 100.0, Precision::Confusion()); - EXPECT_NEAR(aTrans.Y(), anOrig.Y(), Precision::Confusion()); - EXPECT_NEAR(aTrans.Z(), anOrig.Z(), Precision::Confusion()); - } + expectVerticesTransformed(aSourceSubgraph, aResult, aTrsf); } TEST(BRepGraph_TransformTest, TransformNode_SolidKind) { - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Solids().Nb(), 1); const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); const BRepGraph_NodeId aSolidNode(BRepGraph_NodeId::Kind::Solid, aSolidId.Index); + BRepGraph aSourceSubgraph; + const BRepGraph_NodeId aCopiedSolidNode = + BRepGraph_Copy::CopyNode(aGraph, + aSourceSubgraph, + aSolidNode, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + ASSERT_TRUE(aCopiedSolidNode.IsValid()); + ASSERT_FALSE(aSourceSubgraph.IsEmpty()); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(0.0, 50.0, 0.0)); - BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aSolidNode, aTrsf, true, false); - ASSERT_TRUE(aResult.IsDone()); + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aSolidNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); EXPECT_EQ(aResult.Topo().Solids().Nb(), 1); EXPECT_EQ(aResult.Topo().Shells().Nb(), aGraph.Topo().Shells().Nb()); EXPECT_EQ(aResult.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); - const int aNbV = aGraph.Topo().Vertices().Nb(); - EXPECT_EQ(aResult.Topo().Vertices().Nb(), aNbV); - for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) - { - const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); - const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); - EXPECT_NEAR(aTrans.Y(), anOrig.Y() + 50.0, Precision::Confusion()); - } + expectVerticesTransformed(aSourceSubgraph, aResult, aTrsf); } TEST(BRepGraph_TransformTest, TransformNode_VertexKind) { - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Vertices().Nb(), 1); const BRepGraph_VertexId aVertexId = BRepGraph_VertexId::Start(); @@ -480,8 +617,15 @@ TEST(BRepGraph_TransformTest, TransformNode_VertexKind) gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); - BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aVertexNode, aTrsf, true, false); - ASSERT_TRUE(aResult.IsDone()); + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aVertexNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); EXPECT_EQ(aResult.Topo().Vertices().Nb(), 1); const gp_Pnt aTransPt = BRepGraph_Tool::Vertex::Pnt(aResult, BRepGraph_VertexId::Start()); @@ -501,34 +645,41 @@ TEST(BRepGraph_TransformTest, TransformNode_CompoundKind) aBB.Add(aCompound, aBox1.Shape()); aBB.Add(aCompound, aBox2.Shape()); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Compounds().Nb(), 1); const BRepGraph_CompoundId aCompoundId = BRepGraph_CompoundId::Start(); const BRepGraph_NodeId aCompoundNode(BRepGraph_NodeId::Kind::Compound, aCompoundId.Index); + BRepGraph aSourceSubgraph; + const BRepGraph_NodeId aCopiedCompoundNode = + BRepGraph_Copy::CopyNode(aGraph, + aSourceSubgraph, + aCompoundNode, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + ASSERT_TRUE(aCopiedCompoundNode.IsValid()); + ASSERT_FALSE(aSourceSubgraph.IsEmpty()); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(20.0, 0.0, 0.0)); - BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aCompoundNode, aTrsf, true, false); - ASSERT_TRUE(aResult.IsDone()); + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aCompoundNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); EXPECT_EQ(aResult.Topo().Compounds().Nb(), 1); EXPECT_EQ(aResult.Topo().Solids().Nb(), aGraph.Topo().Solids().Nb()); EXPECT_EQ(aResult.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); - const int aNbV = aGraph.Topo().Vertices().Nb(); - EXPECT_EQ(aResult.Topo().Vertices().Nb(), aNbV); - for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) - { - const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); - const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); - EXPECT_NEAR(aTrans.X(), anOrig.X() + 20.0, Precision::Confusion()); - EXPECT_NEAR(aTrans.Y(), anOrig.Y(), Precision::Confusion()); - } + expectVerticesTransformed(aSourceSubgraph, aResult, aTrsf); } TEST(BRepGraph_TransformTest, TransformNode_CompSolidKind) @@ -540,32 +691,40 @@ TEST(BRepGraph_TransformTest, TransformNode_CompSolidKind) BRepPrimAPI_MakeBox aBoxMaker(4.0, 4.0, 4.0); aBB.Add(aCompSolid, aBoxMaker.Shape()); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aCompSolid); - ASSERT_TRUE(aGraph.IsDone()); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompSolid); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().CompSolids().Nb(), 1); const BRepGraph_CompSolidId aCompSolidId = BRepGraph_CompSolidId::Start(); const BRepGraph_NodeId aCompSolidNode(BRepGraph_NodeId::Kind::CompSolid, aCompSolidId.Index); + BRepGraph aSourceSubgraph; + const BRepGraph_NodeId aCopiedCompSolidNode = + BRepGraph_Copy::CopyNode(aGraph, + aSourceSubgraph, + aCompSolidNode, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + ASSERT_TRUE(aCopiedCompSolidNode.IsValid()); + ASSERT_FALSE(aSourceSubgraph.IsEmpty()); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(0.0, 0.0, 7.0)); - BRepGraph aResult = - BRepGraph_Transform::TransformNode(aGraph, aCompSolidNode, aTrsf, true, false); - ASSERT_TRUE(aResult.IsDone()); + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aCompSolidNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); EXPECT_EQ(aResult.Topo().CompSolids().Nb(), 1); EXPECT_EQ(aResult.Topo().Solids().Nb(), aGraph.Topo().Solids().Nb()); - const int aNbV = aGraph.Topo().Vertices().Nb(); - for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) - { - const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); - const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); - EXPECT_NEAR(aTrans.Z(), anOrig.Z() + 7.0, Precision::Confusion()); - } + expectVerticesTransformed(aSourceSubgraph, aResult, aTrsf); } TEST(BRepGraph_TransformTest, TransformNode_NegativeScale_VertexPointsMirrored) @@ -573,55 +732,55 @@ TEST(BRepGraph_TransformTest, TransformNode_NegativeScale_VertexPointsMirrored) // Smoke-tests the negative-scale geometry path through TransformNode: every // vertex point should be mirrored about the origin and the result graph must // remain coherent (same face/edge/vertex counts, valid IsDone). - BRepPrimAPI_MakeBox aBoxMaker(10.0, 10.0, 10.0); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + BRepPrimAPI_MakeBox aBoxMaker(10.0, 10.0, 10.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); const BRepGraph_NodeId aSolidNode(BRepGraph_NodeId::Kind::Solid, aSolidId.Index); + BRepGraph aSourceSubgraph; + const BRepGraph_NodeId aCopiedSolidNode = + BRepGraph_Copy::CopyNode(aGraph, + aSourceSubgraph, + aSolidNode, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop); + ASSERT_TRUE(aCopiedSolidNode.IsValid()); + ASSERT_FALSE(aSourceSubgraph.IsEmpty()); gp_Trsf aTrsf; aTrsf.SetMirror(gp_Pnt(0.0, 0.0, 0.0)); - BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aSolidNode, aTrsf, true, false); - ASSERT_TRUE(aResult.IsDone()); + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aSolidNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Drop) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); EXPECT_EQ(aResult.Topo().Faces().Nb(), aGraph.Topo().Faces().Nb()); EXPECT_EQ(aResult.Topo().Vertices().Nb(), aGraph.Topo().Vertices().Nb()); - const int aNbV = aGraph.Topo().Vertices().Nb(); - for (BRepGraph_VertexId aVId(0); aVId.IsValid(aNbV); ++aVId) - { - const gp_Pnt anOrig = BRepGraph_Tool::Vertex::Pnt(aGraph, aVId); - const gp_Pnt aTrans = BRepGraph_Tool::Vertex::Pnt(aResult, aVId); - EXPECT_NEAR(aTrans.X(), -anOrig.X(), Precision::Confusion()); - EXPECT_NEAR(aTrans.Y(), -anOrig.Y(), Precision::Confusion()); - EXPECT_NEAR(aTrans.Z(), -anOrig.Z(), Precision::Confusion()); - } + expectVerticesTransformed(aSourceSubgraph, aResult, aTrsf); } -TEST(BRepGraph_TransformTest, TransformNode_CopyGeomAndMesh_LODCacheSurvives) +TEST(BRepGraph_TransformTest, TransformNode_CopyGeomAndMesh_DropsRuntimeCache) { - // Regression test for self-aliasing bug in applyMeshCopy: when TransformNode is called - // with theCopyGeom=true and theCopyMesh=true, the LOD face cache entries must survive. - // Previously, applyMeshCopy(aSubgraph, aSubgraph, ...) would clear the face cache before - // reading from it (source==dest), silently dropping all cached LOD triangulation entries. - BRepPrimAPI_MakeBox aBoxMaker(10.0, 10.0, 10.0); - BRepGraph aGraph; - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes = - BRepGraph_Builder::Add(aGraph, aBoxMaker.Shape()); - ASSERT_TRUE(aGraph.IsDone()); + // Regression test: when TransformNode is called with GeomPolicy::Copy and + // MeshPolicy::Copy, the runtime cache is not copied or transformed. + BRepPrimAPI_MakeBox aBoxMaker(10.0, 10.0, 10.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Faces().Nb(), 1); - // Attach a LOD cache entry on the first face. + // Attach a cached triangulation on the first face. const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); - occ::handle aLodTri1 = new Poly_Triangulation(3, 1, false); - aLodTri1->SetNode(1, gp_Pnt(0.0, 0.0, 0.0)); - aLodTri1->SetNode(2, gp_Pnt(1.0, 0.0, 0.0)); - aLodTri1->SetNode(3, gp_Pnt(0.0, 1.0, 0.0)); - aLodTri1->SetTriangle(1, Poly_Triangle(1, 2, 3)); - aLodTri1->Deflection(0.5); occ::handle aLodTri2 = new Poly_Triangulation(3, 1, false); aLodTri2->SetNode(1, gp_Pnt(0.0, 0.0, 0.0)); aLodTri2->SetNode(2, gp_Pnt(2.0, 0.0, 0.0)); @@ -629,13 +788,7 @@ TEST(BRepGraph_TransformTest, TransformNode_CopyGeomAndMesh_LODCacheSurvives) aLodTri2->SetTriangle(1, Poly_Triangle(1, 2, 3)); aLodTri2->Deflection(1.0); - const BRepGraph_TriangulationRepId aRep1 = - BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, aLodTri1); - const BRepGraph_TriangulationRepId aRep2 = - BRepGraph_Tool::Mesh::CreateTriangulationRep(aGraph, aLodTri2); - BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aRep1); - BRepGraph_Tool::Mesh::AppendCachedTriangulation(aGraph, aFaceId, aRep2); - BRepGraph_Tool::Mesh::SetCachedActiveIndex(aGraph, aFaceId, 1); + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aLodTri2); const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); const BRepGraph_NodeId aSolidNode = @@ -643,22 +796,118 @@ TEST(BRepGraph_TransformTest, TransformNode_CopyGeomAndMesh_LODCacheSurvives) gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(5.0, 0.0, 0.0)); - // This is the previously failing path: theCopyGeom=true, theCopyMesh=true. - BRepGraph aResult = BRepGraph_Transform::TransformNode(aGraph, aSolidNode, aTrsf, true, true); - ASSERT_TRUE(aResult.IsDone()); + // Previously failing path: GeomPolicy::Copy, MeshPolicy::Copy. + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aSolidNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Copy) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); - // Both LOD cache entries must survive the transform. - const BRepGraph_MeshCache::FaceMeshEntry* aEntry = aResult.Mesh().Faces().CachedMesh(aFaceId); - ASSERT_NE(aEntry, nullptr); - EXPECT_TRUE(aEntry->IsPresent()); - EXPECT_EQ(aEntry->TriangulationRepIds.Length(), 2); - EXPECT_EQ(aEntry->ActiveTriangulationIndex, 1); - - // The cached triangulation nodes must be shifted by the translation. - const BRepGraph_TriangulationRepId aResRep1 = aEntry->TriangulationRepIds.Value(0); - ASSERT_TRUE(aResRep1.IsValid(aResult.Mesh().Poly().NbTriangulations())); - const occ::handle& aResTri1 = - aResult.Mesh().Poly().TriangulationRep(aResRep1).Triangulation; - ASSERT_FALSE(aResTri1.IsNull()); - EXPECT_NEAR(aResTri1->Node(2).X(), 1.0 + 5.0, Precision::Confusion()); + // Runtime cache is not copied or transformed. + const BRepGraph_CacheMesh::FaceMeshEntry* aEntry = aResult.Mesh().Cache().Faces().Entry(aFaceId); + EXPECT_EQ(aEntry, nullptr); +} + +TEST(BRepGraph_TransformTest, TransformNode_CopyGeomAndMesh_CopiesPersistentMeshAndDropsCache) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 10.0, 10.0); + BRepGraph aGraph; + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = + aGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_CoEdgeId aCoEdgeId = firstCoEdgeOfFace(aGraph, aFaceId); + ASSERT_TRUE(aCoEdgeId.IsValid(aGraph.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId anEdgeId = aGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId; + ASSERT_TRUE(anEdgeId.IsValid(aGraph.Topo().Edges().Nb())); + + occ::handle anUnusedTri = new Poly_Triangulation(3, 1, false); + anUnusedTri->SetNode(1, gp_Pnt(100.0, 0.0, 0.0)); + anUnusedTri->SetNode(2, gp_Pnt(101.0, 0.0, 0.0)); + anUnusedTri->SetNode(3, gp_Pnt(100.0, 1.0, 0.0)); + anUnusedTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, anUnusedTri); + aGraph.Editor().Edges().SetPersistentPolygon3D( + anEdgeId, + makePolygon3D(gp_Pnt(100.0, 0.0, 0.0), gp_Pnt(101.0, 0.0, 0.0))); + aGraph.Editor().CoEdges().SetPersistentPolygon2D( + aCoEdgeId, + makePolygon2D(gp_Pnt2d(100.0, 0.0), gp_Pnt2d(101.0, 0.0))); + aGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, makePolygonOnTri()); + + occ::handle aTri = new Poly_Triangulation(3, 1, false); + aTri->SetNode(1, gp_Pnt(0.0, 0.0, 0.0)); + aTri->SetNode(2, gp_Pnt(1.0, 0.0, 0.0)); + aTri->SetNode(3, gp_Pnt(0.0, 1.0, 0.0)); + aTri->SetTriangle(1, Poly_Triangle(1, 2, 3)); + aGraph.Editor().Faces().SetPersistentTriangulation(aFaceId, aTri); + occ::handle aPolygon3D = + makePolygon3D(gp_Pnt(1.0, 2.0, 3.0), gp_Pnt(4.0, 5.0, 6.0)); + aGraph.Editor().Edges().SetPersistentPolygon3D(anEdgeId, aPolygon3D); + occ::handle aPolygon2D = makePolygon2D(gp_Pnt2d(7.0, 8.0), gp_Pnt2d(9.0, 10.0)); + aGraph.Editor().CoEdges().SetPersistentPolygon2D(aCoEdgeId, aPolygon2D); + occ::handle aPolygonOnTri = makePolygonOnTri(); + aGraph.Editor().CoEdges().SetPersistentPolygonOnTri(aCoEdgeId, aPolygonOnTri); + + aGraph.Mesh().Editor().Faces().SetCachedTriangulation(aFaceId, aTri); + aGraph.Mesh().Editor().Edges().SetCachedPolygon3D(anEdgeId, aPolygon3D); + aGraph.Mesh().Editor().CoEdges().SetCachedPolygon2D(aCoEdgeId, aPolygon2D); + aGraph.Mesh().Editor().CoEdges().AppendCachedPolygonOnTri(aCoEdgeId, aPolygonOnTri); + + const BRepGraph_NodeId aSolidNode(BRepGraph_SolidId::Start()); + gp_Trsf aTrsf; + aTrsf.SetTranslation(gp_Vec(5.0, 0.0, 0.0)); + + BRepGraph aResult; + ASSERT_TRUE(BRepGraph_Transform::TransformNode(aGraph, + aResult, + aSolidNode, + aTrsf, + BRepGraph_Copy::GeomPolicy::Copy, + BRepGraph_Copy::MeshPolicy::Copy) + .IsValid()); + ASSERT_FALSE(aResult.IsEmpty()); + + const BRepGraph_CoEdgeId aResultFirstCoEdgeId = firstCoEdgeOfFace(aResult, aFaceId); + ASSERT_TRUE(aResultFirstCoEdgeId.IsValid(aResult.Topo().CoEdges().Nb())); + const BRepGraph_EdgeId aResultEdgeId = + aResult.Topo().CoEdges().Definition(aResultFirstCoEdgeId).ChildEdgeId; + ASSERT_TRUE(aResultEdgeId.IsValid(aResult.Topo().Edges().Nb())); + + EXPECT_EQ(aResult.Mesh().Cache().Edges().Entry(aResultEdgeId), nullptr); + + const occ::handle& aPersistentPolygon3D = + aResult.Mesh().Persistent().Edges().Polygon3D(aResultEdgeId); + ASSERT_FALSE(aPersistentPolygon3D.IsNull()); + EXPECT_NEAR(aPersistentPolygon3D->Nodes().Value(1).X(), 1.0 + 5.0, Precision::Confusion()); + + const BRepGraph_CoEdgeId aCopiedCoEdgeId = aResultFirstCoEdgeId; + EXPECT_FALSE(aResult.Mesh().Cache().CoEdges().Has(aCopiedCoEdgeId)); + + const BRepGraphInc::CoEdgeDef& aCopiedCoEdgeDef = + aResult.Topo().CoEdges().Definition(aCopiedCoEdgeId); + const occ::handle& aPersistentPolygon2D = + aResult.Mesh().Persistent().CoEdges().PolygonOnSurface(aCopiedCoEdgeId); + ASSERT_FALSE(aPersistentPolygon2D.IsNull()); + EXPECT_NEAR(aPersistentPolygon2D->Nodes().Value(1).X(), 7.0, Precision::Confusion()); + + ASSERT_TRUE(aCopiedCoEdgeDef.FaceId.IsValid(aResult.Topo().Faces().Nb())); + const occ::handle& aCopiedTri = + aResult.Mesh().Persistent().Faces().Triangulation(aCopiedCoEdgeDef.FaceId); + ASSERT_FALSE(aCopiedTri.IsNull()); + EXPECT_NEAR(aCopiedTri->Node(2).X(), 1.0 + 5.0, Precision::Confusion()); + + const occ::handle& aPersistentPolygonOnTri = + aResult.Mesh().Persistent().Edges().PolygonOnTriangulation(aResultEdgeId, + aCopiedCoEdgeDef.FaceId); + ASSERT_FALSE(aPersistentPolygonOnTri.IsNull()); + const occ::handle& aPersistentTri = + aResult.Mesh().Persistent().Faces().Triangulation(aCopiedCoEdgeDef.FaceId); + ASSERT_FALSE(aPersistentTri.IsNull()); + EXPECT_NEAR(aPersistentTri->Node(2).X(), 1.0 + 5.0, Precision::Confusion()); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_TypedIdDispatch_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_TypedIdDispatch_Test.cxx index b048c315ac..28536287a2 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_TypedIdDispatch_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_TypedIdDispatch_Test.cxx @@ -13,7 +13,6 @@ #include #include -#include #include @@ -36,30 +35,15 @@ TEST(BRepGraph_TypedIdDispatchTest, VisitNodeId_ConvertsToMatchingTypedId) TEST(BRepGraph_TypedIdDispatchTest, VisitRefId_ConvertsToMatchingTypedId) { - const BRepGraph_RefId aRefId(BRepGraph_RefId::Kind::CoEdge, 6); + const BRepGraph_RefId aRefId(BRepGraph_RefId::Kind::Vertex, 6); - bool isCoEdge = false; + bool isVertex = false; const int anIdx = BRepGraph_RefId::Visit(aRefId, [&](const auto theTypedId) -> int { using TypeId = std::remove_cv_t; - isCoEdge = std::is_same_v; + isVertex = std::is_same_v; return theTypedId.Index; }); - EXPECT_TRUE(isCoEdge); + EXPECT_TRUE(isVertex); EXPECT_EQ(anIdx, 6); } - -TEST(BRepGraph_TypedIdDispatchTest, VisitRepId_ConvertsToMatchingTypedId) -{ - const BRepGraph_RepId aRepId(BRepGraph_RepId::Kind::PolygonOnTri, 2); - - bool isPolygonOnTri = false; - const int anIdx = BRepGraph_RepId::Visit(aRepId, [&](const auto theTypedId) -> int { - using TypeId = std::remove_cv_t; - isPolygonOnTri = std::is_same_v; - return theTypedId.Index; - }); - - EXPECT_TRUE(isPolygonOnTri); - EXPECT_EQ(anIdx, 2); -} diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Validate_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Validate_Test.cxx index 151a9c23f3..a0914c53d8 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Validate_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Validate_Test.cxx @@ -15,9 +15,10 @@ #include #include #include +#include #include #include -#include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -36,8 +36,20 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include @@ -46,22 +58,36 @@ namespace //================================================================================================= -NCollection_DynamicArray coEdgeRefsOfWire(const BRepGraph& theGraph, - const BRepGraph_WireId theWireId) +const NCollection_LinearVector& coEdgesOfWire(const BRepGraph& theGraph, + const BRepGraph_WireId theWireId) { - NCollection_DynamicArray aRefIds; - const BRepGraph_NodeId aParentNode = theWireId; - const BRepGraph::RefsView& aRefs = theGraph.Refs(); - const int aNbCoEdgeRefs = aRefs.CoEdges().Nb(); - for (BRepGraph_CoEdgeRefId aRefId(0); aRefId.IsValid(aNbCoEdgeRefs); ++aRefId) + return theGraph.Topo().Wires().Relations(theWireId).CoEdgeIds; +} + +BRepGraph_EdgeId edgeWithCurve(const BRepGraph& theGraph, + const BRepGraph_EdgeId theSkip = BRepGraph_EdgeId()) +{ + for (BRepGraph_EdgeIterator anIt(theGraph); anIt.More(); anIt.Next()) { - const BRepGraphInc::CoEdgeRef& aRef = aRefs.CoEdges().Entry(aRefId); - if (aRef.ParentId == aParentNode && !aRef.IsRemoved) + if (anIt.CurrentId() != theSkip && anIt.Current().Curve3DRepId.IsValid()) { - aRefIds.Append(aRefId); + return anIt.CurrentId(); } } - return aRefIds; + return BRepGraph_EdgeId(); +} + +bool hasErrorContaining(const BRepGraph_Validate::Result& theResult, const char* theNeedle) +{ + for (const BRepGraph_Validate::Issue& anIssue : theResult.Issues) + { + if (anIssue.Sev == BRepGraph_Validate::Severity::Error + && anIssue.Description.Search(theNeedle) >= 1) + { + return true; + } + } + return false; } } // namespace @@ -73,9 +99,8 @@ TEST(BRepGraph_ValidateTest, CleanGraph_NoIssues) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); const BRepGraph_Validate::Result aResult = BRepGraph_Validate::Perform(aGraph); EXPECT_TRUE(aResult.IsValid()); @@ -112,11 +137,11 @@ TEST(BRepGraph_ValidateTest, AfterGeomDeduplicate_NoIssues) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(aGraph, aCompound); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); - (void)BRepGraph_Deduplicate::Perform(aGraph); + [[maybe_unused]] const BRepGraph_Deduplicate::Result aDedupResult = + BRepGraph_Deduplicate::Perform(aGraph); const BRepGraph_Validate::Result aResult = BRepGraph_Validate::Perform(aGraph); EXPECT_TRUE(aResult.IsValid()); @@ -130,31 +155,35 @@ TEST(BRepGraph_ValidateTest, DetectsRemovedNodeReference) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); - // Find an edge that has a valid start vertex and remove it. - BRepGraph_VertexId aVtxToRemove; - const int aNbEdges = aGraph.Topo().Edges().Nb(); + // Find an edge that has a valid start vertex, then corrupt its ref to point to a removed vertex. + BRepGraph_VertexId aVtxToRemove; + BRepGraph_VertexRefId aRefToCorrupt; + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - const BRepGraphInc::VertexRef& aStartRef = - BRepGraph_Tool::Edge::StartVertexRef(aGraph, anEdgeId); - if (aStartRef.VertexDefId.IsValid()) + const BRepGraph_VertexRefId aStartRef = BRepGraph_Tool::Edge::StartVertexId(aGraph, anEdgeId); + const BRepGraph_Tool::VertexUsage aStart = BRepGraph_Tool::Vertex::Usage(aGraph, aStartRef); + if (aStart.IsValid()) { - aVtxToRemove = aStartRef.VertexDefId; + aRefToCorrupt = aStartRef; break; } } + aVtxToRemove = aGraph.Editor().Vertices().Add(gp_Pnt(100.0, 0.0, 0.0), Precision::Confusion()); ASSERT_TRUE(aVtxToRemove.IsValid()); + ASSERT_TRUE(aRefToCorrupt.IsValid()); - // Remove the vertex without fixing edges referencing it. aGraph.Editor().Gen().RemoveNode(aVtxToRemove); + BRepGraph_MutGuard aRefMut = + aGraph.Editor().Vertices().MutRef(aRefToCorrupt); + aRefMut.Internal().ChildVertexId = aVtxToRemove; const BRepGraph_Validate::Result aDefaultResult = BRepGraph_Validate::Perform(aGraph); - EXPECT_TRUE(aDefaultResult.IsValid()); + EXPECT_FALSE(aDefaultResult.IsValid()); const BRepGraph_Validate::Result anAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); @@ -169,18 +198,17 @@ TEST(BRepGraph_ValidateTest, WireConnectivity_DisconnectedEdges) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes4 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes4 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Wires().Nb(), 0); // Corrupt a wire by swapping its vertex reference to break connectivity. // Find a wire with at least 2 edges. BRepGraph_WireId aTargetWire; - const int aNbWires = aGraph.Topo().Wires().Nb(); + const uint32_t aNbWires = aGraph.Topo().Wires().Nb(); for (BRepGraph_WireId aWireId(0); aWireId.IsValid(aNbWires); ++aWireId) { - if (coEdgeRefsOfWire(aGraph, aWireId).Length() >= 2) + if (coEdgesOfWire(aGraph, aWireId).Size() >= 2) { aTargetWire = aWireId; break; @@ -189,13 +217,12 @@ TEST(BRepGraph_ValidateTest, WireConnectivity_DisconnectedEdges) ASSERT_TRUE(aTargetWire.IsValid()); // Get the first edge in the wire and corrupt its end vertex. - const NCollection_DynamicArray aWireRefIds = - coEdgeRefsOfWire(aGraph, aTargetWire); - ASSERT_GE(aWireRefIds.Length(), 1); - const BRepGraphInc::CoEdgeRef& aFirstCR = aGraph.Refs().CoEdges().Entry(aWireRefIds.Value(0)); + const NCollection_LinearVector& aWireCoEdgeIds = + coEdgesOfWire(aGraph, aTargetWire); + ASSERT_GE(aWireCoEdgeIds.Size(), 1); const BRepGraphInc::CoEdgeDef& aFirstCoEdge = - aGraph.Topo().CoEdges().Definition(BRepGraph_CoEdgeId(aFirstCR.CoEdgeDefId)); - const BRepGraph_NodeId aFirstEdgeId(aFirstCoEdge.EdgeDefId); + aGraph.Topo().CoEdges().Definition(aWireCoEdgeIds.Value(0)); + const BRepGraph_NodeId aFirstEdgeId(aFirstCoEdge.ChildEdgeId); ASSERT_TRUE(aFirstEdgeId.IsValid()); BRepGraph_MutGuard aFirstEdge = @@ -203,34 +230,35 @@ TEST(BRepGraph_ValidateTest, WireConnectivity_DisconnectedEdges) // Find a vertex different from the current end vertex. const BRepGraph_VertexId anOrigEndVtx = - aGraph.Refs().Vertices().Entry(aFirstEdge->EndVertexRefId).VertexDefId; + aGraph.Refs().Vertices().Entry(aFirstEdge->EndVertexRefId).ChildVertexId; const BRepGraph_VertexId anOrigStartVtx = - aGraph.Refs().Vertices().Entry(aFirstEdge->StartVertexRefId).VertexDefId; + aGraph.Refs().Vertices().Entry(aFirstEdge->StartVertexRefId).ChildVertexId; const BRepGraph_NodeId anOrigEnd = BRepGraph_NodeId(anOrigEndVtx); - const int aNbVertices = aGraph.Topo().Vertices().Nb(); + const uint32_t aNbVertices = aGraph.Topo().Vertices().Nb(); for (BRepGraph_VertexId aVertexId(0); aVertexId.IsValid(aNbVertices); ++aVertexId) { if (aVertexId != anOrigEnd && aVertexId != BRepGraph_NodeId(anOrigStartVtx)) { BRepGraph_MutGuard aMutEndRef = aGraph.Editor().Vertices().MutRef(aFirstEdge->EndVertexRefId); - aMutEndRef.Internal().VertexDefId = aVertexId; + aMutEndRef.Internal().ChildVertexId = aVertexId; break; } } - ASSERT_NE(aGraph.Refs().Vertices().Entry(aFirstEdge->EndVertexRefId).VertexDefId, anOrigEndVtx); + ASSERT_NE(aGraph.Refs().Vertices().Entry(aFirstEdge->EndVertexRefId).ChildVertexId, anOrigEndVtx); const BRepGraph_Validate::Result aResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); - EXPECT_FALSE(aResult.IsValid()); + EXPECT_FALSE(hasErrorContaining(aResult, "Wire edges not connected")); - // Check that at least one connectivity error was found. - bool aFoundConnectivity = false; - for (int anIdx = 0; anIdx < aResult.Issues.Length(); ++anIdx) + // Check that at least one connectivity warning was found. + bool aFoundConnectivity = false; + const uint32_t aNbIssues = static_cast(aResult.Issues.Size()); + for (uint32_t anIdx = 0; anIdx < aNbIssues; ++anIdx) { const BRepGraph_Validate::Issue& anIssue = aResult.Issues.Value(anIdx); - if (anIssue.Sev == BRepGraph_Validate::Severity::Error) + if (anIssue.Sev == BRepGraph_Validate::Severity::Warning) { TCollection_AsciiString aDesc = anIssue.Description; if (aDesc.Search("Wire edges not connected") > 0) @@ -250,15 +278,14 @@ TEST(BRepGraph_ValidateTest, BoundsCheck_InvalidIndex) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes5 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes5 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); // Corrupt edge's Curve3d to null. BRepGraph_MutGuard anEdge = aGraph.Editor().Edges().Mut(BRepGraph_EdgeId::Start()); - anEdge.Internal().Curve3DRepId = BRepGraph_Curve3DRepId(); + anEdge.Internal().Curve3DRepId = BRepGraph_EdgeCurve3DRepId(); const BRepGraph_Validate::Result aResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); @@ -273,24 +300,26 @@ TEST(BRepGraph_ValidateTest, AfterSplitEdge_ProducesSubEdges) BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes6 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); - const int anOrigEdgeCount = aGraph.Topo().Edges().Nb(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes6 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); + const uint32_t anOrigEdgeCount = aGraph.Topo().Edges().Nb(); // Find a non-degenerate edge with valid vertices to split. BRepGraph_EdgeId anEdgeId; double aSplitParam = 0.0; - const int aNbEdges = aGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anCandEdgeId(0); anCandEdgeId.IsValid(aNbEdges); ++anCandEdgeId) { const BRepGraphInc::EdgeDef& anEdgeDef = aGraph.Topo().Edges().Definition(anCandEdgeId); - if (!anEdgeDef.IsDegenerate && anEdgeDef.Curve3DRepId.IsValid() - && BRepGraph_Tool::Edge::StartVertexRef(aGraph, anCandEdgeId).VertexDefId.IsValid() - && BRepGraph_Tool::Edge::EndVertexRef(aGraph, anCandEdgeId).VertexDefId.IsValid()) + if (!BRepGraph_Tool::Edge::Degenerated(aGraph, anCandEdgeId) && anEdgeDef.Curve3DRepId.IsValid() + && BRepGraph_Tool::Edge::StartVertexId(aGraph, anCandEdgeId).IsValid() + && BRepGraph_Tool::Edge::EndVertexId(aGraph, anCandEdgeId).IsValid()) { - anEdgeId = anCandEdgeId; - aSplitParam = 0.5 * (anEdgeDef.ParamFirst + anEdgeDef.ParamLast); + anEdgeId = anCandEdgeId; + { + const auto _r = BRepGraph_Tool::Edge::Range(aGraph, anCandEdgeId); + aSplitParam = 0.5 * (_r.first + _r.second); + } break; } } @@ -311,33 +340,32 @@ TEST(BRepGraph_ValidateTest, AfterSplitEdge_ProducesSubEdges) EXPECT_TRUE(aSubB.IsValid()); // Original edge should be marked removed. - EXPECT_TRUE(aGraph.Topo().Edges().Definition(anEdgeId).IsRemoved); + EXPECT_TRUE(anEdgeId.IsRemoved(aGraph)); // Full Audit must remain clean: every new CoEdge must carry a Curve2DRep, - // no orphan VertexRefs, no reverse-index drift. + // no orphan VertexRefs, no relation-table drift. const BRepGraph_Validate::Result anAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); EXPECT_TRUE(anAuditResult.IsValid()) << "Audit must remain clean after splitting a non-seam box edge"; } -TEST(BRepGraph_ValidateTest, CorruptedPCurve_FaceDefIdOutOfBounds) +TEST(BRepGraph_ValidateTest, CorruptedPCurve_FaceIdOutOfBounds) { BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes7 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes7 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); - // Corrupt a CoEdge's FaceDefId to an out-of-range value. + // Corrupt a CoEdge's FaceId to an out-of-range value. ASSERT_GT(aGraph.Topo().CoEdges().Nb(), 0); BRepGraph_MutGuard aCoEdgeDef = aGraph.Editor().CoEdges().Mut(BRepGraph_CoEdgeId::Start()); - aCoEdgeDef.Internal().FaceDefId = BRepGraph_FaceId(aGraph.Topo().Faces().Nb() + 999); + aCoEdgeDef.Internal().FaceId = BRepGraph_FaceId(aGraph.Topo().Faces().Nb() + 999); const BRepGraph_Validate::Result aDefaultResult = BRepGraph_Validate::Perform(aGraph); EXPECT_FALSE(aDefaultResult.IsValid()); @@ -351,46 +379,24 @@ TEST(BRepGraph_ValidateTest, CorruptedPCurve_FaceDefIdOutOfBounds) EXPECT_FALSE(anAuditResult.IsValid()); } -TEST(BRepGraph_ValidateTest, LightweightAndAudit_DetectActiveCountDrift) +TEST(BRepGraph_ValidateTest, RemoveNodeMaintainsActiveCount) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes8 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes8 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); - const int aNbActiveFacesBefore = aGraph.Topo().Faces().NbActive(); + const uint32_t aNbActiveFacesBefore = aGraph.Topo().Faces().NbActive(); ASSERT_GT(aNbActiveFacesBefore, 0); - // Intentionally bypass RemoveNode() to simulate counter drift bug class. - BRepGraph_MutGuard aFaceDef = - aGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start()); - aFaceDef.Internal().IsRemoved = true; - - const BRepGraph_Validate::Result aLightResult = - BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); - EXPECT_FALSE(aLightResult.IsValid()); - EXPECT_GT(aLightResult.NbIssues(BRepGraph_Validate::Severity::Error), 0); - - bool aFoundBoundaryActiveCountMismatch = false; - for (int anIdx = 0; anIdx < aLightResult.Issues.Length(); ++anIdx) - { - const TCollection_AsciiString& aDesc = aLightResult.Issues.Value(anIdx).Description; - if (aDesc.Search("Mutation boundary active count mismatch for Faces") > 0) - { - aFoundBoundaryActiveCountMismatch = true; - break; - } - } - EXPECT_TRUE(aFoundBoundaryActiveCountMismatch); + aGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(BRepGraph_FaceId::Start())); const BRepGraph_Validate::Result anAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); - EXPECT_FALSE(anAuditResult.IsValid()); - EXPECT_GT(anAuditResult.NbIssues(BRepGraph_Validate::Severity::Error), 0); bool aFoundActiveCountMismatch = false; - for (int anIdx = 0; anIdx < anAuditResult.Issues.Length(); ++anIdx) + for (size_t anIdx = 0; anIdx < anAuditResult.Issues.Size(); ++anIdx) { const TCollection_AsciiString& aDesc = anAuditResult.Issues.Value(anIdx).Description; if (aDesc.Search("NbActiveFaces mismatch") > 0) @@ -399,36 +405,35 @@ TEST(BRepGraph_ValidateTest, LightweightAndAudit_DetectActiveCountDrift) break; } } - EXPECT_TRUE(aFoundActiveCountMismatch); - EXPECT_EQ(aGraph.Topo().Faces().NbActive(), aNbActiveFacesBefore); + EXPECT_FALSE(aFoundActiveCountMismatch); + EXPECT_EQ(aGraph.Topo().Faces().NbActive(), aNbActiveFacesBefore - 1); } TEST(BRepGraph_ValidateTest, Audit_ValidatesCoEdgeUIDsFromBuilderWireCreation) { BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes9 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes9 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Edges().Nb(), 0); - NCollection_DynamicArray> anEdges; - anEdges.Append(std::make_pair(BRepGraph_EdgeId::Start(), TopAbs_FORWARD)); - const BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(anEdges); + NCollection_LinearVector aCoEdgeIds; + aCoEdgeIds.Append(aGraph.Editor().CoEdges().Add(BRepGraph_EdgeId::Start(), TopAbs_FORWARD)); + const BRepGraph_WireId aWireId = aGraph.Editor().Wires().Add(aCoEdgeIds.ToArray1()); ASSERT_TRUE(aWireId.IsValid()); - const NCollection_DynamicArray aWireRefIds = - coEdgeRefsOfWire(aGraph, aWireId); - ASSERT_EQ(aWireRefIds.Length(), 1); - const BRepGraph_NodeId aCoEdgeId = - BRepGraph_CoEdgeId(aGraph.Refs().CoEdges().Entry(aWireRefIds.Value(0)).CoEdgeDefId.Index); + const NCollection_LinearVector& aWireCoEdgeIds = + coEdgesOfWire(aGraph, aWireId); + ASSERT_EQ(aWireCoEdgeIds.Size(), 1); + const BRepGraph_NodeId aCoEdgeId = aWireCoEdgeIds.Value(0); EXPECT_TRUE(aGraph.UIDs().Of(aCoEdgeId).IsValid()); const BRepGraph_Validate::Result anAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); if (!anAuditResult.IsValid()) { - for (int anIdx = 0; anIdx < anAuditResult.Issues.Length(); ++anIdx) + for (size_t anIdx = 0; anIdx < anAuditResult.Issues.Size(); ++anIdx) { const BRepGraph_Validate::Issue& anIssue = anAuditResult.Issues.Value(anIdx); ADD_FAILURE() << "Issue[" << anIdx << "] kind=" << static_cast(anIssue.NodeId.NodeKind) @@ -446,30 +451,30 @@ TEST(BRepGraph_ValidateTest, Audit_ValidatesCoEdgeUIDsFromBuilderWireCreation) TEST(BRepGraph_ValidateTest, AssemblyGraph_ValidProduct_NoIssuesInAudit) { - // Build a box; BRepGraph_Builder::Add() auto-creates a root part product. + // Build a box; BRepGraph::ShapesView::Add() auto-creates a root part product. const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes10 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes10 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Products().Nb(), 1); - // BRepGraph_Builder::Add() auto-creates the part product at index 0. + // BRepGraph::ShapesView::Add() auto-creates the part product at index 0. const BRepGraph_ProductId aPartProduct = BRepGraph_ProductId::Start(); ASSERT_TRUE(aGraph.Topo().Products().IsPart(aPartProduct)); // Explicitly create an assembly product and add two occurrences of the part. - const BRepGraph_ProductId aAssemblyProduct = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aAssemblyProduct = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyProduct); ASSERT_TRUE(aAssemblyProduct.IsValid()); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(20.0, 0.0, 0.0)); const BRepGraph_OccurrenceId anOcc1 = - aGraph.Editor().Products().LinkProducts(aAssemblyProduct, aPartProduct, TopLoc_Location()); + aGraph.Editor().Products().Append(aAssemblyProduct, aPartProduct, TopLoc_Location()); const BRepGraph_OccurrenceId anOcc2 = - aGraph.Editor().Products().LinkProducts(aAssemblyProduct, aPartProduct, TopLoc_Location(aTrsf)); + aGraph.Editor().Products().Append(aAssemblyProduct, aPartProduct, TopLoc_Location(aTrsf)); ASSERT_TRUE(anOcc1.IsValid()); ASSERT_TRUE(anOcc2.IsValid()); @@ -477,7 +482,7 @@ TEST(BRepGraph_ValidateTest, AssemblyGraph_ValidProduct_NoIssuesInAudit) EXPECT_TRUE(aGraph.Topo().Products().IsAssembly(aAssemblyProduct)); EXPECT_EQ(aGraph.Topo().Products().NbComponents(aAssemblyProduct), 2); - // Rebuild reverse index after assembly modifications. + // Rebuild relation tables after assembly modifications. aGraph.Editor().CommitMutation(); // Audit should pass on the explicitly constructed assembly. @@ -485,7 +490,7 @@ TEST(BRepGraph_ValidateTest, AssemblyGraph_ValidProduct_NoIssuesInAudit) BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); if (!aAuditResult.IsValid()) { - for (int anIdx = 0; anIdx < aAuditResult.Issues.Length(); ++anIdx) + for (size_t anIdx = 0; anIdx < aAuditResult.Issues.Size(); ++anIdx) { const BRepGraph_Validate::Issue& anIssue = aAuditResult.Issues.Value(anIdx); ADD_FAILURE() << "Issue[" << anIdx << "] kind=" << static_cast(anIssue.NodeId.NodeKind) @@ -496,58 +501,106 @@ TEST(BRepGraph_ValidateTest, AssemblyGraph_ValidProduct_NoIssuesInAudit) EXPECT_TRUE(aAuditResult.IsValid()); } -TEST(BRepGraph_ValidateTest, AssemblyGraph_CorruptedOccurrenceChildDefId_DetectedByAudit) +TEST(BRepGraph_ValidateTest, DocumentRootReferencedByOccurrence_Detected) { const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes11 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_ProductId aPartProduct = BRepGraph_ProductId::Start(); + ASSERT_TRUE(aGraph.Topo().Products().IsPart(aPartProduct)); + + const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootAssembly); + ASSERT_TRUE(aRootAssembly.IsValid()); + ASSERT_TRUE( + aGraph.Editor().Products().Append(aRootAssembly, aPartProduct, TopLoc_Location()).IsValid()); + + ASSERT_EQ(aGraph.RootProductIds().Size(), 1); + ASSERT_EQ(aGraph.RootProductIds().First(), aRootAssembly); + + // Deliberately reintroduce the child Product as a document root. This is the + // malformed state that makes consumers start a tree from Product[0] even + // though Product[0] already has an assembly parent. + aGraph.Editor().Products().AppendDocumentRoot(aPartProduct); + + const BRepGraph_Validate::Result aLightResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); + EXPECT_FALSE(aLightResult.IsValid()); + + const BRepGraph_Validate::Result aAuditResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_FALSE(aAuditResult.IsValid()); + + bool aFoundRootIssue = false; + for (size_t anIdx = 0; anIdx < aAuditResult.Issues.Size(); ++anIdx) + { + const BRepGraph_Validate::Issue& anIssue = aAuditResult.Issues.Value(anIdx); + if (anIssue.Sev == BRepGraph_Validate::Severity::Error + && anIssue.NodeId == BRepGraph_NodeId(aPartProduct) + && anIssue.Description.Search("Document root Product is referenced") >= 0) + { + aFoundRootIssue = true; + break; + } + } + EXPECT_TRUE(aFoundRootIssue); +} + +TEST(BRepGraph_ValidateTest, AssemblyGraph_CorruptedOccurrenceChildNodeId_DetectedByAudit) +{ + const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); + + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes11 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GE(aGraph.Topo().Products().Nb(), 1); ASSERT_GE(aGraph.Topo().Occurrences().Nb(), 1); - // Corrupt the first occurrence's ChildDefId to an out-of-bounds value. + // Corrupt the first occurrence's ChildNodeId to an out-of-bounds value. BRepGraph_MutGuard anOccDef = aGraph.Editor().Occurrences().Mut(BRepGraph_OccurrenceId::Start()); - anOccDef.Internal().ChildDefId = + anOccDef.Internal().ChildNodeId = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Solid, aGraph.Topo().Solids().Nb() + 999); const BRepGraph_Validate::Result aAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); EXPECT_FALSE(aAuditResult.IsValid()) - << "Occurrence with out-of-bounds ChildDefId should be detected by audit."; + << "Occurrence with out-of-bounds ChildNodeId should be detected by audit."; EXPECT_GT(aAuditResult.NbIssues(BRepGraph_Validate::Severity::Error), 0); // Verify the specific error message. bool aFoundExpectedError = false; - for (int anIdx = 0; anIdx < aAuditResult.Issues.Length(); ++anIdx) + for (size_t anIdx = 0; anIdx < aAuditResult.Issues.Size(); ++anIdx) { const BRepGraph_Validate::Issue& anIssue = aAuditResult.Issues.Value(anIdx); if (anIssue.Sev == BRepGraph_Validate::Severity::Error - && anIssue.Description.Search("ChildDefId invalid") > 0) + && anIssue.Description.Search("ChildNodeId invalid") > 0) { aFoundExpectedError = true; break; } } - EXPECT_TRUE(aFoundExpectedError) << "Audit should report 'OccurrenceDef.ChildDefId invalid'."; + EXPECT_TRUE(aFoundExpectedError) << "Audit should report 'OccurrenceDef.ChildNodeId invalid'."; } TEST(BRepGraph_ValidateTest, - AssemblyGraph_CorruptedOccurrenceChildDefId_ProductIndex_DetectedByAudit) + AssemblyGraph_CorruptedOccurrenceChildNodeId_ProductIndex_DetectedByAudit) { const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape(); BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes12 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes12 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); // Create an assembly with one occurrence. - const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().CreateEmptyProduct(); + const BRepGraph_ProductId aRootAssembly = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aRootAssembly); ASSERT_TRUE(aRootAssembly.IsValid()); const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); @@ -555,34 +608,34 @@ TEST(BRepGraph_ValidateTest, ASSERT_TRUE(aPartId.IsValid()); const BRepGraph_OccurrenceId anOccId = - aGraph.Editor().Products().LinkProducts(aRootAssembly, aPartId, TopLoc_Location()); + aGraph.Editor().Products().Append(aRootAssembly, aPartId, TopLoc_Location()); ASSERT_TRUE(anOccId.IsValid()); - // Corrupt the occurrence's ChildDefId to an invalid index. + // Corrupt the occurrence's ChildNodeId to an invalid index. BRepGraph_MutGuard anOccDef = aGraph.Editor().Occurrences().Mut(anOccId); - anOccDef.Internal().ChildDefId = + anOccDef.Internal().ChildNodeId = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Product, aGraph.Topo().Products().Nb() + 999); const BRepGraph_Validate::Result aAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); EXPECT_FALSE(aAuditResult.IsValid()) - << "Occurrence with out-of-bounds ChildDefId should be detected by audit."; + << "Occurrence with out-of-bounds ChildNodeId should be detected by audit."; EXPECT_GT(aAuditResult.NbIssues(BRepGraph_Validate::Severity::Error), 0); // Verify the specific error message. bool aFoundExpectedError = false; - for (int anIdx = 0; anIdx < aAuditResult.Issues.Length(); ++anIdx) + for (size_t anIdx = 0; anIdx < aAuditResult.Issues.Size(); ++anIdx) { const BRepGraph_Validate::Issue& anIssue = aAuditResult.Issues.Value(anIdx); if (anIssue.Sev == BRepGraph_Validate::Severity::Error - && anIssue.Description.Search("ChildDefId invalid") > 0) + && anIssue.Description.Search("ChildNodeId invalid") > 0) { aFoundExpectedError = true; break; } } - EXPECT_TRUE(aFoundExpectedError) << "Audit should report 'OccurrenceDef.ChildDefId invalid'."; + EXPECT_TRUE(aFoundExpectedError) << "Audit should report 'OccurrenceDef.ChildNodeId invalid'."; } TEST(BRepGraph_ValidateTest, AssemblyGraph_OccurrenceChildRefersToOccurrence_DetectedByAudit) @@ -591,14 +644,13 @@ TEST(BRepGraph_ValidateTest, AssemblyGraph_OccurrenceChildRefersToOccurrence_Det BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes13 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes13 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Occurrences().Nb(), 0); BRepGraph_MutGuard anOccDef = aGraph.Editor().Occurrences().Mut(BRepGraph_OccurrenceId::Start()); - anOccDef.Internal().ChildDefId = BRepGraph_OccurrenceId::Start(); + anOccDef.Internal().ChildNodeId = BRepGraph_OccurrenceId::Start(); const BRepGraph_Validate::Result aAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); @@ -606,7 +658,7 @@ TEST(BRepGraph_ValidateTest, AssemblyGraph_OccurrenceChildRefersToOccurrence_Det << "Occurrence child pointing to another occurrence should be detected by audit."; bool aFoundExpectedError = false; - for (int anIdx = 0; anIdx < aAuditResult.Issues.Length(); ++anIdx) + for (size_t anIdx = 0; anIdx < aAuditResult.Issues.Size(); ++anIdx) { const BRepGraph_Validate::Issue& anIssue = aAuditResult.Issues.Value(anIdx); if (anIssue.Sev == BRepGraph_Validate::Severity::Error @@ -617,54 +669,57 @@ TEST(BRepGraph_ValidateTest, AssemblyGraph_OccurrenceChildRefersToOccurrence_Det } } EXPECT_TRUE(aFoundExpectedError) - << "Audit should report that OccurrenceDef.ChildDefId cannot reference an Occurrence."; + << "Audit should report that OccurrenceDef.ChildNodeId cannot reference an Occurrence."; } TEST(BRepGraph_ValidateTest, LightweightVsAudit_RemovedVertexReference_Differential) { - // Verifies that removed-node isolation is an Audit-only check. - // RemoveNode(vertex) correctly updates active counts (Lightweight passes) - // but leaves edges referencing the removed vertex (Audit detects). + // Removed-node isolation is now part of the lightweight/default contract too: + // active refs must not target removed definitions. BRepGraph aGraph; aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes14 = - BRepGraph_Builder::Add(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes14 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); ASSERT_GT(aGraph.Topo().Vertices().Nb(), 0); // Find a vertex referenced by at least one edge. BRepGraph_VertexId aVtxToRemove; - const int aNbEdges = aGraph.Topo().Edges().Nb(); + const uint32_t aNbEdges = aGraph.Topo().Edges().Nb(); for (BRepGraph_EdgeId anEdgeId(0); anEdgeId.IsValid(aNbEdges); ++anEdgeId) { - const BRepGraphInc::VertexRef& aStartRef = - BRepGraph_Tool::Edge::StartVertexRef(aGraph, anEdgeId); - if (aStartRef.VertexDefId.IsValid()) + const BRepGraph_VertexRefId aStartRef = BRepGraph_Tool::Edge::StartVertexId(aGraph, anEdgeId); + const BRepGraph_Tool::VertexUsage aStart = BRepGraph_Tool::Vertex::Usage(aGraph, aStartRef); + if (aStart.IsValid()) { - aVtxToRemove = aStartRef.VertexDefId; + aVtxToRemove = aStart.DefId; break; } } ASSERT_TRUE(aVtxToRemove.IsValid()) << "Need a vertex referenced by an edge."; - // Remove the vertex. RemoveNode correctly decrements active count - // but does NOT fix edges that still reference it. + // Remove a loose vertex, then corrupt an active edge ref to point at it. + const BRepGraph_VertexRefId aRefToCorrupt = + BRepGraph_Tool::Edge::StartVertexId(aGraph, BRepGraph_EdgeId::Start()); + ASSERT_TRUE(aRefToCorrupt.IsValid()); + aVtxToRemove = aGraph.Editor().Vertices().Add(gp_Pnt(100.0, 0.0, 0.0), Precision::Confusion()); aGraph.Editor().Gen().RemoveNode(aVtxToRemove); + BRepGraph_MutGuard aRefMut = + aGraph.Editor().Vertices().MutRef(aRefToCorrupt); + aRefMut.Internal().ChildVertexId = aVtxToRemove; - // Lightweight only checks active counts - should pass. const BRepGraph_Validate::Result aLightResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Lightweight()); - EXPECT_TRUE(aLightResult.IsValid()) << "Lightweight should not check removed-node isolation."; + EXPECT_FALSE(aLightResult.IsValid()); - // Audit runs checkRemovedNodeIsolation - should detect the dangling reference. const BRepGraph_Validate::Result aAuditResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); - EXPECT_FALSE(aAuditResult.IsValid()) << "Audit should detect edges referencing a removed vertex."; + EXPECT_FALSE(aAuditResult.IsValid()); EXPECT_GT(aAuditResult.NbIssues(BRepGraph_Validate::Severity::Error), 0); // Verify the specific error message. bool aFoundExpectedError = false; - for (int anIdx = 0; anIdx < aAuditResult.Issues.Length(); ++anIdx) + for (size_t anIdx = 0; anIdx < aAuditResult.Issues.Size(); ++anIdx) { const BRepGraph_Validate::Issue& anIssue = aAuditResult.Issues.Value(anIdx); if (anIssue.Sev == BRepGraph_Validate::Severity::Error @@ -678,46 +733,448 @@ TEST(BRepGraph_ValidateTest, LightweightVsAudit_RemovedVertexReference_Different << "Audit should report 'Non-removed EdgeDef references removed StartVertexEntity'."; } -TEST(BRepGraph_ValidateTest, Audit_DetectsOrphanWireRef_AfterFaceRemoval) +TEST(BRepGraph_ValidateTest, Audit_WarnsSurfacedFaceWithNoWireRefs) { - // Build a simple box; pick one face; corrupt its WireRef so the parent points - // at a removed face index. The new audit block for orphan WireRefs must flag it. BRepGraph aGraph; const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape(); aGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes15 = - BRepGraph_Builder::Add(aGraph, aBox); - ASSERT_TRUE(aGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes15 = aGraph.Shapes().Add(aBox); + ASSERT_FALSE(aGraph.IsEmpty()); - // Find a live wire ref and rewrite its ParentId to a definitely-invalid Face id. - const BRepGraph::RefsView& aRefs = aGraph.Refs(); - ASSERT_GT(aRefs.Wires().Nb(), 0); bool aDidCorrupt = false; - for (int aRefIdx = 0; aRefIdx < aRefs.Wires().Nb() && !aDidCorrupt; ++aRefIdx) + for (BRepGraph_FaceIterator aFaceIt(aGraph); aFaceIt.More() && !aDidCorrupt; aFaceIt.Next()) { - const BRepGraph_WireRefId aRefId(aRefIdx); - const BRepGraphInc::WireRef& aRef = aRefs.Wires().Entry(aRefId); - if (aRef.IsRemoved) + const BRepGraph_FaceId aFaceId = aFaceIt.CurrentId(); + const BRepGraphInc::FaceRelations& aFaceRelations = aGraph.Topo().Faces().Relations(aFaceId); + if (aFaceRelations.WireRefIds.IsEmpty()) { continue; } - BRepGraph_MutGuard aMut = aGraph.Editor().Wires().MutRef(aRefId); - aMut.Internal().ParentId = BRepGraph_NodeId(BRepGraph_NodeId::Kind::Face, 9999); - aDidCorrupt = true; + ASSERT_TRUE(aGraph.Editor().Faces().RemoveWire(aFaceId, aFaceRelations.WireRefIds.Last())); + aDidCorrupt = true; } ASSERT_TRUE(aDidCorrupt); const BRepGraph_Validate::Result aResult = BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_TRUE(aResult.IsValid()); + EXPECT_FALSE(aResult.Issues.IsEmpty()); + EXPECT_EQ(aResult.Issues.First().Sev, BRepGraph_Validate::Severity::Warning); + EXPECT_NE(aResult.Issues.First().Description.Search("no wire refs"), -1); +} + +TEST(BRepGraph_ValidateTest, Audit_DetectsSharedOwnedUse) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes43 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_EdgeId anEdgeId1 = edgeWithCurve(aGraph); + ASSERT_TRUE(anEdgeId1.IsValid()); + const BRepGraph_EdgeId anEdgeId2 = edgeWithCurve(aGraph, anEdgeId1); + ASSERT_TRUE(anEdgeId2.IsValid()); + const BRepGraph_EdgeCurve3DRepId aRepId1 = + aGraph.Topo().Edges().Definition(anEdgeId1).Curve3DRepId; + ASSERT_TRUE(aRepId1.IsValid()); + { + BRepGraph_MutGuard anEdge = aGraph.Editor().Edges().Mut(anEdgeId2); + anEdge.Internal().Curve3DRepId = aRepId1; + } + + const BRepGraph_Validate::Result aResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); EXPECT_FALSE(aResult.IsValid()); - bool aFound = false; - for (int i = 0; i < aResult.Issues.Length(); ++i) + EXPECT_TRUE(hasErrorContaining(aResult, "Active EdgeCurve3DRep has multiple owners")); +} + +TEST(BRepGraph_ValidateTest, Audit_DetectsActiveOwnedUseWithoutOwner) +{ + BRepGraph aGraph; + aGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes44 = + aGraph.Shapes().Add(BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()); + ASSERT_FALSE(aGraph.IsEmpty()); + + const BRepGraph_EdgeId anEdgeId = edgeWithCurve(aGraph); + ASSERT_TRUE(anEdgeId.IsValid()); + const BRepGraph_EdgeCurve3DRepId aRepId = aGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId; + ASSERT_TRUE(aRepId.IsValid()); + { - if (aResult.Issues.Value(i).Description.Search("Orphan WireRef") >= 0) + BRepGraph_MutGuard anEdge = aGraph.Editor().Edges().Mut(anEdgeId); + anEdge.Internal().Curve3DRepId = BRepGraph_EdgeCurve3DRepId(); + } + + const BRepGraph_Validate::Result aResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_FALSE(aResult.IsValid()); + EXPECT_TRUE(hasErrorContaining(aResult, "Active EdgeCurve3DRep has no owner")); +} + +TEST(BRepGraph_ValidateTest, Synthetic_Box_AuditClean) +{ + BRepGraph aGraph; + aGraph.Clear(); + + auto& aVtxOps = aGraph.Editor().Vertices(); + const BRepGraph_VertexId aV0 = aVtxOps.Add(gp_Pnt(0, 0, 0), 1e-7); + const BRepGraph_VertexId aV1 = aVtxOps.Add(gp_Pnt(10, 0, 0), 1e-7); + const BRepGraph_VertexId aV2 = aVtxOps.Add(gp_Pnt(10, 20, 0), 1e-7); + const BRepGraph_VertexId aV3 = aVtxOps.Add(gp_Pnt(0, 20, 0), 1e-7); + const BRepGraph_VertexId aV4 = aVtxOps.Add(gp_Pnt(0, 0, 30), 1e-7); + const BRepGraph_VertexId aV5 = aVtxOps.Add(gp_Pnt(10, 0, 30), 1e-7); + const BRepGraph_VertexId aV6 = aVtxOps.Add(gp_Pnt(10, 20, 30), 1e-7); + const BRepGraph_VertexId aV7 = aVtxOps.Add(gp_Pnt(0, 20, 30), 1e-7); + + auto& anEdgeOps = aGraph.Editor().Edges(); + const BRepGraph_EdgeId aE0 = + anEdgeOps.Add(aV0, aV1, new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE1 = + anEdgeOps.Add(aV1, aV2, new Geom_Line(gp_Pnt(10, 0, 0), gp_Dir(0, 1, 0)), 0.0, 20.0, 1e-7); + const BRepGraph_EdgeId aE2 = + anEdgeOps.Add(aV2, aV3, new Geom_Line(gp_Pnt(10, 20, 0), gp_Dir(-1, 0, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE3 = + anEdgeOps.Add(aV3, aV0, new Geom_Line(gp_Pnt(0, 20, 0), gp_Dir(0, -1, 0)), 0.0, 20.0, 1e-7); + const BRepGraph_EdgeId aE4 = + anEdgeOps.Add(aV4, aV5, new Geom_Line(gp_Pnt(0, 0, 30), gp_Dir(1, 0, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE5 = + anEdgeOps.Add(aV5, aV6, new Geom_Line(gp_Pnt(10, 0, 30), gp_Dir(0, 1, 0)), 0.0, 20.0, 1e-7); + const BRepGraph_EdgeId aE6 = + anEdgeOps.Add(aV6, aV7, new Geom_Line(gp_Pnt(10, 20, 30), gp_Dir(-1, 0, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE7 = + anEdgeOps.Add(aV7, aV4, new Geom_Line(gp_Pnt(0, 20, 30), gp_Dir(0, -1, 0)), 0.0, 20.0, 1e-7); + const BRepGraph_EdgeId aE8 = + anEdgeOps.Add(aV0, aV4, new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 0.0, 30.0, 1e-7); + const BRepGraph_EdgeId aE9 = + anEdgeOps.Add(aV1, aV5, new Geom_Line(gp_Pnt(10, 0, 0), gp_Dir(0, 0, 1)), 0.0, 30.0, 1e-7); + const BRepGraph_EdgeId aE10 = + anEdgeOps.Add(aV2, aV6, new Geom_Line(gp_Pnt(10, 20, 0), gp_Dir(0, 0, 1)), 0.0, 30.0, 1e-7); + const BRepGraph_EdgeId aE11 = + anEdgeOps.Add(aV3, aV7, new Geom_Line(gp_Pnt(0, 20, 0), gp_Dir(0, 0, 1)), 0.0, 30.0, 1e-7); + + auto& aCoEdgeOps = aGraph.Editor().CoEdges(); + auto makeWire = [&](std::initializer_list theEdges, + std::initializer_list theOrients) -> BRepGraph_WireId { + NCollection_LinearVector aCEs; + auto anOrientIt = theOrients.begin(); + for (const BRepGraph_EdgeId& anEdge : theEdges) { - aFound = true; - break; + aCEs.Append(aCoEdgeOps.Add(anEdge, *anOrientIt)); + ++anOrientIt; } + return aGraph.Editor().Wires().Add(aCEs.ToArray1()); + }; + + const BRepGraph_WireId aW0 = + makeWire({aE0, aE1, aE2, aE3}, + {TopAbs_FORWARD, TopAbs_FORWARD, TopAbs_FORWARD, TopAbs_FORWARD}); + const BRepGraph_WireId aW1 = + makeWire({aE4, aE5, aE6, aE7}, + {TopAbs_FORWARD, TopAbs_FORWARD, TopAbs_FORWARD, TopAbs_FORWARD}); + const BRepGraph_WireId aW2 = + makeWire({aE0, aE9, aE4, aE8}, + {TopAbs_FORWARD, TopAbs_FORWARD, TopAbs_REVERSED, TopAbs_REVERSED}); + const BRepGraph_WireId aW3 = + makeWire({aE1, aE10, aE5, aE9}, + {TopAbs_FORWARD, TopAbs_FORWARD, TopAbs_REVERSED, TopAbs_REVERSED}); + const BRepGraph_WireId aW4 = + makeWire({aE2, aE11, aE6, aE10}, + {TopAbs_FORWARD, TopAbs_FORWARD, TopAbs_REVERSED, TopAbs_REVERSED}); + const BRepGraph_WireId aW5 = + makeWire({aE3, aE8, aE7, aE11}, + {TopAbs_FORWARD, TopAbs_FORWARD, TopAbs_REVERSED, TopAbs_REVERSED}); + + auto& aFaceOps = aGraph.Editor().Faces(); + NCollection_LinearVector aNoInner; + const BRepGraph_FaceId aF0 = + aFaceOps.Add(new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, -1)))), + aW0, + aNoInner.ToArray1(), + 1e-7); + const BRepGraph_FaceId aF1 = + aFaceOps.Add(new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(0, 0, 30), gp_Dir(0, 0, 1)))), + aW1, + aNoInner.ToArray1(), + 1e-7); + const BRepGraph_FaceId aF2 = + aFaceOps.Add(new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, -1, 0)))), + aW2, + aNoInner.ToArray1(), + 1e-7); + const BRepGraph_FaceId aF3 = + aFaceOps.Add(new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(10, 0, 0), gp_Dir(1, 0, 0)))), + aW3, + aNoInner.ToArray1(), + 1e-7); + const BRepGraph_FaceId aF4 = + aFaceOps.Add(new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(10, 20, 0), gp_Dir(0, 1, 0)))), + aW4, + aNoInner.ToArray1(), + 1e-7); + const BRepGraph_FaceId aF5 = + aFaceOps.Add(new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(0, 20, 0), gp_Dir(-1, 0, 0)))), + aW5, + aNoInner.ToArray1(), + 1e-7); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + for (const BRepGraph_FaceId& aF : {aF0, aF1, aF2, aF3, aF4, aF5}) + { + aGraph.Editor().Shells().Append(aShell, aF); } - EXPECT_TRUE(aFound) << "Expected 'Orphan WireRef' issue from audit validate"; + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid, aShell); + + const BRepGraph_Validate::Result aResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_TRUE(aResult.IsValid()) << "Synthetic box must pass Audit"; + EXPECT_EQ(aResult.NbIssues(BRepGraph_Validate::Severity::Error), 0); +} + +TEST(BRepGraph_ValidateTest, Synthetic_Cylinder_AuditClean) +{ + BRepGraph aGraph; + aGraph.Clear(); + + auto& aVtxOps = aGraph.Editor().Vertices(); + const BRepGraph_VertexId aVBot = aVtxOps.Add(gp_Pnt(5, 0, 0), 1e-7); + const BRepGraph_VertexId aVTop = aVtxOps.Add(gp_Pnt(5, 0, 15), 1e-7); + + auto& anEdgeOps = aGraph.Editor().Edges(); + occ::handle aBotCircle = + new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0); + const BRepGraph_EdgeId aEBot = anEdgeOps.Add(aVBot, aVBot, aBotCircle, 0.0, 2 * M_PI, 1e-7); + occ::handle aTopCircle = + new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 15), gp_Dir(0, 0, 1)), 5.0); + const BRepGraph_EdgeId aETop = anEdgeOps.Add(aVTop, aVTop, aTopCircle, 0.0, 2 * M_PI, 1e-7); + occ::handle aSeamLine = new Geom_Line(gp_Pnt(5, 0, 0), gp_Dir(0, 0, 1)); + const BRepGraph_EdgeId aESeam = anEdgeOps.Add(aVBot, aVTop, aSeamLine, 0.0, 15.0, 1e-7); + + auto& aCoEdgeOps = aGraph.Editor().CoEdges(); + + NCollection_LinearVector aBotWireCEs; + aBotWireCEs.Append(aCoEdgeOps.Add(aEBot, TopAbs_FORWARD)); + aBotWireCEs.Append(aCoEdgeOps.Add(aESeam, TopAbs_REVERSED)); + const BRepGraph_WireId aWBot = aGraph.Editor().Wires().Add(aBotWireCEs.ToArray1()); + + NCollection_LinearVector aTopWireCEs; + aTopWireCEs.Append(aCoEdgeOps.Add(aETop, TopAbs_FORWARD)); + aTopWireCEs.Append(aCoEdgeOps.Add(aESeam, TopAbs_FORWARD)); + const BRepGraph_WireId aWTop = aGraph.Editor().Wires().Add(aTopWireCEs.ToArray1()); + + NCollection_LinearVector aLatWireCEs; + aLatWireCEs.Append(aCoEdgeOps.Add(aESeam, TopAbs_FORWARD)); + aLatWireCEs.Append(aCoEdgeOps.Add(aESeam, TopAbs_REVERSED)); + const BRepGraph_WireId aWLat = aGraph.Editor().Wires().Add(aLatWireCEs.ToArray1()); + + auto& aFaceOps = aGraph.Editor().Faces(); + NCollection_LinearVector aNoInner; + occ::handle aBotPlane = + new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, -1)))); + occ::handle aTopPlane = + new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(0, 0, 15), gp_Dir(0, 0, 1)))); + occ::handle aLatSurf = + new Geom_CylindricalSurface(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0); + + const BRepGraph_FaceId aFBot = aFaceOps.Add(aBotPlane, aWBot, aNoInner.ToArray1(), 1e-7); + const BRepGraph_FaceId aFTop = aFaceOps.Add(aTopPlane, aWTop, aNoInner.ToArray1(), 1e-7); + const BRepGraph_FaceId aFLat = aFaceOps.Add(aLatSurf, aWLat, aNoInner.ToArray1(), 1e-7); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + aGraph.Editor().Shells().Append(aShell, aFBot); + aGraph.Editor().Shells().Append(aShell, aFTop); + aGraph.Editor().Shells().Append(aShell, aFLat); + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid, aShell); + + const BRepGraph_Validate::Result aResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_TRUE(aResult.IsValid()) << "Synthetic cylinder must pass Audit"; +} + +TEST(BRepGraph_ValidateTest, Synthetic_Sphere_AuditClean) +{ + BRepGraph aGraph; + aGraph.Clear(); + + auto& aVtxOps = aGraph.Editor().Vertices(); + const BRepGraph_VertexId aVSeam = aVtxOps.Add(gp_Pnt(8, 0, 0), 1e-7); + + auto& anEdgeOps = aGraph.Editor().Edges(); + occ::handle aSeamCircle = + new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), 8.0); + const BRepGraph_EdgeId aESeam = anEdgeOps.Add(aVSeam, aVSeam, aSeamCircle, 0.0, 2 * M_PI, 1e-7); + + auto& aCoEdgeOps = aGraph.Editor().CoEdges(); + NCollection_LinearVector aWireCEs; + aWireCEs.Append(aCoEdgeOps.Add(aESeam, TopAbs_FORWARD)); + aWireCEs.Append(aCoEdgeOps.Add(aESeam, TopAbs_REVERSED)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aWireCEs.ToArray1()); + + auto& aFaceOps = aGraph.Editor().Faces(); + NCollection_LinearVector aNoInner; + occ::handle aSphere = new Geom_SphericalSurface(gp_Ax3(), 8.0); + const BRepGraph_FaceId aFace = aFaceOps.Add(aSphere, aWire, aNoInner.ToArray1(), 1e-7); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + aGraph.Editor().Shells().Append(aShell, aFace); + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid, aShell); + + const BRepGraph_Validate::Result aResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_TRUE(aResult.IsValid()) << "Synthetic sphere must pass Audit"; +} + +TEST(BRepGraph_ValidateTest, Synthetic_Compound_AuditClean) +{ + BRepGraph aGraph; + aGraph.Clear(); + + auto& aVtxOps = aGraph.Editor().Vertices(); + const BRepGraph_VertexId aV0 = aVtxOps.Add(gp_Pnt(0, 0, 0), 1e-7); + const BRepGraph_VertexId aV1 = aVtxOps.Add(gp_Pnt(10, 0, 0), 1e-7); + const BRepGraph_VertexId aV2 = aVtxOps.Add(gp_Pnt(5, 10, 0), 1e-7); + + auto& anEdgeOps = aGraph.Editor().Edges(); + const BRepGraph_EdgeId aE0 = + anEdgeOps.Add(aV0, aV1, new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE1 = + anEdgeOps.Add(aV1, + aV2, + new Geom_Line(gp_Pnt(10, 0, 0), gp_Vec(-5.0, 10.0, 0.0).Normalized()), + 0.0, + std::sqrt(125.0), + 1e-7); + const BRepGraph_EdgeId aE2 = + anEdgeOps.Add(aV2, + aV0, + new Geom_Line(gp_Pnt(5, 10, 0), gp_Vec(-5.0, -10.0, 0.0).Normalized()), + 0.0, + std::sqrt(125.0), + 1e-7); + + auto& aCoEdgeOps = aGraph.Editor().CoEdges(); + NCollection_LinearVector aWireCEs; + aWireCEs.Append(aCoEdgeOps.Add(aE0, TopAbs_FORWARD)); + aWireCEs.Append(aCoEdgeOps.Add(aE1, TopAbs_FORWARD)); + aWireCEs.Append(aCoEdgeOps.Add(aE2, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aWireCEs.ToArray1()); + + auto& aFaceOps = aGraph.Editor().Faces(); + NCollection_LinearVector aNoInner; + occ::handle aPlane = new Geom_Plane(gp_Pln(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)))); + const BRepGraph_FaceId aFace = aFaceOps.Add(aPlane, aWire, aNoInner.ToArray1(), 1e-7); + + const BRepGraph_ShellId aShell1 = aGraph.Editor().Shells().Add(); + aGraph.Editor().Shells().Append(aShell1, aFace); + const BRepGraph_SolidId aSolid1 = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid1, aShell1); + + const BRepGraph_VertexId aV3 = aVtxOps.Add(gp_Pnt(20, 0, 0), 1e-7); + const BRepGraph_VertexId aV4 = aVtxOps.Add(gp_Pnt(30, 0, 0), 1e-7); + const BRepGraph_VertexId aV5 = aVtxOps.Add(gp_Pnt(25, 10, 0), 1e-7); + + const BRepGraph_EdgeId aE3 = + anEdgeOps.Add(aV3, aV4, new Geom_Line(gp_Pnt(20, 0, 0), gp_Dir(1, 0, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE4 = + anEdgeOps.Add(aV4, + aV5, + new Geom_Line(gp_Pnt(30, 0, 0), gp_Vec(-5.0, 10.0, 0.0).Normalized()), + 0.0, + std::sqrt(125.0), + 1e-7); + const BRepGraph_EdgeId aE5 = + anEdgeOps.Add(aV5, + aV3, + new Geom_Line(gp_Pnt(25, 10, 0), gp_Vec(-5.0, -10.0, 0.0).Normalized()), + 0.0, + std::sqrt(125.0), + 1e-7); + + NCollection_LinearVector aWire2CEs; + aWire2CEs.Append(aCoEdgeOps.Add(aE3, TopAbs_FORWARD)); + aWire2CEs.Append(aCoEdgeOps.Add(aE4, TopAbs_FORWARD)); + aWire2CEs.Append(aCoEdgeOps.Add(aE5, TopAbs_FORWARD)); + const BRepGraph_WireId aWire2 = aGraph.Editor().Wires().Add(aWire2CEs.ToArray1()); + + const BRepGraph_FaceId aFace2 = aFaceOps.Add(aPlane, aWire2, aNoInner.ToArray1(), 1e-7); + + const BRepGraph_ShellId aShell2 = aGraph.Editor().Shells().Add(); + aGraph.Editor().Shells().Append(aShell2, aFace2); + const BRepGraph_SolidId aSolid2 = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid2, aShell2); + + NCollection_LinearVector aChildren; + aChildren.Append(aSolid1); + aChildren.Append(aSolid2); + [[maybe_unused]] const BRepGraph_CompoundId aCompId = + aGraph.Editor().Compounds().Add(aChildren.ToArray1()); + + const BRepGraph_Validate::Result aResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_TRUE(aResult.IsValid()) << "Synthetic compound must pass Audit"; +} + +TEST(BRepGraph_ValidateTest, Synthetic_Assembly_AuditClean) +{ + BRepGraph aGraph; + aGraph.Clear(); + + auto& aVtxOps = aGraph.Editor().Vertices(); + const BRepGraph_VertexId aV0 = aVtxOps.Add(gp_Pnt(0, 0, 0), 1e-7); + const BRepGraph_VertexId aV1 = aVtxOps.Add(gp_Pnt(10, 0, 0), 1e-7); + const BRepGraph_VertexId aV2 = aVtxOps.Add(gp_Pnt(10, 10, 0), 1e-7); + const BRepGraph_VertexId aV3 = aVtxOps.Add(gp_Pnt(0, 10, 0), 1e-7); + + auto& anEdgeOps = aGraph.Editor().Edges(); + const BRepGraph_EdgeId aE0 = + anEdgeOps.Add(aV0, aV1, new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE1 = + anEdgeOps.Add(aV1, aV2, new Geom_Line(gp_Pnt(10, 0, 0), gp_Dir(0, 1, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE2 = + anEdgeOps.Add(aV2, aV3, new Geom_Line(gp_Pnt(10, 10, 0), gp_Dir(-1, 0, 0)), 0.0, 10.0, 1e-7); + const BRepGraph_EdgeId aE3 = + anEdgeOps.Add(aV3, aV0, new Geom_Line(gp_Pnt(0, 10, 0), gp_Dir(0, -1, 0)), 0.0, 10.0, 1e-7); + + auto& aCoEdgeOps = aGraph.Editor().CoEdges(); + NCollection_LinearVector aWireCEs; + aWireCEs.Append(aCoEdgeOps.Add(aE0, TopAbs_FORWARD)); + aWireCEs.Append(aCoEdgeOps.Add(aE1, TopAbs_FORWARD)); + aWireCEs.Append(aCoEdgeOps.Add(aE2, TopAbs_FORWARD)); + aWireCEs.Append(aCoEdgeOps.Add(aE3, TopAbs_FORWARD)); + const BRepGraph_WireId aWire = aGraph.Editor().Wires().Add(aWireCEs.ToArray1()); + + auto& aFaceOps = aGraph.Editor().Faces(); + NCollection_LinearVector aNoInner; + occ::handle aPlane = new Geom_Plane(gp_Pln()); + const BRepGraph_FaceId aFace = aFaceOps.Add(aPlane, aWire, aNoInner.ToArray1(), 1e-7); + + const BRepGraph_ShellId aShell = aGraph.Editor().Shells().Add(); + aGraph.Editor().Shells().Append(aShell, aFace); + const BRepGraph_SolidId aSolid = aGraph.Editor().Solids().Add(); + aGraph.Editor().Solids().Append(aSolid, aShell); + + const BRepGraph_ProductId aPartId = BRepGraph_ProductId::Start(); + + const BRepGraph_ProductId aAssemblyId = aGraph.Editor().Products().Add(); + aGraph.Editor().Products().AppendDocumentRoot(aAssemblyId); + + gp_Trsf aTrsf1; + aTrsf1.SetTranslation(gp_Vec(0.0, 0.0, 0.0)); + [[maybe_unused]] const BRepGraph_OccurrenceId anOcc1 = + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf1)); + + gp_Trsf aTrsf2; + aTrsf2.SetTranslation(gp_Vec(20.0, 0.0, 0.0)); + [[maybe_unused]] const BRepGraph_OccurrenceId anOcc2 = + aGraph.Editor().Products().Append(aAssemblyId, aPartId, TopLoc_Location(aTrsf2)); + + aGraph.Editor().CommitMutation(); + + const BRepGraph_Validate::Result aResult = + BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()); + EXPECT_TRUE(aResult.IsValid()) << "Synthetic assembly must pass Audit"; } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_VersionStamp_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_VersionStamp_Test.cxx index 87a3803188..3725f95bf3 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_VersionStamp_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_VersionStamp_Test.cxx @@ -13,11 +13,15 @@ #include #include +#include +#include #include #include +#include #include -#include +#include #include +#include #include #include @@ -30,9 +34,8 @@ protected: BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBox); - ASSERT_TRUE(myGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = myGraph.Shapes().Add(aBox); + ASSERT_FALSE(myGraph.IsEmpty()); } BRepGraph myGraph; @@ -53,7 +56,7 @@ TEST_F(BRepGraph_VersionStampTest, StampOf_ValidNode_ReturnsValidStamp) const BRepGraph_VersionStamp aStamp = myGraph.UIDs().StampOf(aFaceId); EXPECT_TRUE(aStamp.IsValid()); - EXPECT_TRUE(aStamp.myUID.IsValid()); + EXPECT_TRUE(aStamp.myNodeUID.IsValid()); EXPECT_EQ(aStamp.myMutationGen, 0u); EXPECT_EQ(aStamp.myGeneration, myGraph.UIDs().Generation()); } @@ -77,7 +80,9 @@ TEST_F(BRepGraph_VersionStampTest, IsStale_MutatedNode_ReturnsTrue) const BRepGraph_VersionStamp aStamp = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); // Mutate the face. - myGraph.Editor().Faces().SetNaturalRestriction(BRepGraph_FaceId::Start(), true); + myGraph.Editor().Faces().SetTolerance( + BRepGraph_FaceId::Start(), + BRepGraph_Tool::Face::Tolerance(myGraph, BRepGraph_FaceId::Start()) + 0.01); EXPECT_TRUE(myGraph.UIDs().IsStale(aStamp)); } @@ -98,9 +103,9 @@ TEST_F(BRepGraph_VersionStampTest, IsStale_DifferentGeneration_ReturnsTrue) // Rebuild the graph - generation changes. BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes2 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); - ASSERT_TRUE(myGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes2 = + myGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(myGraph.IsEmpty()); EXPECT_TRUE(myGraph.UIDs().IsStale(aStamp)); } @@ -139,25 +144,27 @@ TEST_F(BRepGraph_VersionStampTest, StampIdentity_DifferentNodes_NotEqual) EXPECT_NE(aStamp1, aStamp2); } -TEST_F(BRepGraph_VersionStampTest, IsSameNode_SameVersion_ReturnsTrue) +TEST_F(BRepGraph_VersionStampTest, IsSameItem_SameVersion_ReturnsTrue) { const BRepGraph_VersionStamp aStamp1 = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); const BRepGraph_VersionStamp aStamp2 = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); - EXPECT_TRUE(aStamp1.IsSameNode(aStamp2)); + EXPECT_TRUE(aStamp1.IsSameItem(aStamp2)); } -TEST_F(BRepGraph_VersionStampTest, IsSameNode_DifferentVersion_StillSameNode) +TEST_F(BRepGraph_VersionStampTest, IsSameItem_DifferentVersion_StillSameItem) { const BRepGraph_VersionStamp aStampBefore = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); - myGraph.Editor().Faces().SetNaturalRestriction(BRepGraph_FaceId::Start(), true); + myGraph.Editor().Faces().SetTolerance( + BRepGraph_FaceId::Start(), + BRepGraph_Tool::Face::Tolerance(myGraph, BRepGraph_FaceId::Start()) + 0.01); const BRepGraph_VersionStamp aStampAfter = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); // Full equality fails (different MutationGen). EXPECT_NE(aStampBefore, aStampAfter); - // But they refer to the same node (same UID). - EXPECT_TRUE(aStampBefore.IsSameNode(aStampAfter)); + // But they refer to the same item identity. + EXPECT_TRUE(aStampBefore.IsSameItem(aStampAfter)); } TEST_F(BRepGraph_VersionStampTest, StampOf_AssemblyNodes_WorksForProductsAndOccurrences) @@ -170,6 +177,55 @@ TEST_F(BRepGraph_VersionStampTest, StampOf_AssemblyNodes_WorksForProductsAndOccu EXPECT_FALSE(myGraph.UIDs().IsStale(aProdStamp)); } +TEST_F(BRepGraph_VersionStampTest, GenericItemUID_NodeAndReferenceItems_RoundTrip) +{ + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_ItemId aFaceItem(aFaceId); + const BRepGraph_ItemUID aFaceUID = myGraph.UIDs().Of(aFaceItem); + ASSERT_TRUE(aFaceUID.IsValid()); + EXPECT_TRUE(aFaceUID.IsNode()); + EXPECT_EQ(aFaceUID.NodeKind(), BRepGraph_NodeId::Kind::Face); + EXPECT_EQ(myGraph.UIDs().ItemIdFrom(aFaceUID), aFaceItem); + EXPECT_TRUE(myGraph.UIDs().Has(aFaceUID)); + + const BRepGraph_VersionStamp aFaceStamp = myGraph.UIDs().StampOf(aFaceItem); + EXPECT_TRUE(aFaceStamp.IsValid()); + EXPECT_TRUE(aFaceStamp.IsNodeStamp()); + EXPECT_EQ(aFaceStamp.ItemUID(), aFaceUID); + + const BRepGraph_FaceRefId aFaceRefId = BRepGraph_FaceRefId::Start(); + const BRepGraph_ItemId aFaceRefItem(aFaceRefId); + const BRepGraph_ItemUID aFaceRefUID = myGraph.UIDs().Of(aFaceRefItem); + ASSERT_TRUE(aFaceRefUID.IsValid()); + EXPECT_TRUE(aFaceRefUID.IsReference()); + EXPECT_EQ(aFaceRefUID.RefKind(), BRepGraph_RefId::Kind::Face); + EXPECT_EQ(myGraph.UIDs().ItemIdFrom(aFaceRefUID), aFaceRefItem); + EXPECT_TRUE(myGraph.UIDs().Has(aFaceRefUID)); + + const BRepGraph_VersionStamp aFaceRefStamp = myGraph.UIDs().StampOf(aFaceRefItem); + EXPECT_TRUE(aFaceRefStamp.IsValid()); + EXPECT_TRUE(aFaceRefStamp.IsRefStamp()); + EXPECT_EQ(aFaceRefStamp.ItemUID(), aFaceRefUID); +} + +TEST_F(BRepGraph_VersionStampTest, StampOf_RemovedUse_ReturnsInvalidUntilReused) +{ + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_FaceSurfaceRepId aRepId = myGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId; + ASSERT_TRUE(aRepId.IsValid()); + + const occ::handle aSurface = BRepGraph_Tool::Face::Surface(myGraph, aFaceId); + ASSERT_FALSE(aSurface.IsNull()); + EXPECT_TRUE(myGraph.UIDs().StampOf(aRepId).IsValid()); + + myGraph.Editor().Faces().ClearSurface(aFaceId); + EXPECT_FALSE(myGraph.UIDs().StampOf(aRepId).IsValid()); + + myGraph.Editor().Faces().SetSurface(aFaceId, aSurface); + EXPECT_EQ(myGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId, aRepId); + EXPECT_TRUE(myGraph.UIDs().StampOf(aRepId).IsValid()); +} + // --- Graph GUID tests --- TEST_F(BRepGraph_VersionStampTest, GraphGUID_AfterBuild_IsValid) @@ -186,9 +242,9 @@ TEST_F(BRepGraph_VersionStampTest, GraphGUID_Rebuild_Changes) BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes3 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); - ASSERT_TRUE(myGraph.IsDone()); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes3 = + myGraph.Shapes().Add(aBoxMaker.Shape()); + ASSERT_FALSE(myGraph.IsEmpty()); const Standard_GUID aGUID2 = myGraph.UIDs().GraphGUID(); // Two random GUIDs should differ (probability of collision is negligible). @@ -212,7 +268,9 @@ TEST_F(BRepGraph_VersionStampTest, ToGUID_DifferentMutationGen_DifferentGUID) const Standard_GUID& aGraph = myGraph.UIDs().GraphGUID(); const Standard_GUID aGUIDBefore = aStampBefore.ToGUID(aGraph); - myGraph.Editor().Faces().SetNaturalRestriction(BRepGraph_FaceId::Start(), true); + myGraph.Editor().Faces().SetTolerance( + BRepGraph_FaceId::Start(), + BRepGraph_Tool::Face::Tolerance(myGraph, BRepGraph_FaceId::Start()) + 0.01); const BRepGraph_VersionStamp aStampAfter = myGraph.UIDs().StampOf(BRepGraph_FaceId::Start()); const Standard_GUID aGUIDAfter = aStampAfter.ToGUID(aGraph); diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx index 2ee29dbeda..7f9d84f298 100644 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx +++ b/src/ModelingData/TKBRep/GTests/BRepGraph_Views_Test.cxx @@ -13,52 +13,119 @@ #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 +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include +#include +#include + +#include #include namespace { -//! Concrete subclass for testing cache values. -class TestCacheValue : public BRepGraph_CacheValue +class TestCacheService : public BRepGraph_Cache { public: - DEFINE_STANDARD_RTTI_INLINE(TestCacheValue, BRepGraph_CacheValue) - TestCacheValue() = default; + DEFINE_STANDARD_RTTI_INLINE(TestCacheService, BRepGraph_Cache) + + static const Standard_GUID& GetID() + { + static const Standard_GUID THE_ID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10020"); + return THE_ID; + } + + const Standard_GUID& ID() const override { return GetID(); } + + const TCollection_AsciiString& Name() const override + { + static const TCollection_AsciiString THE_NAME("ViewsTestCacheService"); + return THE_NAME; + } + + bool Attached() const { return IsAttached(); } + + void Set(const BRepGraph_NodeId theNode, const int theValue) + { + if (myValues.IsBound(theNode)) + { + myValues.ChangeFind(theNode) = theValue; + } + else + { + myValues.Bind(theNode, theValue); + } + } + + bool Get(const BRepGraph_NodeId theNode, int& theValue) const + { + const int* aValue = myValues.Seek(theNode); + if (aValue == nullptr) + { + return false; + } + theValue = *aValue; + return true; + } + + void Clear() noexcept override + { + ++myClearCount; + myValues.Clear(); + } + + int ClearCount() const { return myClearCount; } + +private: + NCollection_DataMap myValues; + int myClearCount = 0; }; -const occ::handle& testUserAttrKind() +template +static uint32_t countActiveRefs(const BRepGraph& theGraph, const theRefContainerType& theRefIds) { - static const occ::handle THE_KIND = - new BRepGraph_CacheKind(Standard_GUID("2f9b6a5c-1f2d-4a88-9c1c-7a0c16a10020"), "ViewsTestAttr"); - return THE_KIND; -} - -template -static int countActiveRefs(const NCollection_DynamicArray& theRefIds, - const theRefFn& theRefAccess) -{ - int aCount = 0; - for (const theRefIdType& aRefId : theRefIds) + uint32_t aCount = 0; + for (const auto& aRefId : theRefIds) { - if (!theRefAccess(aRefId).IsRemoved) + if (!aRefId.IsRemoved(theGraph)) { ++aCount; } @@ -66,12 +133,27 @@ static int countActiveRefs(const NCollection_DynamicArray& theRefI return aCount; } -static int countActiveNodes(const BRepGraph& theGraph, - const BRepGraph_NodeId::Kind theKind, - const int theUpperBound) +template +static uint32_t countActiveNodeIds(const BRepGraph& theGraph, + const theNodeIdContainerType& theNodeIds) { - int aCount = 0; - for (int anIdx = 0; anIdx < theUpperBound; ++anIdx) + uint32_t aCount = 0; + for (const auto& aNodeId : theNodeIds) + { + if (!theGraph.Topo().Gen().IsRemoved(BRepGraph_NodeId(aNodeId))) + { + ++aCount; + } + } + return aCount; +} + +static uint32_t countActiveNodes(const BRepGraph& theGraph, + const BRepGraph_NodeId::Kind theKind, + const uint32_t theUpperBound) +{ + uint32_t aCount = 0; + for (uint32_t anIdx = 0; anIdx < theUpperBound; ++anIdx) { if (!theGraph.Topo().Gen().IsRemoved(BRepGraph_NodeId(theKind, anIdx))) { @@ -80,6 +162,90 @@ static int countActiveNodes(const BRepGraph& theGraph, } return aCount; } + +template +static uint32_t countIterator(theIteratorType theIterator) +{ + uint32_t aCount = 0; + for (; theIterator.More(); theIterator.Next()) + { + ++aCount; + } + return aCount; +} + +static uint32_t countRelated(const BRepGraph& theGraph, + const BRepGraph_NodeId theNode, + const BRepGraph_RelatedIterator::RelationKind theKind) +{ + uint32_t aCount = 0; + for (BRepGraph_RelatedIterator anIt(theGraph, theNode); anIt.More(); anIt.Next()) + { + if (anIt.CurrentRelation() == theKind) + { + ++aCount; + } + } + return aCount; +} + +static BRepGraph_FaceId firstFaceOfEdge(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) +{ + BRepGraph_FacesOfEdge aFaceIt = theGraph.Topo().Edges().FacesOf(theEdge); + return aFaceIt.More() ? aFaceIt.CurrentId() : BRepGraph_FaceId(); +} + +static bool edgeHasFace(const BRepGraph& theGraph, + const BRepGraph_EdgeId theEdge, + const BRepGraph_FaceId theFace) +{ + for (BRepGraph_FacesOfEdge aFaceIt = theGraph.Topo().Edges().FacesOf(theEdge); aFaceIt.More(); + aFaceIt.Next()) + { + if (aFaceIt.CurrentId() == theFace) + { + return true; + } + } + return false; +} + +static bool containsEdge(const NCollection_LinearVector& theEdges, + const BRepGraph_EdgeId theEdge) +{ + for (const BRepGraph_EdgeId& anEdge : theEdges) + { + if (anEdge == theEdge) + { + return true; + } + } + return false; +} + +static uint32_t countAdjacentEdgesOfEdge(const BRepGraph& theGraph, const BRepGraph_EdgeId theEdge) +{ + if (!theEdge.IsValid(theGraph.Topo().Edges().Nb()) || theEdge.IsRemoved(theGraph)) + { + return 0; + } + + NCollection_LinearVector anAdjacentEdges; + for (BRepGraph_DefsVertexOfEdge aVertexIt(theGraph, theEdge); aVertexIt.More(); aVertexIt.Next()) + { + const BRepGraph_VertexId aVertexId = aVertexIt.CurrentId(); + for (const BRepGraph_EdgeId& anAdjacentEdgeId : theGraph.Topo().Vertices().Edges(aVertexId)) + { + if (anAdjacentEdgeId == theEdge || anAdjacentEdgeId.IsRemoved(theGraph) + || containsEdge(anAdjacentEdges, anAdjacentEdgeId)) + { + continue; + } + anAdjacentEdges.Append(anAdjacentEdgeId); + } + } + return static_cast(anAdjacentEdges.Size()); +} } // namespace class BRepGraph_ViewsTest : public testing::Test @@ -90,8 +256,7 @@ protected: BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); const TopoDS_Shape& aBox = aBoxMaker.Shape(); myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBox); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes1 = myGraph.Shapes().Add(aBox); } BRepGraph myGraph; @@ -164,7 +329,7 @@ TEST_F(BRepGraph_ViewsTest, DefsView_ActiveCounts_MatchStorageState) TEST_F(BRepGraph_ViewsTest, DefsView_NbActiveFaces_ExcludeRemoved) { - const int aFacesBefore = myGraph.Topo().Faces().NbActive(); + const uint32_t aFacesBefore = myGraph.Topo().Faces().NbActive(); myGraph.Editor().Gen().RemoveNode(BRepGraph_FaceId::Start()); EXPECT_EQ(myGraph.Topo().Faces().NbActive(), aFacesBefore - 1); @@ -187,6 +352,26 @@ TEST_F(BRepGraph_ViewsTest, DefsView_TopoEntity_Valid) EXPECT_NE(aBase, nullptr); } +TEST_F(BRepGraph_ViewsTest, TopoView_GenValidity) +{ + const BRepGraph_NodeId aFaceId(BRepGraph_FaceId::Start()); + EXPECT_EQ(myGraph.Topo().Gen().Nb(BRepGraph_NodeId::Kind::Face), myGraph.Topo().Faces().Nb()); + EXPECT_TRUE(myGraph.Topo().Gen().IsValid(aFaceId)); + EXPECT_TRUE(myGraph.Topo().Gen().IsActive(aFaceId)); + EXPECT_FALSE(myGraph.Topo().Gen().IsRemoved(aFaceId)); + + myGraph.Editor().Gen().RemoveNode(aFaceId); + EXPECT_TRUE(myGraph.Topo().Gen().IsValid(aFaceId)); + EXPECT_FALSE(myGraph.Topo().Gen().IsActive(aFaceId)); + EXPECT_TRUE(myGraph.Topo().Gen().IsRemoved(aFaceId)); + + const BRepGraph_NodeId anOutOfRangeFace(BRepGraph_NodeId::Kind::Face, + myGraph.Topo().Faces().Nb()); + EXPECT_FALSE(myGraph.Topo().Gen().IsValid(anOutOfRangeFace)); + EXPECT_FALSE(myGraph.Topo().Gen().IsActive(anOutOfRangeFace)); + EXPECT_TRUE(myGraph.Topo().Gen().IsRemoved(anOutOfRangeFace)); +} + TEST_F(BRepGraph_ViewsTest, DefsView_NbNodes_Positive) { EXPECT_GT(myGraph.Topo().Gen().NbNodes(), 0u); @@ -212,13 +397,13 @@ TEST_F(BRepGraph_ViewsTest, DefsView_EdgeCurve3d_NonNull) } } -TEST_F(BRepGraph_ViewsTest, DefsView_FindPCurve_NoCrash) +TEST_F(BRepGraph_ViewsTest, DefsView_FindPCurveCoEdgeId_NoCrash) { - // FindPCurve may or may not return a non-null pointer for an arbitrary edge/face pair. + // FindPCurveCoEdgeId may or may not return a valid id for an arbitrary edge/face pair. // Just verify it does not crash. - (void)BRepGraph_Tool::Edge::FindPCurve(myGraph, - BRepGraph_EdgeId::Start(), - BRepGraph_FaceId::Start()); + std::ignore = BRepGraph_Tool::Edge::FindPCurveCoEdgeId(myGraph, + BRepGraph_EdgeId::Start(), + BRepGraph_FaceId::Start()); } TEST_F(BRepGraph_ViewsTest, DefsView_RepIdConvenienceAccessors_RoundTrip) @@ -226,19 +411,14 @@ TEST_F(BRepGraph_ViewsTest, DefsView_RepIdConvenienceAccessors_RoundTrip) const BRepGraph_FaceId aFaceId(0); const BRepGraph_EdgeId anEdgeId(0); - EXPECT_EQ(myGraph.Topo().Faces().SurfaceRepId(aFaceId), - myGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId); - EXPECT_EQ(myGraph.Topo().Faces().ActiveTriangulationRepId(aFaceId), - myGraph.Topo().Faces().Definition(aFaceId).TriangulationRepId); - EXPECT_EQ(myGraph.Topo().Edges().Curve3DRepId(anEdgeId), - myGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId); + EXPECT_FALSE(myGraph.Topo().Faces().Surface(aFaceId).IsNull()); + EXPECT_FALSE(myGraph.Topo().Edges().Curve3D(anEdgeId).IsNull()); - const NCollection_DynamicArray& aCoEdges = + const NCollection_LinearVector& aCoEdges = myGraph.Topo().Edges().CoEdges(anEdgeId); - ASSERT_GT(aCoEdges.Length(), 0); + ASSERT_GT(aCoEdges.Size(), 0); const BRepGraph_CoEdgeId aCoEdgeId = aCoEdges.Value(0); - EXPECT_EQ(myGraph.Topo().CoEdges().Curve2DRepId(aCoEdgeId), - myGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId); + EXPECT_FALSE(myGraph.Topo().CoEdges().Curve2D(aCoEdgeId).IsNull()); } TEST_F(BRepGraph_ViewsTest, DefsView_RepIdConvenienceAccessors_InvalidInput) @@ -247,10 +427,10 @@ TEST_F(BRepGraph_ViewsTest, DefsView_RepIdConvenienceAccessors_InvalidInput) const BRepGraph_EdgeId anEdgeOut(myGraph.Topo().Edges().Nb()); const BRepGraph_CoEdgeId aCoEdgeOut(myGraph.Topo().CoEdges().Nb()); - EXPECT_FALSE(myGraph.Topo().Faces().SurfaceRepId(aFaceOut).IsValid()); - EXPECT_FALSE(myGraph.Topo().Faces().ActiveTriangulationRepId(aFaceOut).IsValid()); - EXPECT_FALSE(myGraph.Topo().Edges().Curve3DRepId(anEdgeOut).IsValid()); - EXPECT_FALSE(myGraph.Topo().CoEdges().Curve2DRepId(aCoEdgeOut).IsValid()); + EXPECT_TRUE(myGraph.Topo().Faces().Surface(aFaceOut).IsNull()); + EXPECT_TRUE(myGraph.Topo().Faces().ActiveTriangulation(aFaceOut).IsNull()); + EXPECT_TRUE(myGraph.Topo().Edges().Curve3D(anEdgeOut).IsNull()); + EXPECT_TRUE(myGraph.Topo().CoEdges().Curve2D(aCoEdgeOut).IsNull()); } // ---------- UIDsView ---------- @@ -286,18 +466,18 @@ TEST_F(BRepGraph_ViewsTest, UIDsView_NodeIdFrom_MultipleRoundTrip) aUIDs.Append(aFaceUID); aUIDs.Append(anEdgeUID); - ASSERT_EQ(aUIDs.Length(), 2); + ASSERT_EQ(aUIDs.Size(), 2); EXPECT_EQ(myGraph.UIDs().NodeIdFrom(aUIDs.Value(0)), BRepGraph_NodeId(BRepGraph_FaceId::Start())); EXPECT_EQ(myGraph.UIDs().NodeIdFrom(aUIDs.Value(1)), BRepGraph_NodeId(BRepGraph_EdgeId::Start())); } -TEST_F(BRepGraph_ViewsTest, UIDsView_NodeIdFrom_InvalidAndWrongGeneration) +TEST_F(BRepGraph_ViewsTest, UIDsView_NodeIdFrom_InvalidAndUnknownUID) { NCollection_DynamicArray aUIDs; aUIDs.Append(BRepGraph_UID()); - aUIDs.Append(BRepGraph_UID(BRepGraph_NodeId::Kind::Face, 1, myGraph.UIDs().Generation() + 1)); + aUIDs.Append(BRepGraph_UID(BRepGraph_NodeId::Kind::Face, 9999)); - ASSERT_EQ(aUIDs.Length(), 2); + ASSERT_EQ(aUIDs.Size(), 2); EXPECT_FALSE(myGraph.UIDs().NodeIdFrom(aUIDs.Value(0)).IsValid()); EXPECT_FALSE(myGraph.UIDs().NodeIdFrom(aUIDs.Value(1)).IsValid()); } @@ -329,9 +509,9 @@ TEST_F(BRepGraph_ViewsTest, UIDsView_Of_RemovedNode_ReturnsInvalid) TEST_F(BRepGraph_ViewsTest, UIDsView_RefLookup_RemovedRef_ReturnsInvalidAndHasFalse) { - const NCollection_DynamicArray& aFaceRefs = + const NCollection_LinearVector& aFaceRefs = myGraph.Refs().Faces().IdsOf(BRepGraph_ShellId::Start()); - ASSERT_GT(aFaceRefs.Length(), 0); + ASSERT_GT(aFaceRefs.Size(), 0); const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.Value(0); const BRepGraph_RefUID aUID = myGraph.UIDs().Of(aFaceRefId); @@ -347,9 +527,9 @@ TEST_F(BRepGraph_ViewsTest, UIDsView_RefLookup_RemovedRef_ReturnsInvalidAndHasFa TEST_F(BRepGraph_ViewsTest, UIDsView_Of_RemovedRef_ReturnsInvalid) { - const NCollection_DynamicArray& aFaceRefs = + const NCollection_LinearVector& aFaceRefs = myGraph.Refs().Faces().IdsOf(BRepGraph_ShellId::Start()); - ASSERT_GT(aFaceRefs.Length(), 0); + ASSERT_GT(aFaceRefs.Size(), 0); const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.Value(0); const BRepGraph_RefUID aUID = myGraph.UIDs().Of(aFaceRefId); ASSERT_TRUE(aUID.IsValid()); @@ -369,70 +549,62 @@ TEST_F(BRepGraph_ViewsTest, UIDsView_Of_OutOfRangeRef_ReturnsInvalid) TEST_F(BRepGraph_ViewsTest, SpatialView_AdjacentFaces_FourPerBoxFace) { - BRepGraph_FaceId aFaceId(0); - NCollection_DynamicArray aResult = - myGraph.Topo().Faces().Adjacent(aFaceId, myGraph.Allocator()); - EXPECT_EQ(aResult.Length(), 4); + BRepGraph_FaceId aFaceId(0); + EXPECT_EQ(countRelated(myGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_RelatedIterator::RelationKind::AdjacentFace), + 4); } TEST_F(BRepGraph_ViewsTest, SpatialView_FacesOfEdge_TwoPerBoxEdge) { - BRepGraph_EdgeId anEdgeId(0); - const NCollection_DynamicArray& aResult = - myGraph.Topo().Edges().Faces(anEdgeId); - EXPECT_EQ(aResult.Length(), 2); + BRepGraph_EdgeId anEdgeId(0); + EXPECT_EQ(countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)), 2); } TEST_F(BRepGraph_ViewsTest, SpatialView_OutParam_Parity) { - const BRepGraph_FaceId aFaceId(0); - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraph_VertexId aVertexId(0); - const occ::handle& anAllocator = myGraph.Allocator(); + const BRepGraph_FaceId aFaceId(0); + const BRepGraph_EdgeId anEdgeId(0); + const BRepGraph_VertexId aVertexId(0); - const NCollection_DynamicArray aAdjacentByValue = - myGraph.Topo().Faces().Adjacent(aFaceId, anAllocator); - EXPECT_EQ(aAdjacentByValue.Length(), 4); - - const NCollection_DynamicArray anAdjEdgesByValue = - myGraph.Topo().Edges().Adjacent(anEdgeId, anAllocator); - EXPECT_GE(anAdjEdgesByValue.Length(), 4); + EXPECT_EQ(countRelated(myGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_RelatedIterator::RelationKind::AdjacentFace), + 4); + EXPECT_GE(countAdjacentEdgesOfEdge(myGraph, anEdgeId), 4); } TEST_F(BRepGraph_ViewsTest, TopoView_GroupedFaceOps_Parity) { - const BRepGraph_FaceId aFaceId(0); - const occ::handle& anAllocator = myGraph.Allocator(); + const BRepGraph_FaceId aFaceId(0); EXPECT_EQ(BRepGraph_NodeId(aFaceId), BRepGraph_NodeId(aFaceId)); - EXPECT_EQ(myGraph.Topo().Faces().SurfaceRepId(aFaceId), - myGraph.Topo().Faces().Definition(aFaceId).SurfaceRepId); - EXPECT_EQ(myGraph.Topo().Faces().ActiveTriangulationRepId(aFaceId), - myGraph.Topo().Faces().Definition(aFaceId).TriangulationRepId); - EXPECT_EQ(myGraph.Topo().Faces().OuterWire(aFaceId), BRepGraph_WireId::Start()); + EXPECT_FALSE(myGraph.Topo().Faces().Surface(aFaceId).IsNull()); - EXPECT_EQ(myGraph.Topo().Faces().Adjacent(aFaceId, anAllocator).Length(), 4); + EXPECT_EQ(countRelated(myGraph, + BRepGraph_NodeId(aFaceId), + BRepGraph_RelatedIterator::RelationKind::AdjacentFace), + 4); } TEST_F(BRepGraph_ViewsTest, TopoView_GroupedEdgeAndVertexOps_Parity) { - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraph_VertexId aVertexId(0); - const occ::handle& anAllocator = myGraph.Allocator(); + const BRepGraph_EdgeId anEdgeId(0); + const BRepGraph_VertexId aVertexId(0); EXPECT_EQ(BRepGraph_NodeId(anEdgeId), BRepGraph_NodeId(anEdgeId)); EXPECT_EQ(myGraph.Topo().Edges().NbFaces(anEdgeId), 2); - EXPECT_EQ(myGraph.Topo().Edges().Curve3DRepId(anEdgeId), - myGraph.Topo().Edges().Definition(anEdgeId).Curve3DRepId); - EXPECT_FALSE(myGraph.Topo().Edges().IsBoundary(anEdgeId)); - EXPECT_TRUE(myGraph.Topo().Edges().IsManifold(anEdgeId)); - EXPECT_GE(myGraph.Topo().Edges().Wires(anEdgeId).Length(), 1); - EXPECT_GE(myGraph.Topo().Edges().CoEdges(anEdgeId).Length(), 1); - EXPECT_EQ(myGraph.Topo().Edges().Faces(anEdgeId).Length(), 2); - EXPECT_GE(myGraph.Topo().Edges().Adjacent(anEdgeId, anAllocator).Length(), 4); + EXPECT_FALSE(myGraph.Topo().Edges().Curve3D(anEdgeId).IsNull()); + EXPECT_FALSE(BRepGraph_Tool::Edge::IsBoundary(myGraph, anEdgeId)); + EXPECT_TRUE(BRepGraph_Tool::Edge::IsManifold(myGraph, anEdgeId)); + EXPECT_GE(countIterator(myGraph.Topo().Edges().WiresOf(anEdgeId)), 1); + EXPECT_GE(myGraph.Topo().Edges().CoEdges(anEdgeId).Size(), 1); + EXPECT_EQ(countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)), 2); + EXPECT_GE(countAdjacentEdgesOfEdge(myGraph, anEdgeId), 4); EXPECT_EQ(BRepGraph_NodeId(aVertexId), BRepGraph_NodeId(aVertexId)); - EXPECT_GE(myGraph.Topo().Vertices().Edges(aVertexId).Length(), 1); + EXPECT_GE(myGraph.Topo().Vertices().Edges(aVertexId).Size(), 1); } TEST_F(BRepGraph_ViewsTest, TopoView_GroupedCoEdgeOps_Parity) @@ -441,32 +613,34 @@ TEST_F(BRepGraph_ViewsTest, TopoView_GroupedCoEdgeOps_Parity) const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aCoEdgeId); EXPECT_EQ(BRepGraph_NodeId(aCoEdgeId), BRepGraph_NodeId(aCoEdgeId)); - EXPECT_EQ(myGraph.Topo().CoEdges().Definition(aCoEdgeId).EdgeDefId, aCoEdge.EdgeDefId); - EXPECT_EQ(myGraph.Topo().CoEdges().Definition(aCoEdgeId).FaceDefId, aCoEdge.FaceDefId); - EXPECT_EQ(myGraph.Topo().CoEdges().Curve2DRepId(aCoEdgeId), - myGraph.Topo().CoEdges().Definition(aCoEdgeId).Curve2DRepId); + EXPECT_EQ(myGraph.Topo().CoEdges().Definition(aCoEdgeId).ChildEdgeId, aCoEdge.ChildEdgeId); + EXPECT_EQ(myGraph.Topo().CoEdges().Definition(aCoEdgeId).FaceId, aCoEdge.FaceId); + EXPECT_FALSE(myGraph.Topo().CoEdges().Curve2D(aCoEdgeId).IsNull()); // Verify TopoView::CoEdgeOps::SeamPair and Tool::CoEdge::SeamPair agree. - EXPECT_EQ(myGraph.Topo().CoEdges().SeamPair(aCoEdgeId), + EXPECT_EQ(BRepGraph_Tool::CoEdge::SeamPair(myGraph, aCoEdgeId), BRepGraph_Tool::CoEdge::SeamPair(myGraph, aCoEdgeId)); } TEST_F(BRepGraph_ViewsTest, TopoView_GroupedProductAndOccurrenceOps_Parity) { const BRepGraph_ProductId aPartProduct = - myGraph.Editor().Products().LinkProductToTopology(BRepGraph_NodeId(BRepGraph_SolidId::Start())); - const BRepGraph_ProductId aSubAssembly = myGraph.Editor().Products().CreateEmptyProduct(); - const BRepGraph_ProductId aRootAssembly = myGraph.Editor().Products().CreateEmptyProduct(); + myGraph.Editor().Products().Add(BRepGraph_NodeId(BRepGraph_SolidId::Start())); + myGraph.Editor().Products().AppendDocumentRoot(aPartProduct); + const BRepGraph_ProductId aSubAssembly = myGraph.Editor().Products().Add(); + myGraph.Editor().Products().AppendDocumentRoot(aSubAssembly); + const BRepGraph_ProductId aRootAssembly = myGraph.Editor().Products().Add(); + myGraph.Editor().Products().AppendDocumentRoot(aRootAssembly); ASSERT_TRUE(aPartProduct.IsValid()); ASSERT_TRUE(aSubAssembly.IsValid()); ASSERT_TRUE(aRootAssembly.IsValid()); const BRepGraph_OccurrenceId aSubOccurrence = - myGraph.Editor().Products().LinkProducts(aRootAssembly, aSubAssembly, TopLoc_Location()); + myGraph.Editor().Products().Append(aRootAssembly, aSubAssembly, TopLoc_Location()); const BRepGraph_OccurrenceId aPartOccurrence = - myGraph.Editor().Products().LinkProducts(aSubAssembly, - aPartProduct, - TopLoc_Location(), - aSubOccurrence); + myGraph.Editor().Products().Append(aSubAssembly, + aPartProduct, + TopLoc_Location(), + aSubOccurrence); ASSERT_TRUE(aSubOccurrence.IsValid()); ASSERT_TRUE(aPartOccurrence.IsValid()); @@ -474,181 +648,127 @@ TEST_F(BRepGraph_ViewsTest, TopoView_GroupedProductAndOccurrenceOps_Parity) EXPECT_EQ(myGraph.Topo().Products().ShapeRoot(aPartProduct), BRepGraph_NodeId(BRepGraph_SolidId::Start())); EXPECT_FALSE(myGraph.Topo().Products().ShapeRoot(aRootAssembly).IsValid()); - EXPECT_EQ(myGraph.Refs().Occurrences().IdsOf(aRootAssembly).Length(), 1); - EXPECT_EQ(myGraph.Refs().Occurrences().IdsOf(aSubAssembly).Length(), 1); + EXPECT_EQ(myGraph.Refs().Occurrences().IdsOf(aRootAssembly).Size(), 1); + EXPECT_EQ(myGraph.Refs().Occurrences().IdsOf(aSubAssembly).Size(), 1); EXPECT_EQ(BRepGraph_NodeId(aPartOccurrence), BRepGraph_NodeId(aPartOccurrence)); EXPECT_EQ(myGraph.Topo().Occurrences().Product(aPartOccurrence), aPartProduct); EXPECT_EQ(myGraph.Topo().Occurrences().ParentProduct(aPartOccurrence), aSubAssembly); - const NCollection_DynamicArray& aOccurrenceRefs = + const NCollection_LinearVector& aOccurrenceRefs = myGraph.Refs().Occurrences().IdsOf(aSubAssembly); - ASSERT_EQ(aOccurrenceRefs.Length(), 1); - { - BRepGraph_MutGuard anOccurrenceRef = - myGraph.Editor().Occurrences().MutRef(aOccurrenceRefs.Value(0)); - myGraph.Editor().Gen().RemoveRef(aOccurrenceRefs.Value(0)); - } + ASSERT_EQ(aOccurrenceRefs.Size(), 1); + myGraph.Editor().Gen().RemoveRef(aOccurrenceRefs.Value(0)); EXPECT_EQ(myGraph.Topo().Products().NbComponents(aSubAssembly), 0); } TEST_F(BRepGraph_ViewsTest, SpatialView_OutParam_ClearAndInvalid) { - const occ::handle& anAllocator = myGraph.Allocator(); + EXPECT_EQ(countRelated(myGraph, + BRepGraph_NodeId(BRepGraph_FaceId::Start()), + BRepGraph_RelatedIterator::RelationKind::AdjacentFace), + 4); + EXPECT_EQ(countRelated(myGraph, + BRepGraph_NodeId(BRepGraph_FaceId(999)), + BRepGraph_RelatedIterator::RelationKind::AdjacentFace), + 0); - const NCollection_DynamicArray aFaceResult = - myGraph.Topo().Faces().Adjacent(BRepGraph_FaceId::Start(), anAllocator); - EXPECT_EQ(aFaceResult.Length(), 4); - EXPECT_EQ(myGraph.Topo().Faces().Adjacent(BRepGraph_FaceId(999), anAllocator).Length(), 0); - - const NCollection_DynamicArray anAdjEdgeResult = - myGraph.Topo().Edges().Adjacent(BRepGraph_EdgeId::Start(), anAllocator); - EXPECT_GE(anAdjEdgeResult.Length(), 4); - EXPECT_EQ(myGraph.Topo().Edges().Adjacent(BRepGraph_EdgeId(999), anAllocator).Length(), 0); + EXPECT_GE(countAdjacentEdgesOfEdge(myGraph, BRepGraph_EdgeId::Start()), 4); + EXPECT_EQ(countAdjacentEdgesOfEdge(myGraph, BRepGraph_EdgeId(999)), 0); } -// ---------- CacheView ---------- +// ---------- CacheRegistry ---------- -TEST_F(BRepGraph_ViewsTest, AttrsView_SetGet_RoundTrip) +TEST_F(BRepGraph_ViewsTest, CacheRegistry_EnsureRegistersAttachedService) { - BRepGraph_FaceId aFaceId(0); - occ::handle anAttr = new TestCacheValue(); - myGraph.Cache().Set(aFaceId, testUserAttrKind(), anAttr); - occ::handle aRetrieved = myGraph.Cache().Get(aFaceId, testUserAttrKind()); - EXPECT_EQ(aRetrieved, anAttr); - EXPECT_TRUE(myGraph.Cache().Has(aFaceId, testUserAttrKind())); + occ::handle aCache = myGraph.CacheRegistry().Ensure(); + ASSERT_FALSE(aCache.IsNull()); + EXPECT_TRUE(aCache->Attached()); + EXPECT_EQ(myGraph.CacheRegistry().Find(), aCache); } -TEST_F(BRepGraph_ViewsTest, AttrsView_Remove_Works) +TEST_F(BRepGraph_ViewsTest, CacheRegistry_CacheIter_RangeFor) { - BRepGraph_FaceId aFaceId(0); - occ::handle anAttr = new TestCacheValue(); - myGraph.Cache().Set(aFaceId, testUserAttrKind(), anAttr); - EXPECT_TRUE(myGraph.Cache().Remove(aFaceId, testUserAttrKind())); - EXPECT_FALSE(myGraph.Cache().Has(aFaceId, testUserAttrKind())); - EXPECT_TRUE(myGraph.Cache().Get(aFaceId, testUserAttrKind()).IsNull()); -} + [[maybe_unused]] occ::handle aRegistered = + myGraph.CacheRegistry().Ensure(); -TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKinds_ReportsStoredKind) -{ - BRepGraph_FaceId aFaceId(0); - occ::handle anAttr = new TestCacheValue(); - myGraph.Cache().Set(aFaceId, testUserAttrKind(), anAttr); - - BRepGraph_CacheKindIterator anIt = myGraph.Cache().CacheKindIter(aFaceId); - - ASSERT_EQ(anIt.NbKinds(), 1); - ASSERT_TRUE(anIt.More()); - ASSERT_FALSE(anIt.Value().IsNull()); - EXPECT_EQ(anIt.Value()->ID(), testUserAttrKind()->ID()); - EXPECT_EQ(myGraph.Cache().Get(aFaceId, testUserAttrKind()), anAttr); -} - -TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKindIter_RangeFor) -{ - BRepGraph_FaceId aFaceId(0); - myGraph.Cache().Set(aFaceId, testUserAttrKind(), new TestCacheValue()); - - int aCount = 0; - bool hasUserKind = false; - for (const occ::handle& aKind : myGraph.Cache().CacheKindIter(aFaceId)) + uint32_t aCount = 0; + bool hasTest = false; + for (const occ::handle aCache : myGraph.CacheRegistry().CacheIter()) { - if (!aKind.IsNull() && aKind->ID() == testUserAttrKind()->ID()) + if (!aCache.IsNull() && aCache->ID() == TestCacheService::GetID()) { - hasUserKind = true; + hasTest = true; } ++aCount; } - EXPECT_EQ(aCount, 1); - EXPECT_TRUE(hasUserKind); + EXPECT_EQ(aCount, 1u); + EXPECT_TRUE(hasTest); } -TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKindIter_RangeFor_Empty) +TEST_F(BRepGraph_ViewsTest, CacheRegistry_ClearAllClearsRepresentationButKeepsService) { - // No cache entries set - range-for should produce zero iterations. - int aCount = 0; - for (const occ::handle& aKind : - myGraph.Cache().CacheKindIter(BRepGraph_FaceId::Start())) - { - (void)aKind; - ++aCount; - } - EXPECT_EQ(aCount, 0); + occ::handle aCache = myGraph.CacheRegistry().Ensure(); + aCache->Set(BRepGraph_FaceId(0), 42); + int aValue = 0; + ASSERT_TRUE(aCache->Get(BRepGraph_FaceId(0), aValue)); + + myGraph.CacheRegistry().ClearAll(); + + EXPECT_FALSE(aCache->Get(BRepGraph_FaceId(0), aValue)); + EXPECT_EQ(aCache->ClearCount(), 1); + EXPECT_EQ(myGraph.CacheRegistry().Find(), aCache); } -TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKindIter_KindSlot) +TEST_F(BRepGraph_ViewsTest, CacheRegistry_GraphClearClearsRepresentationButKeepsService) { - BRepGraph_FaceId aFaceId(0); - myGraph.Cache().Set(aFaceId, testUserAttrKind(), new TestCacheValue()); + occ::handle aCache = myGraph.CacheRegistry().Ensure(); + aCache->Set(BRepGraph_FaceId(0), 42); - BRepGraph_CacheKindIterator anIt = myGraph.Cache().CacheKindIter(aFaceId); - ASSERT_TRUE(anIt.More()); - // KindSlot must be a valid non-negative index matching the registered slot. - EXPECT_GE(anIt.KindSlot(), 0); - // Retrieving via slot must return the same value as via kind handle. - occ::handle aByKind = myGraph.Cache().Get(aFaceId, testUserAttrKind()); - occ::handle aBySlot = myGraph.Cache().Get(aFaceId, anIt.KindSlot()); - EXPECT_EQ(aByKind, aBySlot); + myGraph.Clear(); + + int aValue = 0; + EXPECT_FALSE(aCache->Get(BRepGraph_FaceId(0), aValue)); + EXPECT_EQ(myGraph.CacheRegistry().Find(), aCache); } -TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKindIter_RefId_RangeFor) +TEST_F(BRepGraph_ViewsTest, CacheRegistry_UnregisterDetachesService) { - // Set a cache value on a FaceRef. - const BRepGraph_FaceRefId aRef(0); - myGraph.Cache().Set(aRef, testUserAttrKind(), new TestCacheValue()); + occ::handle aCache = myGraph.CacheRegistry().Ensure(); + ASSERT_TRUE(aCache->Attached()); - int aCount = 0; - bool hasUserKind = false; - for (const occ::handle& aKind : myGraph.Cache().CacheKindIter(aRef)) - { - if (!aKind.IsNull() && aKind->ID() == testUserAttrKind()->ID()) - { - hasUserKind = true; - } - ++aCount; - } - EXPECT_EQ(aCount, 1); - EXPECT_TRUE(hasUserKind); + myGraph.CacheRegistry().UnregisterCache(TestCacheService::GetID()); + + EXPECT_FALSE(aCache->Attached()); + EXPECT_TRUE(myGraph.CacheRegistry().Find().IsNull()); } -TEST_F(BRepGraph_ViewsTest, AttrsView_CacheKindIter_RefId_Empty) -{ - // No cache entries on this ref - iterator should be empty. - BRepGraph_CacheKindIterator anIt = - myGraph.Cache().CacheKindIter(BRepGraph_FaceRefId::Start()); - EXPECT_FALSE(anIt.More()); - EXPECT_EQ(anIt.NbKinds(), 0); -} - -TEST_F(BRepGraph_ViewsTest, EdgeOps_FindCoEdgeId_ValidPair) +TEST_F(BRepGraph_ViewsTest, BRepGraphTool_FindCoEdgeId_ValidPair) { // A box has edges shared by faces. Pick an edge and one of its faces. const BRepGraph_EdgeId anEdge(0); const BRepGraphInc::EdgeDef& anEdgeDef = myGraph.Topo().Edges().Definition(anEdge); - (void)anEdgeDef; + std::ignore = anEdgeDef; - // Use reverse index to get a face for this edge. - const NCollection_DynamicArray& aFaces = myGraph.Topo().Edges().Faces(anEdge); - ASSERT_GT(aFaces.Length(), 0); - const BRepGraph_FaceId aFace = aFaces.Value(0); + // Use relation tables to get a face for this edge. + const BRepGraph_FaceId aFace = firstFaceOfEdge(myGraph, anEdge); + ASSERT_TRUE(aFace.IsValid()); - const BRepGraph_CoEdgeId aCoEdgeId = myGraph.Topo().Edges().FindCoEdgeId(anEdge, aFace); + const BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_Tool::Edge::FindCoEdgeId(myGraph, anEdge, aFace); ASSERT_TRUE(aCoEdgeId.IsValid()); // The returned CoEdge must reference the same edge and face. const BRepGraphInc::CoEdgeDef& aCoEdge = myGraph.Topo().CoEdges().Definition(aCoEdgeId); - EXPECT_EQ(aCoEdge.EdgeDefId, anEdge); - EXPECT_EQ(aCoEdge.FaceDefId, aFace); + EXPECT_EQ(aCoEdge.ChildEdgeId, anEdge); + EXPECT_EQ(aCoEdge.FaceId, aFace); } -TEST_F(BRepGraph_ViewsTest, EdgeOps_FindCoEdgeId_InvalidPair_ReturnsInvalid) +TEST_F(BRepGraph_ViewsTest, BRepGraphTool_FindCoEdgeId_InvalidPair_ReturnsInvalid) { // Use a valid edge but a face that doesn't share it. // Edge 0 and the last face are very unlikely to share a coedge in a box. - const BRepGraph_EdgeId anEdge(0); - const NCollection_DynamicArray& aEdgeFaces = - myGraph.Topo().Edges().Faces(anEdge); + const BRepGraph_EdgeId anEdge(0); // Find a face NOT adjacent to edge 0. BRepGraph_FaceId aNonAdjacentFace; @@ -656,16 +776,7 @@ TEST_F(BRepGraph_ViewsTest, EdgeOps_FindCoEdgeId_InvalidPair_ReturnsInvalid) aFaceId.IsValid(myGraph.Topo().Faces().Nb()); ++aFaceId) { - bool isAdjacent = false; - for (const BRepGraph_FaceId& aFace : aEdgeFaces) - { - if (aFace.Index == aFaceId.Index) - { - isAdjacent = true; - break; - } - } - if (!isAdjacent) + if (!edgeHasFace(myGraph, anEdge, aFaceId)) { aNonAdjacentFace = aFaceId; break; @@ -674,81 +785,17 @@ TEST_F(BRepGraph_ViewsTest, EdgeOps_FindCoEdgeId_InvalidPair_ReturnsInvalid) if (aNonAdjacentFace.IsValid()) { const BRepGraph_CoEdgeId aCoEdgeId = - myGraph.Topo().Edges().FindCoEdgeId(anEdge, aNonAdjacentFace); + BRepGraph_Tool::Edge::FindCoEdgeId(myGraph, anEdge, aNonAdjacentFace); EXPECT_FALSE(aCoEdgeId.IsValid()); } } -TEST_F(BRepGraph_ViewsTest, AttrsView_MutFace_InvalidatesEntry) -{ - BRepGraph_FaceId aFaceId(0); - occ::handle anAttr = new TestCacheValue(); - myGraph.Cache().Set(aFaceId, testUserAttrKind(), anAttr); - ASSERT_TRUE(myGraph.Cache().Has(aFaceId, testUserAttrKind())); - - { - BRepGraph_MutGuard aFace = - myGraph.Editor().Faces().Mut(BRepGraph_FaceId::Start()); - myGraph.Editor().Faces().SetTolerance(aFace, aFace->Tolerance + 0.1); - } - - EXPECT_FALSE(myGraph.Cache().Has(aFaceId, testUserAttrKind())); - EXPECT_TRUE(myGraph.Cache().Get(aFaceId, testUserAttrKind()).IsNull()); - EXPECT_FALSE(myGraph.Cache().CacheKindIter(aFaceId).More()); -} - -TEST_F(BRepGraph_ViewsTest, AttrsView_RemoveNode_InvalidatesEntry) -{ - BRepGraph_FaceId aFaceId(0); - occ::handle anAttr = new TestCacheValue(); - myGraph.Cache().Set(aFaceId, testUserAttrKind(), anAttr); - ASSERT_TRUE(myGraph.Cache().Has(aFaceId, testUserAttrKind())); - - myGraph.Editor().Gen().RemoveNode(aFaceId); - - EXPECT_TRUE(myGraph.Topo().Gen().IsRemoved(aFaceId)); - EXPECT_FALSE(myGraph.Cache().Has(aFaceId, testUserAttrKind())); - EXPECT_FALSE(myGraph.Cache().CacheKindIter(aFaceId).More()); -} - -TEST_F(BRepGraph_ViewsTest, AttrsView_MutFaceRef_InvalidatesEntry) -{ - const BRepGraph_FaceRefId aRef(0); - myGraph.Cache().Set(aRef, testUserAttrKind(), new TestCacheValue()); - ASSERT_TRUE(myGraph.Cache().Has(aRef, testUserAttrKind())); - ASSERT_TRUE(myGraph.Cache().CacheKindIter(aRef).More()); - - { - BRepGraph_MutGuard aFaceRef = myGraph.Editor().Faces().MutRef(aRef); - myGraph.Editor().Faces().SetRefOrientation(aFaceRef, TopAbs::Reverse(aFaceRef->Orientation)); - } - - EXPECT_FALSE(myGraph.Cache().Has(aRef, testUserAttrKind())); - EXPECT_TRUE(myGraph.Cache().Get(aRef, testUserAttrKind()).IsNull()); - EXPECT_FALSE(myGraph.Cache().CacheKindIter(aRef).More()); -} - -TEST_F(BRepGraph_ViewsTest, AttrsView_RemoveFaceRef_HidesCacheKindIterator) -{ - const BRepGraph_FaceRefId aRef(0); - myGraph.Cache().Set(aRef, testUserAttrKind(), new TestCacheValue()); - ASSERT_TRUE(myGraph.Cache().Has(aRef, testUserAttrKind())); - - { - BRepGraph_MutGuard aFaceRef = myGraph.Editor().Faces().MutRef(aRef); - myGraph.Editor().Gen().RemoveRef(aRef); - } - - EXPECT_FALSE(myGraph.Cache().Has(aRef, testUserAttrKind())); - EXPECT_FALSE(myGraph.Cache().CacheKindIter(aRef).More()); -} - TEST_F(BRepGraph_ViewsTest, RefsView_ActiveCounts_MatchFreshBuild) { EXPECT_EQ(myGraph.Refs().Shells().NbActive(), myGraph.Refs().Shells().Nb()); EXPECT_EQ(myGraph.Refs().Faces().NbActive(), myGraph.Refs().Faces().Nb()); EXPECT_EQ(myGraph.Refs().Wires().NbActive(), myGraph.Refs().Wires().Nb()); - EXPECT_EQ(myGraph.Refs().CoEdges().NbActive(), myGraph.Refs().CoEdges().Nb()); + EXPECT_EQ(myGraph.Topo().CoEdges().NbActive(), myGraph.Topo().CoEdges().Nb()); EXPECT_EQ(myGraph.Refs().Vertices().NbActive(), myGraph.Refs().Vertices().Nb()); EXPECT_EQ(myGraph.Refs().Solids().NbActive(), myGraph.Refs().Solids().Nb()); EXPECT_EQ(myGraph.Refs().Children().NbActive(), myGraph.Refs().Children().Nb()); @@ -762,63 +809,40 @@ TEST_F(BRepGraph_ViewsTest, RefsView_RefIdsOf_MatchFreshBuild) const BRepGraph_WireId aWireId(0); const BRepGraph_SolidId aSolidId(0); - EXPECT_EQ( - countActiveRefs(myGraph.Refs().Faces().IdsOf(aShellId), - [this](const BRepGraph_FaceRefId theRefId) -> const BRepGraphInc::FaceRef& { - return myGraph.Refs().Faces().Entry(theRefId); - }), - myGraph.Refs().Faces().IdsOf(aShellId).Length()); - EXPECT_EQ( - countActiveRefs(myGraph.Refs().Wires().IdsOf(aFaceId), - [this](const BRepGraph_WireRefId theRefId) -> const BRepGraphInc::WireRef& { - return myGraph.Refs().Wires().Entry(theRefId); - }), - myGraph.Refs().Wires().IdsOf(aFaceId).Length()); - EXPECT_EQ( - countActiveRefs(myGraph.Refs().CoEdges().IdsOf(aWireId), - [this](const BRepGraph_CoEdgeRefId theRefId) -> const BRepGraphInc::CoEdgeRef& { - return myGraph.Refs().CoEdges().Entry(theRefId); - }), - myGraph.Refs().CoEdges().IdsOf(aWireId).Length()); - EXPECT_EQ( - countActiveRefs(myGraph.Refs().Shells().IdsOf(aSolidId), - [this](const BRepGraph_ShellRefId theRefId) -> const BRepGraphInc::ShellRef& { - return myGraph.Refs().Shells().Entry(theRefId); - }), - myGraph.Refs().Shells().IdsOf(aSolidId).Length()); + EXPECT_EQ(countActiveRefs(myGraph, myGraph.Refs().Faces().IdsOf(aShellId)), + myGraph.Refs().Faces().IdsOf(aShellId).Size()); + EXPECT_EQ(countActiveRefs(myGraph, myGraph.Refs().Wires().IdsOf(aFaceId)), + myGraph.Refs().Wires().IdsOf(aFaceId).Size()); + EXPECT_EQ(countActiveNodeIds(myGraph, myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds), + myGraph.Topo().Wires().Relations(aWireId).CoEdgeIds.Size()); + EXPECT_EQ(countActiveRefs(myGraph, myGraph.Refs().Shells().IdsOf(aSolidId)), + myGraph.Refs().Shells().IdsOf(aSolidId).Size()); } TEST_F(BRepGraph_ViewsTest, RefsView_FaceRefIdsOf_LocalFilteringHandlesRemoved) { const BRepGraph_ShellId aShellId(0); - const NCollection_DynamicArray& aFaceRefs = + const NCollection_LinearVector& aFaceRefs = myGraph.Refs().Faces().IdsOf(aShellId); - ASSERT_GT(aFaceRefs.Length(), 0); + ASSERT_GT(aFaceRefs.Size(), 0); + const size_t aNbFaceRefsBefore = aFaceRefs.Size(); - { - BRepGraph_MutGuard aFaceRef = - myGraph.Editor().Faces().MutRef(aFaceRefs.Value(0)); - myGraph.Editor().Gen().RemoveRef(aFaceRefs.Value(0)); - } + myGraph.Editor().Gen().RemoveRef(aFaceRefs.Value(0)); - EXPECT_EQ( - countActiveRefs(myGraph.Refs().Faces().IdsOf(aShellId), - [this](const BRepGraph_FaceRefId theRefId) -> const BRepGraphInc::FaceRef& { - return myGraph.Refs().Faces().Entry(theRefId); - }), - aFaceRefs.Length() - 1); + EXPECT_EQ(countActiveRefs(myGraph, myGraph.Refs().Faces().IdsOf(aShellId)), + aNbFaceRefsBefore - 1); } TEST_F(BRepGraph_ViewsTest, RefsView_VertexRefIdsOfEdge_ContainsBoundaryVertices) { - int aNbVertexRefs = 0; + uint32_t aNbVertexRefs = 0; for (BRepGraph_RefsVertexOfEdge aRefIt(myGraph, BRepGraph_EdgeId::Start()); aRefIt.More(); aRefIt.Next()) { const BRepGraph_VertexRefId aVertexRefId = aRefIt.CurrentId(); const BRepGraphInc::VertexRef& aRef = myGraph.Refs().Vertices().Entry(aVertexRefId); - EXPECT_FALSE(aRef.IsRemoved); - EXPECT_TRUE(aRef.VertexDefId.IsValid(myGraph.Topo().Vertices().Nb())); + EXPECT_FALSE(aVertexRefId.IsRemoved(myGraph)); + EXPECT_TRUE(aRef.ChildVertexId.IsValid(myGraph.Topo().Vertices().Nb())); ++aNbVertexRefs; } EXPECT_GE(aNbVertexRefs, 2); @@ -826,97 +850,106 @@ TEST_F(BRepGraph_ViewsTest, RefsView_VertexRefIdsOfEdge_ContainsBoundaryVertices TEST_F(BRepGraph_ViewsTest, RefsView_GenericRefHelpers_RoundTripForTypedRef) { - const NCollection_DynamicArray& aFaceRefs = + const NCollection_LinearVector& aFaceRefs = myGraph.Refs().Faces().IdsOf(BRepGraph_ShellId::Start()); - ASSERT_GT(aFaceRefs.Length(), 0); + ASSERT_GT(aFaceRefs.Size(), 0); const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.Value(0); - gp_Trsf aTrsf; - aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0)); - { BRepGraph_MutGuard aFaceRef = myGraph.Editor().Faces().MutRef(aFaceRefId); - myGraph.Editor().Faces().SetRefLocalLocation(aFaceRef, TopLoc_Location(aTrsf)); myGraph.Editor().Faces().SetRefOrientation(aFaceRef, TopAbs_REVERSED); } const BRepGraphInc::FaceRef& aFaceRefEntry = myGraph.Refs().Faces().Entry(aFaceRefId); - EXPECT_EQ(myGraph.Refs().ChildNode(aFaceRefId), BRepGraph_NodeId(aFaceRefEntry.FaceDefId)); - EXPECT_TRUE(myGraph.Refs().LocalLocation(aFaceRefId).IsEqual(aFaceRefEntry.LocalLocation)); - EXPECT_EQ(myGraph.Refs().Orientation(aFaceRefId), aFaceRefEntry.Orientation); - EXPECT_FALSE(myGraph.Refs().IsRemoved(aFaceRefId)); + EXPECT_EQ(myGraph.Refs().Gen().ChildNode(aFaceRefId), + BRepGraph_NodeId(aFaceRefEntry.ChildFaceId)); + EXPECT_TRUE(myGraph.Refs().Gen().LocalLocation(aFaceRefId).IsIdentity()); + EXPECT_EQ(myGraph.Refs().Gen().Orientation(aFaceRefId), aFaceRefEntry.Orientation); + EXPECT_EQ(myGraph.Refs().Gen().Nb(BRepGraph_RefId::Kind::Face), myGraph.Refs().Faces().Nb()); + EXPECT_TRUE(myGraph.Refs().Gen().IsValid(aFaceRefId)); + EXPECT_TRUE(myGraph.Refs().Gen().IsActive(aFaceRefId)); + EXPECT_FALSE(myGraph.Refs().Gen().IsRemoved(aFaceRefId)); - const BRepGraph_CoEdgeRefId aCoEdgeRefId = - myGraph.Topo().Wires().Definition(BRepGraph_WireId::Start()).CoEdgeRefIds.Value(0); - const BRepGraphInc::CoEdgeRef& aCoEdgeRefEntry = myGraph.Refs().CoEdges().Entry(aCoEdgeRefId); - EXPECT_EQ(myGraph.Refs().ChildNode(aCoEdgeRefId), BRepGraph_NodeId(aCoEdgeRefEntry.CoEdgeDefId)); - EXPECT_TRUE(myGraph.Refs().LocalLocation(aCoEdgeRefId).IsEqual(aCoEdgeRefEntry.LocalLocation)); - EXPECT_EQ(myGraph.Refs().Orientation(aCoEdgeRefId), TopAbs_FORWARD); + const BRepGraph_CoEdgeId aCoEdgeId = + myGraph.Topo().Wires().Relations(BRepGraph_WireId::Start()).CoEdgeIds.Value(0); + const BRepGraphInc::CoEdgeDef& aCoEdgeEntry = myGraph.Topo().CoEdges().Definition(aCoEdgeId); + EXPECT_TRUE(aCoEdgeEntry.ChildEdgeId.IsValid()); + EXPECT_TRUE(aCoEdgeEntry.FaceId.IsValid()); + EXPECT_NE(aCoEdgeEntry.Orientation, TopAbs_INTERNAL); + EXPECT_NE(aCoEdgeEntry.Orientation, TopAbs_EXTERNAL); } TEST_F(BRepGraph_ViewsTest, RefsView_RefAtStep_RoundTrip) { const BRepGraph_SolidId aSolidId(0); const BRepGraph_ShellRefId aShellRefId = - myGraph.Topo().Solids().Definition(aSolidId).ShellRefIds.Value(0); - EXPECT_EQ(myGraph.Refs().RefAtStep(BRepGraph_NodeId(aSolidId), 0), BRepGraph_RefId(aShellRefId)); + myGraph.Topo().Solids().Relations(aSolidId).ShellRefIds.Value(0); + EXPECT_EQ(myGraph.Refs().Gen().RefAtStep(BRepGraph_NodeId(aSolidId), 0), + BRepGraph_RefId(aShellRefId)); - const BRepGraph_WireId aWireId(0); - const BRepGraph_CoEdgeRefId aCoEdgeRefId = - myGraph.Topo().Wires().Definition(aWireId).CoEdgeRefIds.Value(0); - EXPECT_EQ(myGraph.Refs().RefAtStep(BRepGraph_NodeId(aWireId), 0), BRepGraph_RefId(aCoEdgeRefId)); + const BRepGraph_WireId aWireId(0); + EXPECT_FALSE(myGraph.Refs().Gen().RefAtStep(BRepGraph_NodeId(aWireId), 0).IsValid()); - EXPECT_FALSE(myGraph.Refs().RefAtStep(BRepGraph_NodeId(aWireId), 100).IsValid()); + EXPECT_FALSE(myGraph.Refs().Gen().RefAtStep(BRepGraph_NodeId(aWireId), 100).IsValid()); } -TEST_F(BRepGraph_ViewsTest, RefsView_GenericRefHelpers_OccurrenceDefaults) +TEST_F(BRepGraph_ViewsTest, RefsView_GenericRefHelpers_OccurrenceLocalLocation) { const BRepGraph_ProductId aPartProduct = - myGraph.Editor().Products().LinkProductToTopology(BRepGraph_NodeId(BRepGraph_SolidId::Start())); - const BRepGraph_ProductId anAssembly = myGraph.Editor().Products().CreateEmptyProduct(); + myGraph.Editor().Products().Add(BRepGraph_NodeId(BRepGraph_SolidId::Start())); + myGraph.Editor().Products().AppendDocumentRoot(aPartProduct); + const BRepGraph_ProductId anAssembly = myGraph.Editor().Products().Add(); + myGraph.Editor().Products().AppendDocumentRoot(anAssembly); ASSERT_TRUE(aPartProduct.IsValid()); ASSERT_TRUE(anAssembly.IsValid()); gp_Trsf aTrsf; aTrsf.SetTranslation(gp_Vec(4.0, 5.0, 6.0)); - ASSERT_TRUE(myGraph.Editor() - .Products() - .LinkProducts(anAssembly, aPartProduct, TopLoc_Location(aTrsf)) - .IsValid()); + ASSERT_TRUE( + myGraph.Editor().Products().Append(anAssembly, aPartProduct, TopLoc_Location(aTrsf)).IsValid()); - const NCollection_DynamicArray& anOccurrenceRefs = + const NCollection_LinearVector& anOccurrenceRefs = myGraph.Refs().Occurrences().IdsOf(anAssembly); - ASSERT_EQ(anOccurrenceRefs.Length(), 1); + ASSERT_EQ(anOccurrenceRefs.Size(), 1); const BRepGraph_OccurrenceRefId aRefId = anOccurrenceRefs.Value(0); const BRepGraphInc::OccurrenceRef& aRefEntry = myGraph.Refs().Occurrences().Entry(aRefId); - EXPECT_EQ(myGraph.Refs().RefAtStep(BRepGraph_NodeId(anAssembly), 0), BRepGraph_RefId(aRefId)); - EXPECT_EQ(myGraph.Refs().ChildNode(aRefId), BRepGraph_NodeId(aRefEntry.OccurrenceDefId)); - EXPECT_TRUE(myGraph.Refs().LocalLocation(aRefId).IsEqual(TopLoc_Location())); - EXPECT_EQ(myGraph.Refs().Orientation(aRefId), TopAbs_FORWARD); - EXPECT_FALSE(myGraph.Refs().IsRemoved(aRefId)); + EXPECT_EQ(myGraph.Refs().Gen().RefAtStep(BRepGraph_NodeId(anAssembly), 0), + BRepGraph_RefId(aRefId)); + EXPECT_EQ(myGraph.Refs().Gen().ChildNode(aRefId), BRepGraph_NodeId(aRefEntry.ChildOccurrenceId)); + EXPECT_TRUE(myGraph.Refs().Gen().LocalLocation(aRefId).IsEqual(aRefEntry.LocalLocation)); + EXPECT_EQ(myGraph.Refs().Gen().Orientation(aRefId), TopAbs_FORWARD); + EXPECT_FALSE(myGraph.Refs().Gen().IsRemoved(aRefId)); } TEST_F(BRepGraph_ViewsTest, RefsView_GenericRefHelpers_InvalidAndRemoved) { - EXPECT_FALSE(myGraph.Refs().ChildNode(BRepGraph_RefId()).IsValid()); - EXPECT_TRUE(myGraph.Refs().LocalLocation(BRepGraph_RefId()).IsEqual(TopLoc_Location())); - EXPECT_EQ(myGraph.Refs().Orientation(BRepGraph_RefId()), TopAbs_FORWARD); - EXPECT_TRUE(myGraph.Refs().IsRemoved(BRepGraph_RefId())); + EXPECT_FALSE(myGraph.Refs().Gen().ChildNode(BRepGraph_RefId()).IsValid()); + EXPECT_TRUE(myGraph.Refs().Gen().LocalLocation(BRepGraph_RefId()).IsEqual(TopLoc_Location())); + EXPECT_EQ(myGraph.Refs().Gen().Orientation(BRepGraph_RefId()), TopAbs_FORWARD); + EXPECT_FALSE(myGraph.Refs().Gen().IsValid(BRepGraph_RefId())); + EXPECT_FALSE(myGraph.Refs().Gen().IsActive(BRepGraph_RefId())); + EXPECT_TRUE(myGraph.Refs().Gen().IsRemoved(BRepGraph_RefId())); - const NCollection_DynamicArray& aFaceRefs = + const NCollection_LinearVector& aFaceRefs = myGraph.Refs().Faces().IdsOf(BRepGraph_ShellId::Start()); - ASSERT_GT(aFaceRefs.Length(), 0); + ASSERT_GT(aFaceRefs.Size(), 0); const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.Value(0); - { - BRepGraph_MutGuard aFaceRef = - myGraph.Editor().Faces().MutRef(aFaceRefId); - myGraph.Editor().Gen().RemoveRef(aFaceRefId); - } + myGraph.Editor().Gen().RemoveRef(aFaceRefId); - EXPECT_TRUE(myGraph.Refs().IsRemoved(aFaceRefId)); + EXPECT_TRUE(myGraph.Refs().Gen().IsValid(aFaceRefId)); + EXPECT_FALSE(myGraph.Refs().Gen().IsActive(aFaceRefId)); + EXPECT_TRUE(myGraph.Refs().Gen().IsRemoved(aFaceRefId)); + + const BRepGraph_FaceRefId anOutOfRangeFaceRef(myGraph.Refs().Faces().Nb()); + EXPECT_FALSE(myGraph.Refs().Gen().IsValid(anOutOfRangeFaceRef)); + EXPECT_FALSE(myGraph.Refs().Gen().IsActive(anOutOfRangeFaceRef)); + EXPECT_TRUE(myGraph.Refs().Gen().IsRemoved(anOutOfRangeFaceRef)); + EXPECT_FALSE(myGraph.Refs().Gen().ChildNode(anOutOfRangeFaceRef).IsValid()); + EXPECT_TRUE(myGraph.Refs().Gen().LocalLocation(anOutOfRangeFaceRef).IsEqual(TopLoc_Location())); + EXPECT_EQ(myGraph.Refs().Gen().Orientation(anOutOfRangeFaceRef), TopAbs_FORWARD); } // ---------- ShapesView ---------- @@ -933,25 +966,16 @@ TEST_F(BRepGraph_ViewsTest, ShapesView_HasOriginal_True) EXPECT_TRUE(myGraph.Shapes().HasOriginal(aFaceId)); } -TEST_F(BRepGraph_ViewsTest, ShapesView_FindOriginal_ValidNode_ReturnsPointer) +TEST_F(BRepGraph_ViewsTest, ShapesView_Original_ValidNode_ReturnsShape) { const BRepGraph_FaceId aFaceId(0); - const TopoDS_Shape* aFound = myGraph.Shapes().FindOriginal(aFaceId); - const TopoDS_Shape& anOrig = myGraph.Shapes().OriginalOf(aFaceId); - ASSERT_NE(aFound, nullptr); - EXPECT_TRUE(aFound->IsSame(anOrig)); + const TopoDS_Shape aFound = myGraph.Shapes().Original(aFaceId); + EXPECT_FALSE(aFound.IsNull()); } -TEST_F(BRepGraph_ViewsTest, ShapesView_FindOriginal_InvalidNode_ReturnsNull) +TEST_F(BRepGraph_ViewsTest, ShapesView_Original_InvalidNode_ReturnsNull) { - EXPECT_EQ(myGraph.Shapes().FindOriginal(BRepGraph_NodeId()), nullptr); -} - -TEST_F(BRepGraph_ViewsTest, ShapesView_OriginalOf_InvalidNode_Throws) -{ -#ifndef No_Exception - EXPECT_THROW((void)myGraph.Shapes().OriginalOf(BRepGraph_NodeId()), Standard_ProgramError); -#endif + EXPECT_TRUE(myGraph.Shapes().Original(BRepGraph_NodeId()).IsNull()); } TEST_F(BRepGraph_ViewsTest, ShapesView_RemovedNode_OriginalQueries_AreUnavailable) @@ -962,11 +986,8 @@ TEST_F(BRepGraph_ViewsTest, ShapesView_RemovedNode_OriginalQueries_AreUnavailabl myGraph.Editor().Gen().RemoveNode(aFaceId); EXPECT_FALSE(myGraph.Shapes().HasOriginal(aFaceId)); - EXPECT_EQ(myGraph.Shapes().FindOriginal(aFaceId), nullptr); + EXPECT_TRUE(myGraph.Shapes().Original(aFaceId).IsNull()); EXPECT_TRUE(myGraph.Shapes().Shape(aFaceId).IsNull()); -#ifndef No_Exception - EXPECT_THROW((void)myGraph.Shapes().OriginalOf(aFaceId), Standard_ProgramError); -#endif } TEST_F(BRepGraph_ViewsTest, ShapesView_Reconstruct_InvalidNode_ReturnsNull) @@ -984,9 +1005,8 @@ TEST_F(BRepGraph_ViewsTest, ShapesView_Reconstruct_RemovedNode_ReturnsNull) TEST_F(BRepGraph_ViewsTest, ShapesView_FindNodeAndHasNode_RemovedNode_AreUnavailable) { const BRepGraph_FaceId aFaceId(0); - const TopoDS_Shape* aOrig = myGraph.Shapes().FindOriginal(aFaceId); - ASSERT_NE(aOrig, nullptr); - const TopoDS_Shape aFaceShape = *aOrig; + const TopoDS_Shape aFaceShape = myGraph.Shapes().Original(aFaceId); + ASSERT_FALSE(aFaceShape.IsNull()); ASSERT_TRUE(myGraph.Shapes().HasNode(aFaceShape)); ASSERT_EQ(myGraph.Shapes().FindNode(aFaceShape), BRepGraph_NodeId(aFaceId)); @@ -1012,7 +1032,8 @@ TEST_F(BRepGraph_ViewsTest, MutView_EdgeDef_IncrementsOwnGen) TEST_F(BRepGraph_ViewsTest, MutView_InvalidNode_ThrowsProgramError) { #ifndef No_Exception - EXPECT_THROW((void)myGraph.Editor().Faces().Mut(BRepGraph_FaceId(777777)), Standard_ProgramError); + EXPECT_THROW(std::ignore = myGraph.Editor().Faces().Mut(BRepGraph_FaceId(777777)), + Standard_ProgramError); #endif } @@ -1021,32 +1042,20 @@ TEST_F(BRepGraph_ViewsTest, MutView_RemovedNode_ThrowsProgramError) #ifndef No_Exception const BRepGraph_FaceId aFaceId(0); myGraph.Editor().Gen().RemoveNode(aFaceId); - EXPECT_THROW((void)myGraph.Editor().Faces().Mut(aFaceId), Standard_ProgramError); + EXPECT_THROW(std::ignore = myGraph.Editor().Faces().Mut(aFaceId), Standard_ProgramError); #endif } TEST_F(BRepGraph_ViewsTest, MutView_RemovedRef_ThrowsProgramError) { - const NCollection_DynamicArray& aFaceRefs = + const NCollection_LinearVector& aFaceRefs = myGraph.Refs().Faces().IdsOf(BRepGraph_ShellId::Start()); - ASSERT_GT(aFaceRefs.Length(), 0); + ASSERT_GT(aFaceRefs.Size(), 0); const BRepGraph_FaceRefId aFaceRefId = aFaceRefs.Value(0); ASSERT_TRUE(myGraph.Editor().Gen().RemoveRef(aFaceRefId)); #ifndef No_Exception - EXPECT_THROW((void)myGraph.Editor().Faces().MutRef(aFaceRefId), Standard_ProgramError); -#endif -} - -TEST_F(BRepGraph_ViewsTest, MutView_RemovedRep_ThrowsProgramError) -{ - const BRepGraph_SurfaceRepId aSurfaceRepId = - myGraph.Topo().Faces().SurfaceRepId(BRepGraph_FaceId::Start()); - ASSERT_TRUE(aSurfaceRepId.IsValid()); - - myGraph.Editor().Gen().RemoveRep(aSurfaceRepId); -#ifndef No_Exception - EXPECT_THROW((void)myGraph.Editor().Reps().MutSurface(aSurfaceRepId), Standard_ProgramError); + EXPECT_THROW(std::ignore = myGraph.Editor().Faces().MutRef(aFaceRefId), Standard_ProgramError); #endif } @@ -1054,7 +1063,7 @@ TEST_F(BRepGraph_ViewsTest, MutView_RemovedRep_ThrowsProgramError) TEST_F(BRepGraph_ViewsTest, EditorView_AddVertex_Works) { - const int aNbBefore = myGraph.Topo().Vertices().Nb(); + const uint32_t aNbBefore = myGraph.Topo().Vertices().Nb(); BRepGraph_VertexId aVtx = myGraph.Editor().Vertices().Add(gp_Pnt(1, 2, 3), 0.001); EXPECT_TRUE(aVtx.IsValid()); EXPECT_EQ(myGraph.Topo().Vertices().Nb(), aNbBefore + 1); @@ -1066,63 +1075,439 @@ TEST_F(BRepGraph_ViewsTest, EditorView_IsRemoved_False) EXPECT_FALSE(myGraph.Topo().Gen().IsRemoved(aFaceId)); } -TEST_F(BRepGraph_ViewsTest, EditorView_RemoveRep_Surface_HidesSurfaceQueries) +// ---------- History layer lookup ---------- + +TEST_F(BRepGraph_ViewsTest, History_ConstLookup) { - const BRepGraph_FaceId aFaceId(0); - const BRepGraph_SurfaceRepId aSurfaceRepId = myGraph.Topo().Faces().SurfaceRepId(aFaceId); - ASSERT_TRUE(aSurfaceRepId.IsValid()); - ASSERT_TRUE(BRepGraph_Tool::Face::HasSurface(myGraph, aFaceId)); - - myGraph.Editor().Gen().RemoveRep(aSurfaceRepId); - - EXPECT_TRUE(myGraph.Topo().Geometry().SurfaceRep(aSurfaceRepId).IsRemoved); - EXPECT_FALSE(myGraph.Topo().Faces().SurfaceRepId(aFaceId).IsValid()); - EXPECT_FALSE(BRepGraph_Tool::Face::HasSurface(myGraph, aFaceId)); - EXPECT_TRUE(BRepGraph_Tool::Face::Surface(myGraph, aFaceId).IsNull()); + const BRepGraph& aConstGraph = myGraph; + const BRepGraph_LayerHistory* aHistory = + aConstGraph.LayerRegistry().Find().get(); + ASSERT_NE(aHistory, nullptr); + EXPECT_TRUE(aHistory->IsEnabled()); } -TEST_F(BRepGraph_ViewsTest, EditorView_RemoveRep_CurveAndPCurve_HideCurveQueries) +TEST_F(BRepGraph_ViewsTest, History_MutableLookup) { - const BRepGraph_EdgeId anEdgeId(0); - const BRepGraph_Curve3DRepId aCurve3DRepId = myGraph.Topo().Edges().Curve3DRepId(anEdgeId); - ASSERT_TRUE(aCurve3DRepId.IsValid()); - ASSERT_TRUE(BRepGraph_Tool::Edge::HasCurve(myGraph, anEdgeId)); - - myGraph.Editor().Gen().RemoveRep(aCurve3DRepId); - - EXPECT_TRUE(myGraph.Topo().Geometry().Curve3DRep(aCurve3DRepId).IsRemoved); - EXPECT_FALSE(myGraph.Topo().Edges().Curve3DRepId(anEdgeId).IsValid()); - EXPECT_FALSE(BRepGraph_Tool::Edge::HasCurve(myGraph, anEdgeId)); - EXPECT_TRUE(BRepGraph_Tool::Edge::Curve(myGraph, anEdgeId).IsNull()); - - const NCollection_DynamicArray& aCoEdges = - myGraph.Topo().Edges().CoEdges(anEdgeId); - ASSERT_GT(aCoEdges.Length(), 0); - const BRepGraph_CoEdgeId aCoEdgeId = aCoEdges.Value(0); - const BRepGraph_Curve2DRepId aCurve2DRepId = myGraph.Topo().CoEdges().Curve2DRepId(aCoEdgeId); - ASSERT_TRUE(aCurve2DRepId.IsValid()); - ASSERT_TRUE(BRepGraph_Tool::CoEdge::HasPCurve(myGraph, aCoEdgeId)); - - myGraph.Editor().Gen().RemoveRep(aCurve2DRepId); - - EXPECT_TRUE(myGraph.Topo().Geometry().Curve2DRep(aCurve2DRepId).IsRemoved); - EXPECT_FALSE(myGraph.Topo().CoEdges().Curve2DRepId(aCoEdgeId).IsValid()); - EXPECT_FALSE(BRepGraph_Tool::CoEdge::HasPCurve(myGraph, aCoEdgeId)); - EXPECT_TRUE(BRepGraph_Tool::CoEdge::PCurve(myGraph, aCoEdgeId).IsNull()); + myGraph.LayerRegistry().Ensure()->SetEnabled(false); + EXPECT_FALSE(myGraph.LayerRegistry().Ensure()->IsEnabled()); + myGraph.LayerRegistry().Ensure()->SetEnabled(true); + EXPECT_TRUE(myGraph.LayerRegistry().Ensure()->IsEnabled()); } -// ---------- History() accessor ---------- +// ---------- BRepGraph_Tool bug-fix regression tests ---------- -TEST_F(BRepGraph_ViewsTest, History_ConstAccessor) +TEST_F(BRepGraph_ViewsTest, BRepGraphTool_FindPCurveCoEdgeId_SkipsRemovedCoEdges) { - const BRepGraph& aConstGraph = myGraph; - EXPECT_TRUE(aConstGraph.History().IsEnabled()); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_WireId aWireId = BRepGraph_Tool::Face::OuterWire(myGraph, aFaceId); + ASSERT_TRUE(aWireId.IsValid()); + + const BRepGraphInc::WireRelations& aWireRelations = myGraph.Topo().Wires().Relations(aWireId); + ASSERT_GT(aWireRelations.CoEdgeIds.Size(), 0); + const BRepGraph_CoEdgeId aCoEdgeId = aWireRelations.CoEdgeIds.Value(0); + ASSERT_TRUE(aCoEdgeId.IsValid()); + + const BRepGraphInc::CoEdgeDef& aCoEdgeDef = myGraph.Topo().CoEdges().Definition(aCoEdgeId); + const BRepGraph_EdgeId anEdgeId = aCoEdgeDef.ChildEdgeId; + const BRepGraph_FaceId aFaceId2 = aCoEdgeDef.FaceId; + + const BRepGraph_CoEdgeId aFoundBefore = + BRepGraph_Tool::Edge::FindCoEdgeId(myGraph, anEdgeId, aFaceId2); + ASSERT_EQ(aFoundBefore, aCoEdgeId); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aCoEdgeId)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(aCoEdgeId)); + + const BRepGraph_CoEdgeId aPCurveId = + BRepGraph_Tool::Edge::FindPCurveCoEdgeId(myGraph, anEdgeId, aFaceId2); + EXPECT_FALSE(aPCurveId.IsValid()); + + const BRepGraph_CoEdgeId aFoundAfter = + BRepGraph_Tool::Edge::FindCoEdgeId(myGraph, anEdgeId, aFaceId2); + EXPECT_NE(aFoundAfter, aCoEdgeId); } -TEST_F(BRepGraph_ViewsTest, History_MutableAccessor) +TEST_F(BRepGraph_ViewsTest, BRepGraphTool_OuterWire_SkipsRemovedWireDef) { - myGraph.History().SetEnabled(false); - EXPECT_FALSE(myGraph.History().IsEnabled()); - myGraph.History().SetEnabled(true); - EXPECT_TRUE(myGraph.History().IsEnabled()); + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_WireId aWireId = BRepGraph_Tool::Face::OuterWire(myGraph, aFaceId); + ASSERT_TRUE(aWireId.IsValid()); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aWireId)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(aWireId)); + + const BRepGraph_WireId anOuterAfter = BRepGraph_Tool::Face::OuterWire(myGraph, aFaceId); + EXPECT_NE(anOuterAfter, aWireId); +} + +TEST_F(BRepGraph_ViewsTest, BRepGraphTool_OuterWire_SkipsWireWithoutUVBounds) +{ + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + const BRepGraph_WireId aValidWire = BRepGraph_Tool::Face::OuterWire(myGraph, aFaceId); + ASSERT_TRUE(aValidWire.IsValid()); + + NCollection_LinearVector anEmptyCoEdges; + const BRepGraph_WireId anEmptyWire = myGraph.Editor().Wires().Add(anEmptyCoEdges.ToArray1()); + ASSERT_TRUE(anEmptyWire.IsValid()); + + const BRepGraph_WireRefId anEmptyWireRef = + myGraph.Editor().Faces().Append(aFaceId, anEmptyWire, TopAbs_FORWARD); + ASSERT_TRUE(anEmptyWireRef.IsValid()); + + EXPECT_EQ(BRepGraph_Tool::Face::OuterWire(myGraph, aFaceId), aValidWire); +} + +TEST_F(BRepGraph_ViewsTest, BRepGraphTool_IsBoundary_SkipsRemovedFaces) +{ + BRepGraph_EdgeId anEdgeWithTwoFaces; + for (BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + anEdgeId.IsValid(myGraph.Topo().Edges().Nb()); + ++anEdgeId) + { + if (countIterator(myGraph.Topo().Edges().FacesOf(anEdgeId)) == 2) + { + anEdgeWithTwoFaces = anEdgeId; + break; + } + } + ASSERT_TRUE(anEdgeWithTwoFaces.IsValid()); + ASSERT_EQ(countIterator(myGraph.Topo().Edges().FacesOf(anEdgeWithTwoFaces)), 2); + ASSERT_FALSE(BRepGraph_Tool::Edge::IsBoundary(myGraph, anEdgeWithTwoFaces)); + ASSERT_TRUE(BRepGraph_Tool::Edge::IsManifold(myGraph, anEdgeWithTwoFaces)); + + const BRepGraph_FaceId aFaceToRemove = firstFaceOfEdge(myGraph, anEdgeWithTwoFaces); + ASSERT_TRUE(aFaceToRemove.IsValid()); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFaceToRemove)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(aFaceToRemove)); + + EXPECT_TRUE(BRepGraph_Tool::Edge::IsBoundary(myGraph, anEdgeWithTwoFaces)); + EXPECT_FALSE(BRepGraph_Tool::Edge::IsManifold(myGraph, anEdgeWithTwoFaces)); +} + +TEST_F(BRepGraph_ViewsTest, BRepGraphTool_IsBoundaryAndIsManifold_RemovedEdge_ReturnsFalse) +{ + BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + ASSERT_TRUE(anEdgeId.IsValid(myGraph.Topo().Edges().Nb())); + ASSERT_FALSE(myGraph.Topo().Gen().IsRemoved(anEdgeId)); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(anEdgeId)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(anEdgeId)); + + EXPECT_FALSE(BRepGraph_Tool::Edge::IsBoundary(myGraph, anEdgeId)); + EXPECT_FALSE(BRepGraph_Tool::Edge::IsManifold(myGraph, anEdgeId)); +} + +TEST_F(BRepGraph_ViewsTest, BRepGraphTool_SeamPair_BoundsCheck) +{ + myGraph.Clear(); + BRepPrimAPI_MakeSphere aSphereMaker(gp_Pnt(0, 0, 0), 10.0); + [[maybe_unused]] const auto aRes = myGraph.Shapes().Add(aSphereMaker.Shape()); + ASSERT_FALSE(myGraph.IsEmpty()); + + bool aFoundSeamPair = false; + for (BRepGraph_CoEdgeId aCoEdgeId = BRepGraph_CoEdgeId::Start(); + aCoEdgeId.IsValid(myGraph.Topo().CoEdges().Nb()); + ++aCoEdgeId) + { + const BRepGraph_CoEdgeId aSeamPair = BRepGraph_Tool::CoEdge::SeamPair(myGraph, aCoEdgeId); + if (aSeamPair.IsValid()) + { + aFoundSeamPair = true; + const BRepGraph_CoEdgeId aBack = BRepGraph_Tool::CoEdge::SeamPair(myGraph, aSeamPair); + EXPECT_EQ(aBack, aCoEdgeId); + break; + } + } + EXPECT_TRUE(aFoundSeamPair); +} + +// ---------- EditorView bug-fix regression tests ---------- + +TEST_F(BRepGraph_ViewsTest, EditorView_ReplaceEdge_SkipsRemovedCoEdgeDef) +{ + BRepPrimAPI_MakeBox aBoxMaker1(10.0, 20.0, 30.0); + BRepPrimAPI_MakeBox aBoxMaker2(10.0, 20.0, 30.0); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aBoxMaker1.Shape()); + aBuilder.Add(aCompound, aBoxMaker2.Shape()); + + myGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = myGraph.Shapes().Add(aCompound); + ASSERT_FALSE(myGraph.IsEmpty()); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + [[maybe_unused]] const BRepGraph_Deduplicate::Result aDedupRes = + BRepGraph_Deduplicate::Perform(myGraph, anOpts); + + std::ignore = BRepGraph_Compact::Perform(myGraph); + + const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(myGraph); + EXPECT_TRUE(aValResult.IsValid()); +} + +TEST_F(BRepGraph_ViewsTest, EditorView_RemoveSolid_PropagatesSubtreeGenToParent) +{ + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + + BRep_Builder aBB; + TopoDS_Compound aCompound; + aBB.MakeCompound(aCompound); + aBB.Add(aCompound, aBox); + + myGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = myGraph.Shapes().Add(aCompound); + ASSERT_FALSE(myGraph.IsEmpty()); + ASSERT_EQ(myGraph.Topo().Compounds().Nb(), 1); + ASSERT_EQ(myGraph.Topo().Solids().Nb(), 1); + + const BRepGraph_SolidId aSolidId = BRepGraph_SolidId::Start(); + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aSolidId)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(aSolidId)); + + std::ignore = BRepGraph_Compact::Perform(myGraph); + + const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(myGraph); + EXPECT_TRUE(aValResult.IsValid()); +} + +TEST_F(BRepGraph_ViewsTest, EditorView_RemoveOccurrence_UnbindsProductOccurrence) +{ + BRepGraph aGraph; + + BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + BRepBuilderAPI_Copy aCopy1(aBox, true); + BRepBuilderAPI_Copy aCopy2(aBox, true); + + BRep_Builder aBB; + TopoDS_Compound aCompound; + aBB.MakeCompound(aCompound); + aBB.Add(aCompound, aCopy1.Shape()); + aBB.Add(aCompound, aCopy2.Shape()); + + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = aGraph.Shapes().Add(aCompound); + ASSERT_FALSE(aGraph.IsEmpty()); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + [[maybe_unused]] const BRepGraph_Deduplicate::Result aDedupRes = + BRepGraph_Deduplicate::Perform(aGraph, anOpts); + + BRepGraph_Compact::Options aCompactOpts; + [[maybe_unused]] const BRepGraph_Compact::Result aCompactRes = + BRepGraph_Compact::Perform(aGraph, aCompactOpts); + + EXPECT_TRUE(aGraph.ValidateRelations()); +} + +TEST_F(BRepGraph_ViewsTest, EditorView_ReplaceEdge_RejectsRemovedWire) +{ + BRepPrimAPI_MakeBox aBoxMaker1(10.0, 20.0, 30.0); + BRepPrimAPI_MakeBox aBoxMaker2(10.0, 20.0, 30.0); + + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + aBuilder.MakeCompound(aCompound); + aBuilder.Add(aCompound, aBoxMaker1.Shape()); + aBuilder.Add(aCompound, aBoxMaker2.Shape()); + + myGraph.Clear(); + [[maybe_unused]] const BRepGraph::ShapesView::Result aBuildRes = myGraph.Shapes().Add(aCompound); + ASSERT_FALSE(myGraph.IsEmpty()); + + BRepGraph_Deduplicate::Options anOpts; + anOpts.MergeEntitiesWhenSafe = true; + std::ignore = BRepGraph_Deduplicate::Perform(myGraph, anOpts); + + std::ignore = BRepGraph_Compact::Perform(myGraph); + + const BRepGraph_Validate::Result aValResult = BRepGraph_Validate::Perform(myGraph); + EXPECT_TRUE(aValResult.IsValid()); +} + +// ---------- Bug-fix regression: Split with removed vertex ---------- + +TEST_F(BRepGraph_ViewsTest, EditorView_Split_RejectsRemovedVertex) +{ + BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + const BRepGraph_VertexRefId aStartVRef = BRepGraph_Tool::Edge::StartVertexId(myGraph, anEdgeId); + const BRepGraph_Tool::VertexUsage aStartVUsage = + BRepGraph_Tool::Vertex::Usage(myGraph, aStartVRef); + ASSERT_TRUE(aStartVUsage.IsValid()); + const BRepGraph_VertexId aStartV = aStartVUsage.DefId; + ASSERT_FALSE(myGraph.Topo().Gen().IsRemoved(aStartV)); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aStartV)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(aStartV)); + + BRepGraph_EdgeId aSubA, aSubB; + myGraph.Editor().Edges().Split(anEdgeId, aStartV, 0.5, aSubA, aSubB); + + EXPECT_FALSE(aSubA.IsValid()); + EXPECT_FALSE(aSubB.IsValid()); +} + +// ---------- Bug-fix regression: CleanupRemovedReferences retires orphaned CoEdges ---------- + +TEST_F(BRepGraph_ViewsTest, CleanupRemovedRefs_OrphanedCoEdgesMarkedRemoved) +{ + const BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + ASSERT_TRUE(anEdgeId.IsValid(myGraph.Topo().Edges().Nb())); + const uint32_t aNbCoEdgesBefore = myGraph.Topo().CoEdges().NbActive(); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(anEdgeId)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(anEdgeId)); + + myGraph.Editor().Gen().CleanupRemovedReferences(); + + const uint32_t aNbCoEdgesAfter = myGraph.Topo().CoEdges().NbActive(); + EXPECT_LT(aNbCoEdgesAfter, aNbCoEdgesBefore); + EXPECT_TRUE(myGraph.ValidateRelations()); +} + +// ---------- Bug-fix regression: TopoView accessors return invalid for removed entities ---------- + +TEST_F(BRepGraph_ViewsTest, TopoView_RepAccessors_ReturnNullForRemovedEntity) +{ + // Edge::Curve3D on removed edge + BRepGraph_EdgeId anEdgeId = BRepGraph_EdgeId::Start(); + ASSERT_FALSE(myGraph.Topo().Edges().Curve3D(anEdgeId).IsNull()); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(anEdgeId)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(anEdgeId)); + + EXPECT_TRUE(myGraph.Topo().Edges().Curve3D(anEdgeId).IsNull()); + + // Face::Surface on removed face + const BRepGraph_FaceId aFaceId = BRepGraph_FaceId::Start(); + ASSERT_FALSE(myGraph.Topo().Faces().Surface(aFaceId).IsNull()); + + myGraph.Editor().Gen().RemoveNode(BRepGraph_NodeId(aFaceId)); + ASSERT_TRUE(myGraph.Topo().Gen().IsRemoved(aFaceId)); + + EXPECT_TRUE(myGraph.Topo().Faces().Surface(aFaceId).IsNull()); + EXPECT_TRUE(myGraph.Topo().Faces().ActiveTriangulation(aFaceId).IsNull()); +} + +// ---------- ShapesView supplement routing for INTERNAL/EXTERNAL ---------- + +TEST_F(BRepGraph_ViewsTest, ShapesView_ShellAddFace_Internal_RoutedToSupplement) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + + BRep_Builder aBB; + TopoDS_Shell aShell; + aBB.MakeShell(aShell); + + TopoDS_Vertex aV0, aV1, aV2, aV3; + aBB.MakeVertex(aV0, gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + aBB.MakeVertex(aV1, gp_Pnt(10.0, 0.0, 0.0), 1.0e-7); + aBB.MakeVertex(aV2, gp_Pnt(10.0, 10.0, 0.0), 1.0e-7); + aBB.MakeVertex(aV3, gp_Pnt(0.0, 10.0, 0.0), 1.0e-7); + + auto makeEdge = [&](const TopoDS_Vertex& theV0, const TopoDS_Vertex& theV1) { + TopoDS_Edge anEdge; + aBB.MakeEdge(anEdge); + aBB.Add(anEdge, theV0.Oriented(TopAbs_FORWARD)); + aBB.Add(anEdge, theV1.Oriented(TopAbs_REVERSED)); + return anEdge; + }; + + TopoDS_Wire aWire; + aBB.MakeWire(aWire); + aBB.Add(aWire, makeEdge(aV0, aV1)); + aBB.Add(aWire, makeEdge(aV1, aV2)); + aBB.Add(aWire, makeEdge(aV2, aV3)); + aBB.Add(aWire, makeEdge(aV3, aV0)); + aWire.Closed(true); + + TopoDS_Face aFace; + aBB.MakeFace(aFace); + aBB.Add(aFace, aWire); + aBB.Add(aShell, aFace); + + const BRepGraph::ShapesView::Result aShellResult = aGraph.Shapes().Add(aShell, anOptions); + ASSERT_TRUE(aShellResult.IsOk()); + const BRepGraph_ShellId aShellId(aShellResult.TopologyRoot); + + TopoDS_Face aInternalFace; + aBB.MakeFace(aInternalFace); + aBB.Add(aInternalFace, aWire); + aInternalFace.Orientation(TopAbs_INTERNAL); + + const BRepGraph::ShapesView::Result aAddResult = + aGraph.Shapes().Add(aInternalFace, BRepGraph_NodeId(aShellId)); + ASSERT_TRUE(aAddResult.IsOk()); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + EXPECT_GT(aLayer->AttachedTo(BRepGraph_NodeId(aShellId)).Size(), 0); +} + +TEST_F(BRepGraph_ViewsTest, ShapesView_CompoundAddChild_MixedOrientations_CoreRefAndSupplement) +{ + BRepGraph aGraph; + BRepGraph::ShapesView::Options anOptions; + anOptions.CreateAutoProduct = false; + + BRep_Builder aBB; + TopoDS_Compound aCompound; + aBB.MakeCompound(aCompound); + const BRepGraph::ShapesView::Result aCompResult = aGraph.Shapes().Add(aCompound, anOptions); + ASSERT_TRUE(aCompResult.IsOk()); + const BRepGraph_CompoundId aCompoundId(aCompResult.TopologyRoot); + + TopoDS_Vertex aV0, aV1, aV2, aV3; + aBB.MakeVertex(aV0, gp_Pnt(0.0, 0.0, 0.0), 1.0e-7); + aBB.MakeVertex(aV1, gp_Pnt(10.0, 0.0, 0.0), 1.0e-7); + aBB.MakeVertex(aV2, gp_Pnt(10.0, 10.0, 0.0), 1.0e-7); + aBB.MakeVertex(aV3, gp_Pnt(0.0, 10.0, 0.0), 1.0e-7); + + auto makeEdge = [&](const TopoDS_Vertex& theV0, const TopoDS_Vertex& theV1) { + TopoDS_Edge anEdge; + aBB.MakeEdge(anEdge); + aBB.Add(anEdge, theV0.Oriented(TopAbs_FORWARD)); + aBB.Add(anEdge, theV1.Oriented(TopAbs_REVERSED)); + return anEdge; + }; + + TopoDS_Wire aWire; + aBB.MakeWire(aWire); + aBB.Add(aWire, makeEdge(aV0, aV1)); + aBB.Add(aWire, makeEdge(aV1, aV2)); + aBB.Add(aWire, makeEdge(aV2, aV3)); + aBB.Add(aWire, makeEdge(aV3, aV0)); + aWire.Closed(true); + + TopoDS_Face aForwardFace; + aBB.MakeFace(aForwardFace); + aBB.Add(aForwardFace, aWire); + aForwardFace.Orientation(TopAbs_FORWARD); + const BRepGraph::ShapesView::Result aFwdResult = + aGraph.Shapes().Add(aForwardFace, BRepGraph_NodeId(aCompoundId)); + ASSERT_TRUE(aFwdResult.IsOk()); + EXPECT_TRUE(aFwdResult.InsertedRef.IsValid()); + + TopoDS_Face aInternalFace; + aBB.MakeFace(aInternalFace); + aBB.Add(aInternalFace, aWire); + aInternalFace.Orientation(TopAbs_INTERNAL); + const BRepGraph::ShapesView::Result aIntResult = + aGraph.Shapes().Add(aInternalFace, BRepGraph_NodeId(aCompoundId)); + ASSERT_TRUE(aIntResult.IsOk()); + EXPECT_FALSE(aIntResult.InsertedRef.IsValid()); + + const occ::handle aLayer = + aGraph.LayerRegistry().FindLayer(); + ASSERT_FALSE(aLayer.IsNull()); + EXPECT_EQ(aLayer->AttachedTo(BRepGraph_NodeId(aCompoundId)).Size(), 1); } diff --git a/src/ModelingData/TKBRep/GTests/BRepGraph_WireExplorer_Test.cxx b/src/ModelingData/TKBRep/GTests/BRepGraph_WireExplorer_Test.cxx deleted file mode 100644 index 8fb55bb5c4..0000000000 --- a/src/ModelingData/TKBRep/GTests/BRepGraph_WireExplorer_Test.cxx +++ /dev/null @@ -1,140 +0,0 @@ -// 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 -#include -#include -#include -#include - -#include - -#include - -class BRepGraph_WireExplorerTest : public testing::Test -{ -protected: - void SetUp() override - { - BRepPrimAPI_MakeBox aBoxMaker(10.0, 20.0, 30.0); - myGraph.Clear(); - [[maybe_unused]] const BRepGraph_Builder::Result aBuildRes1 = - BRepGraph_Builder::Add(myGraph, aBoxMaker.Shape()); - } - - BRepGraph myGraph; -}; - -TEST_F(BRepGraph_WireExplorerTest, BoxWire_CountMatchesFourEdges) -{ - const BRepGraph_WireId aWireId(0); - BRepGraph_WireExplorer anExp(myGraph, aWireId); - - EXPECT_EQ(anExp.NbEdges(), 4); -} - -TEST_F(BRepGraph_WireExplorerTest, BoxWire_EdgesReturnedInConnectionOrder) -{ - const BRepGraph_WireId aWireId(0); - BRepGraph_WireExplorer anExp(myGraph, aWireId); - - // Check that the end vertex of edge[i] matches the start vertex of edge[i+1]. - ASSERT_GE(anExp.NbEdges(), 2); - - BRepGraph_CoEdgeId aPrevId = anExp.CurrentCoEdgeId(); - anExp.Next(); - int anIdx = 0; - for (; anExp.More(); anExp.Next(), ++anIdx) - { - const BRepGraph_CoEdgeId aCurrId = anExp.CurrentCoEdgeId(); - - const BRepGraphInc::CoEdgeDef& aPrevCoEdge = myGraph.Topo().CoEdges().Definition(aPrevId); - const BRepGraphInc::CoEdgeDef& aCurrCoEdge = myGraph.Topo().CoEdges().Definition(aCurrId); - - const BRepGraphInc::EdgeDef& aPrevEdge = - myGraph.Topo().Edges().Definition(aPrevCoEdge.EdgeDefId); - const BRepGraphInc::EdgeDef& aCurrEdge = - myGraph.Topo().Edges().Definition(aCurrCoEdge.EdgeDefId); - - // Resolve oriented end vertex of previous and start vertex of current. - const BRepGraph_VertexRefId aPrevEndRef = (aPrevCoEdge.Orientation == TopAbs_FORWARD) - ? aPrevEdge.EndVertexRefId - : aPrevEdge.StartVertexRefId; - const BRepGraph_VertexRefId aCurrStartRef = (aCurrCoEdge.Orientation == TopAbs_FORWARD) - ? aCurrEdge.StartVertexRefId - : aCurrEdge.EndVertexRefId; - - ASSERT_TRUE(aPrevEndRef.IsValid()) << "Invalid end vertex ref at index " << anIdx; - ASSERT_TRUE(aCurrStartRef.IsValid()) << "Invalid start vertex ref at index " << (anIdx + 1); - - const BRepGraph_VertexId aPrevEndVtx = myGraph.Refs().Vertices().Entry(aPrevEndRef).VertexDefId; - const BRepGraph_VertexId aCurrStartVtx = - myGraph.Refs().Vertices().Entry(aCurrStartRef).VertexDefId; - - EXPECT_EQ(aPrevEndVtx, aCurrStartVtx) << "Disconnected at ordered index " << anIdx; - - aPrevId = aCurrId; - } -} - -TEST_F(BRepGraph_WireExplorerTest, AllBoxWires_AreConnected) -{ - // Verify all 6 wires of the box are well-ordered. - for (BRepGraph_WireIterator aWireIt(myGraph); aWireIt.More(); aWireIt.Next()) - { - BRepGraph_WireExplorer anExp(myGraph, aWireIt.CurrentId()); - EXPECT_EQ(anExp.NbEdges(), 4) << "Wire " << aWireIt.CurrentId().Index; - } -} - -TEST_F(BRepGraph_WireExplorerTest, RangeFor_WorksCorrectly) -{ - const BRepGraph_WireId aWireId(0); - BRepGraph_WireExplorer anExp(myGraph, aWireId); - - int aCount = 0; - for (const BRepGraph_CoEdgeId aCoEdgeId : anExp) - { - EXPECT_TRUE(aCoEdgeId.IsValid(myGraph.Topo().CoEdges().Nb())); - ++aCount; - } - EXPECT_EQ(aCount, 4); -} - -TEST_F(BRepGraph_WireExplorerTest, CurrentCoEdgeId_ReturnsValidId) -{ - const BRepGraph_WireId aWireId(0); - BRepGraph_WireExplorer anExp(myGraph, aWireId); - - ASSERT_TRUE(anExp.More()); - const BRepGraph_CoEdgeId aCoEdgeId = anExp.CurrentCoEdgeId(); - EXPECT_TRUE(aCoEdgeId.IsValid(myGraph.Topo().CoEdges().Nb())); -} - -TEST_F(BRepGraph_WireExplorerTest, Reset_RestartsIteration) -{ - const BRepGraph_WireId aWireId(0); - BRepGraph_WireExplorer anExp(myGraph, aWireId); - - // Consume all edges. - while (anExp.More()) - { - anExp.Next(); - } - EXPECT_FALSE(anExp.More()); - - // Reset and verify re-iteration works. - anExp.Reset(); - EXPECT_TRUE(anExp.More()); - EXPECT_EQ(anExp.CurrentCoEdgeId(), BRepGraph_WireExplorer(myGraph, aWireId).CurrentCoEdgeId()); -} diff --git a/src/ModelingData/TKBRep/GTests/FILES.cmake b/src/ModelingData/TKBRep/GTests/FILES.cmake index 76436fdade..3471deb349 100644 --- a/src/ModelingData/TKBRep/GTests/FILES.cmake +++ b/src/ModelingData/TKBRep/GTests/FILES.cmake @@ -4,20 +4,22 @@ set(OCCT_TKBRep_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_TKBRep_GTests_FILES BRep_Tool_Test.cxx BRepAdaptor_CompCurve_Test.cxx - BRepGraph_CacheKindRegistry_Test.cxx + BRepGraph_CacheRegistry_Test.cxx BRepGraph_Assembly_Test.cxx + BRepGraph_BatchOps_Test.cxx BRepGraph_DefsIterator_Test.cxx BRepGraphInc_Test.cxx - BRepGraph_Builder_Test.cxx + BRepGraph_ShapesViewImport_Test.cxx BRepGraph_NodeId_Test.cxx BRepGraph_RefId_Test.cxx + BRepGraph_RepId_Test.cxx + BRepGraph_ItemId_Test.cxx BRepGraph_TypedIdDispatch_Test.cxx BRepGraph_RefsIterator_Test.cxx - BRepGraph_Benchmark_Test.cxx BRepGraph_Build_Test.cxx BRepGraph_DeferredInvalidation_Test.cxx BRepGraph_Fuzz_Test.cxx - BRepGraph_MeshCache_Test.cxx + BRepGraph_CacheMesh_Test.cxx BRepGraph_MutGuard_Test.cxx BRepGraph_PermissionUpdate_Test.cxx BRepGraph_ReplaceVertex_Test.cxx @@ -28,12 +30,13 @@ set(OCCT_TKBRep_GTests_FILES BRepGraph_ChildExplorer_Test.cxx BRepGraph_Iterator_Test.cxx BRepGraph_LayerIterator_Test.cxx + BRepGraph_Lock_Test.cxx BRepGraph_ParentExplorer_Test.cxx BRepGraph_ReverseIterator_Test.cxx - BRepGraph_WireExplorer_Test.cxx BRepGraph_EventBus_Test.cxx BRepGraph_Geometry_Test.cxx - BRepGraph_History_Test.cxx + BRepGraph_LayerHistory_Test.cxx + BRepGraph_LayerTopoSupplement_Test.cxx BRepGraph_Polygon_Test.cxx BRepGraph_Reconstruct_Test.cxx BRepGraph_Sharing_Test.cxx @@ -46,7 +49,9 @@ set(OCCT_TKBRep_GTests_FILES BRepGraph_Transform_Test.cxx BRepGraph_Validate_Test.cxx BRepGraph_ScenarioMatrix_Test.cxx + BRepGraph_Reverse_Test.cxx BRepGraph_SeamRedesign_Test.cxx + BRepGraph_SparseModel_Test.cxx BRepGraph_Deduplicate_Test.cxx BRepTools_ReShape_Test.cxx TopExp_Test.cxx